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.
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
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.
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.
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)
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?
Related
I am writing a socket server using java.nio. As I need my server to use 0 threads I am using java.nio.channels.Selector. My code look as follows.
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) { // Accept client connections
this.acceptClient(key);
} else if (key.isReadable()) { // Read from client
this.read(key);
} else if (key.isWritable()) {
this.write(key);
}
}
private void acceptClient(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
SocketAddress clientAddress= channel.getRemoteAddress();
//clients is a Hashmap
clients.put(clientAddress, new Client());
clientConnected(clientAddress.toString());
System.out.println("Connected to: " + clientAddress);
channel.register(this.selector, SelectionKey.OP_READ);
}
As you can see I am creating a new Client object per accepted client. What I need to do is, relevant Client object to handle their own reading and writing.
My approach is to uniquely identify clients with their address and forward it to relevant Client object.
I think using client address to uniquely identify clients is not a good approach. What is the best way to handle this issue?
I think using client address to uniquely identify clients is not a good approach.
Nothing wrong with it. The semantics of TCP/IP guarantee that every accepted socket has a different remote SocketAddress.
But you don't need it, or the Map either. Just save the Client as the attachment of the SelectionKey. That way also the Client will disappear along with the SelectionKey automatically when you close the Channel.
By contrast, changing to an IdentityHashMap<SelectionKey, Client> as suggested elsewhere gives you an opportunity to leak the SelectionKey and therefore its Channel and the Client too.
When you register the Channel with the Selector:
channel.register(this.selector, SelectionKey.OP_READ);
It will return a SelectionKey which you can use later when you select from the Selector.
Use that key to populate an IdentityHashMap<SelectionKey,Client> so you can direct the IO to the correct Client instance. As EJP points out in his answer, this will leak SelectionKeys if not properly cleaned up. If you preferred not to clean those up, you could use a WeakHashMap as well but then you're relying in implicit behavior instead of the explicit behavior of the Selector.
EJP's suggestion to use an attachment is probably the best bet. Although I can imagine a more complicated scenario where you might want to maintain additional attachments and maybe refactoring the attachment to be an encapsulation is of high cost.
I have a java.nio.channels.ServerSocketChannel which I initialised as follows:
while(true)
{
ServerSocketChannel channel = ServerSocketChannel.open();
InetSocketAddress serverSocket = new InetSocketAddress(host,port);
channel.bind(serverSocket);
SocketChannel ch = channel.accept();
// Later on when I have read off data from a client, I want to shut this
// connection down and restart listening.
channel.socket().close(); //Just trying to close the associated socket
// too because earlier approaches failed
channel.close();
}
When I send the first message from client it is successfully delivered to server and the client program exits. Then trouble begins. When I initialise the client again and try to
establish at the same port and address of the server as I did the first time, I get a
java.net.BindException: Address already in use: connect
exception even though I closed the associated channel/socket.
I have been renewing the ServerSocketChannel and InetSocketAddressobjects because as my client instance has to shut down after a write, I have to disengage that channel and since I cannot reuse a channel after it has been closed, I have to make a new object everytime. My theory is since the channel reference is reassigned each time, the orphaned object becomes GC meat, but since the close() method apparently is not working properly, the channel is still alive and until GC collects it my port will be hogged.
Nevertheless I tried keeping the initialisation of ServerSocketChannel and InetSocketAddress objects before the while loop, but this did not help, and the same exception occurred after the first write, as before.
ServerSocketChannel channel = ServerSocketChannel.open();
InetSocketAddress serverSocket = new InetSocketAddress(host,port);
channel.bind(serverSocket);
while (true)
{
SocketChannel ch = channel.accept();
//read from a client
}
For clarity , here is how I connect from the client:
SocketChannel ch=SocketChannel.open();
ch.bind(new InetSocketAddress("localhost", 8077));
InetSocketAddress address=new InetSocketAddress("localhost",8079);
//the address and port of the server
System.out.print(ch.connect(address));
ByteBuffer buf=ByteBuffer.allocate(48);
buf.clear();
buf.put("Hellooooooooooooooooooooooooo".getBytes());
buf.flip();
while(buf.hasRemaining()) {
ch.write(buf);
}
ch.close();
It looks like you're confusing client and server. Normally, server starts only once and binds to s port. Usually, there's no need to close there anything as the port gets freed when the program exits. Obviously, you must close the Sockets obtained by ServerSocket.accept(), but that's another story.
I guess you've got confused by your variable names (just like it happened to me as I started with this). Try to call all things according to their type, here was Hungarian really helpful for me.
The code I wrote for testing this is long, stupid, and boring. But it seems to work.
It may also be helpful to do:
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
Search for information about this option to learn more.
do ch.close() as well to GC the client socket.
I'm trying to create a multi-client/server application in Java. I'm having quite some issues because my threads seem to get entangled... Here's what I'm trying to do.
I have a Server-class that accepts clients by using this piece of code:
while(true){
Socket socket = serverSocket.accept();
}
My server should remember the clients that are connected so I create a new Thread called ClientThread with that socket and place that Thread in a List on the Server
That Thread listens to Command-objects that are sent from the client. If it receives a Command, it needs to send it to the Server instance for further processing (without creating a new instance of Server on that ClientThread). I've tried to do this by adding the Server-instance to this Thread when it's created. (Is this the right way?)
My Server should also be able to send Objects back to the clients (1 or more) at any time. I'm trying to do this by using socket.getOutputStream() that is kept in the ClientThread
How should I organize my Threads so that every client is constantly listening to accept objects from the server AND that they can send objects to the Server at any time.
I know this isn't really a specific question, but if you know some info or tutorials that might be helpful for this use case, I'd be really thankful.
btw: I know how to create sockets and send (serializable) objects and so on.. I'm just stuck on how to organize everything
You appear to have tangled Thread and objects. I would
a) make sure you are not extending Thread anywhere or calling your objects XxxxThread. Using an ExecutorService to manage your threads is a good idea.
b) have a simple model for responding to client commands, e.g. each client thread reads a task and then performs a task.
c) Have a wrapper for each connections, e.g. with a sendMessage method.
Since you already know about sockets and threads, I send you the idea pseudo code (case need a specific part of code just let me know)
One thing you did not mention is how to keep track of clients, by its IP o by any other method like an ID? Can any given device open more than one connection with different client ID? Or you'll only accept one connection per device? In any case, if a client is already in the list, what do you suposse to do? Will you communicate the created thread the new socket? Will you destroy that thread and create a new one? Or maybe you'll ignore this new request?
This is my idea (taken from a working application):
Server prepares the server socket and wait in the accept state.
Once a client connects, the server start a thread to attend the client passing the socket it just created with the accept command. When the thread which attends the client starts, the very first message it receives from the client should be a password o special signature to let the client gets in (this is optional).
Server code:
Prepares the server socket which listen in a well known port
Clear client list;
While (!Terminated)
{
// if you want to impose a limit for connections, check it here:
if (Is the list of connected client full?)
{
Sleep(reasonable time in seconds or miliseconds);
continue;
}
ClientSocket = ServerSocket.Accept();
if the client's IP is already in the list
{
depends on what you want to do.
}
else
{
Add client's IP to the list
Start (create) new client Tread(ClientSocket);
}
}
// when server finish
If (client list is not empty?)
{
Kill all threads
or
Wait until all threads are done
or
Wait an amount of time and then kill those remaining.
}
thread client code:
// This is optional, just to make sure a valid client is connected
Read packet from ClientSocket
if (!Is_the_passport_packet)
{
close socket;
return;
}
// if passport is not required, start here
Try
{
While (!Terminated)
{
if (read packet from client);
{
switch (packet.Command)
{
// In your question you said you want the Server thread to process the request
// I guess you have your requirements to do so,
// anyway, you must use a mutex o some other synchronization method.
case TASK_1:
[sync] process TASK_1(packet, ClientSocket);
break;
case TASK_2:
[sync] process TASK_2(packet, ClientSocket);
break;
etc ….
case WORK_DONE:
Close Socket;
return;
default:
Log(received an unknown command: packet.command);
break;
}
}
else if (Client has quit (closed/broken socket))
{
// as you may know, a socket is consider shutdown when you received a 0 length data
// and a broken connection when received -1 in either case all you have to do is
Close Socket;
return;
}
}
}
catch (Exception e)
{
Log(received an exception: e.message);
}
finally
{
Remove this client from the client's list
}
what will happen to the serversocket in my app when I suddenly change the wifi network? I guess it will shut down since my device will get a new IP, at least in TCP, is the UDP MulticastSocket prone to this as well? And how to end the previous Server socket thread and start a new one when the network changes? One solution is using time outs, another is using a flag that will indicate whether the infinite loop should end or not but since listening to a socket is a blocking function it will produce an exception/error anyways.
Any thoughts will be appreciated! :)
EDIT: sample of my server thread.
ServerSocket ss = new ServerSocket(4445);
while(true){
Socket socket = ss.accept();
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Object obj = in.readObject();
Log.i("TAG", "Received: " + obj.toString());
in.close();
socket.close();
}
TCPIP connection will break. So client would have to connect again.
UDP will be ok provided your IP does not change after reconnection. Of course if you transmit UDP its not going to make a difference for that machine.
You should get an exception in case of TCPIP which you can handle.
UDP sockets that are not bound to the address will remain open, as they are stateless. TCP listening sockets not bound to the address will remain open as well.
Conntected TCP sockets may be severed (RST) or just linger until a timeout hits.
It is a little known fact that IP mandates it that a device by default will accept packets directed to any address it has configured on any interface, no matter on which interface the packet arrives. If this were not so, routing would be broken. One can use packet filters to filter out packets with non-matching addresses depending on the interface.
I have a Server Socket and 3-4 android devices as clients. I'm using TCP/IP for communications. Which is the best method. Should I use multiple ports for each client? Or should I use same port. If using same function then how should I identify the communication addressed to different devices?
No, you do not need several ports.
ServerSocket server = new ServerSocket(port);
while (true)
{
Socket socket = server.accept();
// do something with this socket - aka 1 client
new SomeClientClass(socket);
InputStream in = socket.getInputStream();
in.read(byte[]);
OutputStream out = socket.getOutputStream;
// out will only write response to its own client.
// when this new SomeClientClassis created, method returns to this point
// in while loop and waits for the next client
}
You can use one port. The client can send you its id. If it can't you can look at the clients IP address to workout which one it is.
There are thousands of TCP client/server code examples on the web, but I would start with the sample code which comes with the JDK,