12

I'm about to program a server but am wondering if what I have in mind is possible. My program will be outputting to multiple clients on multiple ports - each port can be accessed by multiple clients.

Normally I would use a threaded socket server, but in this case I need it working for multiple ports. The usage I have in mind is in a vague pseudocode below:

  • Start server
  • Listen for incoming connections on several ports
  • Identify the port being connected to
    • If port 1, start a thread listening to client and outputting message type x
    • If port 2, start a thread listening to client and outputting message type y

Hopefully that makes some sense and you can see what I'm trying to do. Simply put: listen to selected ports, create a threaded socket connection based on which port is being connected to.

Is this doable at all, or am I going to end up multi-threading threaded socket servers?

Edit: Changed wording to better reflect the question.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Myn
  • 691
  • 4
  • 9
  • 19
  • I know you have already selected your accepted answer, but please take a look at my answer and perhaps reconsider. –  Mar 22 '11 at 01:41

5 Answers5

15

It's not possible to for a single instance of ServerSocket to listen to multiple ports. You can of course have multiple ServerSockets. However, as you already know, ServerSocket.accept blocks.

What you can use instead is a ServerSocketChannel. They're used in a similar way, but do not block.

If there are no pending connections when ServerSocketChannel.accept is called then it simply returns null.

You can use with a Selector which takes a set of channels and blocks until at least one has a pending connection.

I don't remember the specifics on how to use them, but this seems to be a decent code example.

edit: Here is my own example (pseudo-ish)

Selector selector = Selector.open();

int[] ports = {4000,4001,6000};

for (int port : ports) {
   ServerSocketChannel server = ServerSocketChannel.open();
   server.configureBlocking(false);

   server.socket().bind(new InetSocketAddress(port));
// we are only interested when accept evens occur on this socket
   server.register(selector, SelectionKey.OP_ACCEPT); 
}

while (selector.isOpen()) {
   selector.select();
   Set readyKeys = selector.selectedKeys();
   Iterator iterator = readyKeys.iterator();
   while (iterator.hasNext()) {
      SelectionKey key = (SelectionKey) iterator.next();
      if (key.isAcceptable()) {
         SocketChannel client = server.accept();
         Socket socket = client.socket();
// create new thread to deal with connection (closing both socket and client when done)
      }
   }
}

// tidy up selector and channels
user207421
  • 305,947
  • 44
  • 307
  • 483
Dunes
  • 37,291
  • 7
  • 81
  • 97
  • Interesting.. As you say, it blocks, which is the primary reason I was going to multi-thread threaded servers. Give me a few minutes to have a look at this... – Myn Feb 22 '11 at 14:43
  • 1
    Hm, definitely not what I had in mind; I was aiming for being able to listen to a specific set of ports (not neccessarily with 'ServerSocket'), but this does seem like a step in the right direction. Thank you to all, if I could give out more than one best answer I would, especially to Vance Maverick, but thank you Dunes. – Myn Feb 22 '11 at 15:56
  • Not sure what your problem is. Will write my own pseudo example, see if that clears things up. – Dunes Feb 22 '11 at 16:52
  • I think I see...if my understanding is correct, the program will begin listening to a port, but keep running through the program, listening on all specified ports. When a connection is detected on one of these ports, the thread is created to handle it. Is that a fair assumption? Thank you again Dunes. – Myn Feb 23 '11 at 11:12
  • You're killing the purpose of a **non-blocking asynchronous** server by saying "create a new thread to deal with connection". It will take more time and resources to create the new thread and pass the work to that thread than to just perform the **non-blocking** work on the same thread. –  Mar 21 '11 at 20:34
  • 1
    Your code needs a tweak to work. You don't have access to the `server` variable in the while loop. The `accept` can be done from the key object: `SocketChannel client = ((ServerSocketChannel)key.channel()).accept();` The imports required for this to compile are: `java.net.*; java.io.*; java.nio.*; java.nio.channels.*; java.util.*;` – Stephen Ostermiller Nov 27 '17 at 12:36
  • @StephenOstermiller thanks, Dunes code return null in `server.accept();`. but your code works perfectly. – hassan moradnezhad Jul 20 '19 at 14:06
12

Hello, so let me get this straight. What you want to do is to create a server that can listen on multiple ports and when you get a new connection, you want to be able to tell which port that connection used, is this correct? Well if that's the case, you can do this very easily with use of the java.nio package.

We're going to use a Selector for readiness selection and a ServerSocketChannel to listen for incoming connectings.

First we need to declare our Selector.

Selector selector = Selector.open();

Now lets create a list of ports to listen on and start listening on them.

int ports[] = new int[] { 1234, 4321 };

// loop through each port in our list and bind it to a ServerSocketChannel
for (int port : ports) {
    ServerSocketChannel serverChannel = ServerSocketChannel.open();
    serverChannel.configureBlocking(false);
    serverChannel.socket().bind(new InetSocketAddress(port));
    serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}

Now for the SelectionKey handling process.

while (true) {
    selector.select();

    Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
    while (selectedKeys.hasNext()) {
        SelectionKey selectedKey = selectedKeys.next();

        if (selectedKey.isAcceptable()) {
            SocketChannel socketChannel = ((ServerSocketChannel) selectedKey.channel()).accept();
            socketChannel.configureBlocking(false);
            switch (socketChannel.socket().getPort()) {
                case 1234:
                    // handle connection for the first port (1234)
                    break;
                case 4321:
                    // handle connection for the secon port (4321)
                    break;
            }
        } else if (selectedKey.isReadable()) {
            // yada yada yada
        }
    }
}

Perhaps a switch statement is not necessary for such a simple task, but it's for ease of reading and understanding.

Remember, this server is set up in a non-blocking asynchronous way so that all the I/O calls you perform will not block the current thread. So DO NOT initiate any new threads in the SelectionKey handling process.

Also, I know that this doesn't completely answer your question (it might, it might not) but it will in fact give you an understanding on how to use the java.nio package to create a non-blocking asynchronous server that can listen on multiple ports.

  • I'm following this code and getting an IllegalBlockingmodeException on the line that calls the register function on the serverchannel. – Richard Chase May 13 '15 at 16:06
  • `socketChannel.socket().getPort()` return new port number when a data send from client to server! u must write `socketChannel.socket().getLocalPort()` instead – hassan moradnezhad Jul 20 '19 at 22:55
1

You can't listen to all ports, but you can listen to a set of them. Create one ServerSocket ( http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#ServerSocket%28int%29 ) for each port you want to listen to, and accept connections on each.

Vance Maverick
  • 812
  • 6
  • 4
  • Yeah, this is what I meant by multi-threading threaded servers - I think what I probably meant to say was that I wanted a more efficient way of listening to a set of ports. – Myn Feb 22 '11 at 15:12
1

This should be possible with NIO, however I don't see a good reason to avoid having one thread per listener unless you have more than 1K port.

Do you really need multiple listening ports? In most cases it should be possible for one port support all kinds of clients and have the client tell the server (or the server determine what type of connection is needed)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • This was my sentiment, distinguishing request types are not what ports are supposed to be used for. Request paths are the proper way. What is the nature of the clients here? – mcyalcin Feb 22 '11 at 14:36
  • It's primarily to allow additional functionality to be built in later on. E.g. if message type z was introduced for port 3000, it would not require someone to go messing with the server code. My plans are to have a config file where the server will read which message types to output for which ports. As for whether it requires multiple listening ports, in this case there is a specific requirement for it from a senior engineer. – Myn Feb 22 '11 at 14:41
  • It appears you want to have hard coded subscription to groups of messages by port. This is usually done with a protocol version or protocol type sent by the client. (But I have an example of doing this with two ports on my server so I can see the data with a plain telnet to the port, so I am not against it) – Peter Lawrey Feb 22 '11 at 17:22
  • @PeterLawrey Is it possible run multiple SocketChannel's on single port i.e same SocketAddress? – rns Aug 14 '14 at 07:11
  • 1
    A SocketChannel has two IP:port combinations, one at each each. This has to be unique per connection. For a IP:port you can connect many times from different IP:ports. – Peter Lawrey Aug 15 '14 at 10:08
-1

I don't think you can listen to all ports, no. That would be quite expensive for the OS to implement, so that's simply not how port listening works.

What if multiple applications were simultaneously listening to "all" ports, to which application should the network subsystem deliver incoming packets?

unwind
  • 391,730
  • 64
  • 469
  • 606
  • Mm that's what I feared...And presumably the most efficient way of listening to a select few ports, say if I knew that I would only have someone connecting to port 4000, 4010, 4020, or 4030, would be to multi-thread a threaded socket servers? – Myn Feb 22 '11 at 14:16
  • 1
    Multi-thread a threaded socket servers? I don't understand this. – Erick Robertson Feb 22 '11 at 14:30
  • "Multi-thread threaded socket servers", that should be, rather. By which I mean, create a threaded socket server listening on port, for example, 4000, create a threaded socket server listening on port 4010, etc. and multi-thread these to have the servers running simultaneously. – Myn Feb 22 '11 at 14:37