tags:

views:

295

answers:

8

In Java, how would you set up a socket listener that listened to a socket for a series of bytes that represented a command and on recieving called a method which parsed the incoming data and invoked the appropriate command?

Clarification:

My issue is not with handling the commands (Which might also be error codes or responses to commands from the server) but with creating the socket and listening to it.

More Clarification:

What I want to do is mimic the following line of .Net (C#) code:

_stream.BeginRead(_data,0, _data.Length, new  
    AsyncCallback(this.StreamEventHandler), _stream);

Where:

  • _stream is a network stream created from a socket
  • _data is an array of Byte of length 9
  • this.StreamHandler is a delegate (function pointer) which get executed when data is read.

I am rewriting a library from C# into Java and the component I am currently writing passes commands to a server over TCPIP but also has to be able to bubble up events/responses to the layer above it.

In C# this seems to be trivial and it's looking less and less so in Java.

A: 

I would

  1. put each command into a class of its own, where each class implements a specific interface (e.g. Command)

  2. create a Map<String,Command> which contains a lookup table from each command string to an instance of the class that implements that command

Alnitak
+1  A: 

Have a small main method which sets up the socket and listens for incoming connections. Pass each connection to a worker object (possibly in its own thread).

The worker object should have two APIs: The server and the client. The client API gets a connection and reads data from it, the server API takes a connection and writes data to it.

I like to keep these two in a single class because that makes it much more simple to keep the two in sync. Use a helper class to encode/decode the data for transmission, so you have single point to decide how to transmit integers, commands, options, etc.

If you want to go further, define a command class and write code to serialize that to a socket connection and read it from it. This way, you worker objects just need to declare which command class they handle and the server/client API gets even more simple (at the expense of the command class).

Aaron Digulla
he talked about series of bytes - suggests that the protocol already exists and isn't based on Java serialisation!
Alnitak
A: 

The TCP connection provides you with one InputStream and one OutputStream. You could just poll the InputStream continuously for the next command (and its inputs) on a dedicated thread. ByteBuffer.wrap(byte[] array) may be useful in interpreting the bytes as chars, ints, longs, etc. You could also pass objects around using serialization.

Zach Scrivena
A: 

This should help. Lesson 1: Socket Communications

David Allan Finch
A: 

Any naive approach most likely will not scale well.

Consider using a REST-approach with a suitable small web-server. Jetty is usually a good choice.

Thorbjørn Ravn Andersen
A: 

To create an listen to a socket, in a very naive way:

    mServerSocket = new ServerSocket(port);

    listening = true;

    while (listening) {

      // This call blocks until a connection is made
      Socket socket = serverSocket.accept();

      OutputStream out = socket.getOutputStream();
      InputStream in = socket.getInputStream();

      // Here you do your magic, reading and writing what you need from the streams
      // You would set listening to true if you have some command to close the server
      // remotely

      out.close();
      in.close();
      socket.close();
   }

Normally it is a good idea to delegate the processing of the input stream to some other thread, so you can answer the next request. Otherwise, you will answer all requests serially.

You also need to define some kind of protocol of what bytes you expect on the input and output streams, but from your question it looks like you already have one.

Mario Ortegón
+1  A: 

Starting from my other answer: The specific part you request is the one that goes into the section: "Magic goes here". It can be done in ohh so many ways, but one is:

final InputStream in = socket.getInputStream();
// This creates a new thread to service the request.
new Thread(new Runnable(){
  public void run(){
    byte[] retrievedData= new byte[ITEM_LENGTH];
    in.read(retrievedData, 0, ITEM_LENGTH);
    in.close();

    // Here call your delegate or something to process the data
    callSomethingWithTheData(retrievedData);
  }
}).start();
Mario Ortegón
I think this is as close to an answer as I'm going to get thanks.
Omar Kooheji
A: 

You could create an enum with one member per command

interface Comamnd {
    // whatever you expect all command to know to perform their function
    void perform(Context context);
}

enum Commands implements Command{
   ACTIONONE() {
        void perform(Context context) {
             System.out.println("Action One");
        }
   },
   ACTIONTWO() {
        void perform(Context context) {
             System.out.println("Action Two");
        }
   }
}

// initialise
DataInputStream in = new DataInputStream(socket.getInputStream());

// in a loop
byte[] retrievedData= new byte[ITEM_LENGTH];
in.readFully(retrievedData);
String command = new String(retrievedData, 0);
Commands.valueOf(command).perform(context);
Peter Lawrey