1

I am writing a server program that can accept communication from multiple (but fixed) number of clients. I want to keep the program single-threaded. To do so, I am using non-blocking socket to iterate over each client, but each client's channel uses blocking mode. Here's my server code:

class server {
    public static void main(String args[])
         throws Exception {

        ServerSocketChannel channel = ServerSocketChannel.open();
        channel.configureBlocking(false);
        channel.socket().bind(new java.net.InetSocketAddress("localhost", 8005));
        System.out.println("Server attivo porta 8005");
        Selector selector = Selector.open();
        channel.register(selector, SelectionKey.OP_ACCEPT);

        for(;;) {
          selector.select();
          Set keys = selector.selectedKeys();
          Iterator i = keys.iterator();

          while(i.hasNext()) {
            SelectionKey key = (SelectionKey) i.next();

            i.remove();

            if (key.isAcceptable()) {
              SocketChannel client = channel.accept();
              client.configureBlocking(true);

              ObjectInputStream ois = new ObjectInputStream(
                    client.socket().getInputStream());
              String s = (String)ois.readObject();
              System.out.println(s);
            }
          }
        }
    }
}

The client uses simple blocking I/O, as shown here:

class client {
    public static void main(String args[]) throws Exception {
        SocketChannel channel = SocketChannel.open();

        channel.configureBlocking(true);

        channel.connect(new java.net.InetSocketAddress("localhost", 8005));

        ObjectOutputStream oos = new ObjectOutputStream
              (channel.socket().getOutputStream());

        for (int i = 0; i < 100; i++) {
             oos.writeObject(new String("Hello " + i));
             System.out.println(i);
         }
    }
}

The problem is that although the client wants to write 100 times, the server is reading the message just once. Neither the server nor the client is giving any exception, but I am only getting the output "Hello 0" from the server. Is there any problem in what I am doing here? If so, what alternatives do I have?

Thanks.

Update: Closing the ObjectInputStream within the server's loop gives a BrokenPipeException by the client (the server behaves in the same way).

Arani
  • 400
  • 6
  • 21
  • On the server side, you're never closing your `ObjectOutputStream`. You either need to read until you get an `EOF` or close it after you get the object and continue your loop. – Baldy Jun 06 '14 at 20:15
  • @Baldy Do you mean the ObjectInputStream? On the server side, there is no output stream, only an input stream. – Arani Jun 06 '14 at 20:19
  • Yep, `ObjectInputStream`. See my answer below. – Baldy Jun 06 '14 at 21:08
  • @Baldy Please check update. It does not help in this case. Thanks for looking into it. – Arani Jun 06 '14 at 21:11

2 Answers2

1

The issue is that you're just checking for new connections with key.isAcceptable(). You also need to check for reads with key.isReadble(). You should only be doing connection setup from key.isAcceptable().

See Java ServerSocketChannel SocketChannel (Callback)

Community
  • 1
  • 1
Baldy
  • 2,002
  • 14
  • 14
  • I am never getting key.isReadable() to be true. Maybe this is because the client SocketChannel is set in blocking mode. – Arani Jun 06 '14 at 21:21
  • Also, since it is a blocking read, I understand that the server should block waiting for the data to come in even if I am checking only key.isAcceptable(). – Arani Jun 06 '14 at 21:22
  • You need to set unblocking mode for reads. Also, check out the link I posted as there are additional setup items you need to do on connect in order actually get the read selector working. Since you're trying to do everything with a single thread, blocking doesn't really make sense. – Baldy Jun 06 '14 at 21:31
  • I understand what you are saying about blocking -- but then I will not be able to use ObjectInputStream here. That is why I want to make the reads and writes blocking, while enabling multiplexed reading. Is this not possible at all? – Arani Jun 06 '14 at 22:15
  • Non-blocking should work OK unless you've got high latency on the connection or are sending really large objects. If that's the case, you're going to need something more sophisticated anyway. With blocking you're going to be effectively limited by your slowest client and will end up with lengthly delays when a client disconnects unexpectedly waiting for the socket timeout. – Baldy Jun 06 '14 at 22:21
  • As far as I understand, in non-blocking socket programming, I have to first send the size of the object, and then send the object itself. I wanted to avoid this complexity. Any alternatives? – Arani Jun 06 '14 at 22:32
  • 1
    Ideally you need some sort of protocol so you know when you have a complete data set to convert. I might suggest looking at sending your data in JSON format and using a non-alpha character as message marker. Then you just buffer the inbound data until you get the marker and then pass it off to your JSON parser for conversion to an object. You get the benefits of easier debugging since you can easily see the raw data on the wire and easier versioning (parsers can ignore unknown fields, etc.). Since `ObjectWriter` isn't space efficient, you shouldn't see a significant size penalty. – Baldy Jun 06 '14 at 22:46
0

The problem is that the server is not waiting for the client to send all it's data. In client server programs, what you need to do is to establish a clear protocol between both so that they are in sync when data is transmitted/received. This is usually done by signaling an end of transmission by either side by sending a designated symbol or closing the connection when they are done

Sasikanth Bharadwaj
  • 1,457
  • 1
  • 11
  • 11
  • But closing the ObjectOutputStream in the server after each read gives me a BrokenPipeException on the subsequent write attempts. – Arani Jun 06 '14 at 20:25
  • Please check update -- I have tried to close the output stream, but it gives me a broken pipe exception. Thanks a lot for looking into it. – Arani Jun 06 '14 at 20:28
  • Since the client is the one writing here, the client should close the connection once he has finished writing, that would ensure the server would proceed as expected – Sasikanth Bharadwaj Jun 06 '14 at 20:54
  • But adding shutdownOutput in the client does not help either. Could you please be a little more specific? – Arani Jun 06 '14 at 21:05