Is calling ServerSocket.close() sufficient enough to close a port? - java

I have some java code that looks similar to this:
private void startServer() throws IOException {
URLClassLoader classloader = null;
System.out.println("Opening server socket for listening on " + PORT_NUMBER);
try {
server = new ServerSocket(PORT_NUMBER);
server.setSoTimeout(10000);
connected = true;
System.out.println("Server is now listening on port " + PORT_NUMBER);
} catch (IOException e) {
System.err.println("Could not start server on port " + PORT_NUMBER);
e.printStackTrace();
connected = false;
}
while (connected) {
// Incoming request handler socket.
Socket socket = null;
try {
System.out.println("Waiting for client connection...");
// Block waiting for an incoming connection.
socket = server.accept();
if (socket == null) continue;
...and so on and so forth. When I call server.close() later on (I don't get any different behavior if I call socket.close() first), I don't get any errors, but netstat shows that the port is still being listened on. Should calling ServerSocket.close() be sufficient enough to free up the port on this system?
I am programming for a Java 1.4.2 microedition runtime. It is also worthy to note that I have this method being run in another thread, and I am trying to close the socket from its parent thread.
EDIT Here is the line from netstat, though I can assure you it is still being listened on, since if I start the Xlet again I get an exception with that port number.
tcp 0 0 *.2349 *.* LISTEN

There are several things to consider. One of them is described by the following quotation from JavaDoc of ServerSocket
public void setReuseAddress(boolean on)
throws SocketException
Enable/disable the
SO_REUSEADDR socket option. When a TCP connection is closed the
connection may remain in a timeout state for a period of time after
the connection is closed (typically known as the TIME_WAIT state or
2MSL wait state). For applications using a well known socket address
or port it may not be possible to bind a socket to the required
SocketAddress if there is a connection in the timeout state involving
the socket address or port.
So it is kind of OK that the OS can still show that there is something going on after you close() the server socket. But if you going to open/close a server socket on the same port frequently you might hit a problem.

Related

ServerSocket.getLocalSocketAddress() returning empty

I'm trying to create a local server with Wi-Fi P2P between an Android phone and a Raspberry Pi, with the Android as the host. I have been able to successfully establish a P2P connection using wpa_cli on the Pi, but now I am trying to use a C client socket to connect to the phone and transfer data. However, the line Log.d("Socket waiting", serverSocket.getLocalSocketAddress().toString()); spits out D/Socket waiting: ::/:::8888. It doesn't seem to have an address at all, so how am I supposed to connect to it?
As indicated by my comment, my research told me that the correct IP should be 192.168.49.1. If the IP were any different, that would be okay, because I can just send a BLE packet to the phone, telling it the IP. My issue is that the IP is entirely blank.
My code is as follows, for a thread that waits on a connection:
public static class DataTransfer extends Thread {
#Override
public void run() {
Log.d("DataTransfer", "Start");
ServerSocket serverSocket = null;
try {
/**
* Create a server socket and wait for client connections. This
* call blocks until a connection is accepted from a client
*/
// Expects a connection at 192.168.49.1:8888
serverSocket = new ServerSocket(8888);
//serverSocket.setReuseAddress(true);
//serverSocket.toString()
Log.d("Socket waiting", serverSocket.getLocalSocketAddress().toString());
Socket client = serverSocket.accept();
InputStream inputstream = client.getInputStream();
Log.d("InputStream Available", String.valueOf(inputstream.available()));
serverSocket.close();
}
catch (IOException e) {
Log.e("Receive Error", e.getMessage());
if(serverSocket != null) {
try {
serverSocket.close();
} catch (IOException ex) {
Log.e("Failed to close socket", ex.getMessage());
}
}
return;
}
}
}
And here is the output of ip a on the Pi, once it is connected via Wi-Fi P2P
11: p2p-wlan0-8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether b2:0e:07:e6:e6:55 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.23/24 brd 192.168.1.255 scope global noprefixroute p2p-wlan0-8
valid_lft forever preferred_lft forever
inet6 fe80::e79c:33f3:6e49:b6ed/64 scope link
valid_lft forever preferred_lft forever
Final edit:
My problem was seemingly unrelated. As both comments below indicate, the IP shown off is fine, it just means it accepts connections from anything. My actual issue was that I had a static IP set up on my Pi without specifying which interface the static IP was for. The client needed to be on a 192.168.49.# address, and the static IP was preventing it.
You can specify the interface the server socket is listening on by passing an address to the constructor:
serverSocket = new ServerSocket(8888, 10, InetAddress.getByName("192.168.49.1"));
Seeing :: means your server was listening for IPv6 connections on all interfaces. That is represented by the IPv6 address of all zeros which can be written as ::. But you are trying to connect to an IPv4 address, not IPv6. Most systems I've worked with are configured so that IPv4 connections can be accepted by an IPv6 server, but I guess yours isn't. The answer to this question suggests you may be able to change your system's behavior with sysctl:
sysctl net.ipv6.bindv6only=0
:: is the IPv6 default route. It indicates that you are serving requests from all interfaces.
This is the expected behavior. Is there a problem with that?

Client seems to be connected without the server accepting it

I was trying to illustrate a problem with a socket server and client, where the server was supposed to handle only one client at a time, and so multiple clients would have to wait for each other.
This is my server class. I can accept a connection from one client, read some string, upper case it and send it back. Then the next client would be served. So if I start multiple clients, the first would connect, but the rest would have to wait for the server to call accept() again. That was the assumption.
System.out.println("Starting server...");
try {
ServerSocket welcomeSocket = new ServerSocket(2910);
while(true) {
Socket socket = welcomeSocket.accept();
System.out.println("client connected");
ObjectInputStream inFromClient = new ObjectInputStream(socket.getInputStream());
String o = (String)inFromClient.readObject();
System.out.println("Received: " + o);
String answer = o.toUpperCase();
ObjectOutputStream outToClient = new ObjectOutputStream(socket.getOutputStream());
System.out.println("Sending back: " + answer);
outToClient.writeObject(answer);
}
} catch (IOException | ClassNotFoundException e) {
// socket stuff went wrong
}
Here's my client code:
try {
Socket socket = new Socket("localhost", 2910);
System.out.println("Connected to server");
ObjectOutputStream outToServer = new ObjectOutputStream(socket.getOutputStream());
Scanner in = new Scanner(System.in);
System.out.println("What to send?");
String toSend = in.nextLine();
System.out.println("Sending " + toSend);
outToServer.writeObject(toSend);
ObjectInputStream inFromServer = new ObjectInputStream(socket.getInputStream());
String o = (String)inFromServer.readObject();
System.out.println("Received: " + o);
} catch (IOException | ClassNotFoundException e) {}
I create the connection to the server, and then read from the console. The first client should connect successfully, and then print out "connected to server". The other clients should get stuck on creating the Socket, until the server calls accept(). Or so I thought.
But all my clients print out "Connected to server", and I can type into the console for all clients, and this is sent to the server. The server will then still respond to one client at a time.
But why does my client code move on from the initial connection before my server accepts the connection? This seemed to be the case in java 8, but now I'm using java 11.
Set the backlog to 1 if you want to prevent the operating system from accepting multiple connections until you call accept().
Otherwise, the OS will accept up to typically 5 connections before you call accept(). The clients believe to be connected because they are, indeed, connected at a TCP/IP level.
See the documentation:
public ServerSocket(int port,
int backlog)
throws IOException
Creates a server socket and binds it to the specified local port number, with the specified backlog. A port number of 0 means that the port number is automatically allocated, typically from an ephemeral port range. This port number can then be retrieved by calling getLocalPort.
The maximum queue length for incoming connection indications (a request to connect) is set to the backlog parameter. If a connection indication arrives when the queue is full, the connection is refused.
If the application has specified a server socket factory, that factory's createSocketImpl method is called to create the actual socket implementation. Otherwise a "plain" socket is created.
If there is a security manager, its checkListen method is called with the port argument as its argument to ensure the operation is allowed. This could result in a SecurityException. The backlog argument is the requested maximum number of pending connections on the socket. Its exact semantics are implementation specific. In particular, an implementation may impose a maximum length or may choose to ignore the parameter altogther. The value provided should be greater than 0. If it is less than or equal to 0, then an implementation specific default will be used.
Parameters:
port - the port number, or 0 to use a port number that is automatically allocated.
backlog - requested maximum length of the queue of incoming connections.

ObjectOutputStream writeObject hangs when two clients send objects to server

I am writing client/server application in which multiple clients connect to servers and continiusly send serialized objects to servers at a high rate over TCP connection.
I am using ObjectOutputStream.writeObject on client and ObjectInputStream.readObject at server.
Server application accepts clients connection on the single port using serverSocket.accept() and passes Socket to a new thread for reading objects.
When a single client connects and sends about 25K objects/s - all works fine. Once I start a second client, after the short period of time, one or both clients hang on ObjectOutputStream.writeObject for one of the servers and the corresponding server hangs on the ObjectInputStream.readObject.
No exceptions thrown on the both sides.
If rate is very low, lets say 10-20/s in total - it will not hang but at 100-1000/s it will.
Using netstat -an on the client machine I can see that the send-Q of the corresponding link is about 30K. On the server side the receive-Q is also ~30K.
When running client/server on the local Windows I observe something similar - client hangs but the server continue to process incoming objects and once it catches up, client unlocks and continue to send objects.
Locally on windows the server is slower than client, but on linux, number of the server instances running on the deferent machines is more than enough for the rate that clients produce.
Any clue what is going on?
client code snip:
Socket socket = new Socket(address, port);
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
while(true)
{
IMessage msg = createMsg();
outputStream.writeObject(msg);
outputStream.flush();
outputStream.reset();
}
server code accepting connections:
while(active)
{
Socket socket = serverSocket.accept();
SocketThread socketThread = new SocketThread(socket);
socketThread.setDaemon(true);
socketThread.start();
}
server code reading objects:
public class SocketThread extends Thread
{
Socket socket;
public SocketThread(Socket socket)
{
this.socket = socket;
}
#Override
public void run() {
try {
ObjectInputStream inStream = new ObjectInputStream(socket.getInputStream());
while(true)
{
IMessage msg = (IMessage)inStream.readObject();
if(msg == null){
continue;
}
List<IMessageHandler> handlers = handlersMap.get(msg.getClass());
for(IMessageHandler handler : handlers){
handler.onMessage(msg);
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
You have just described the operation of TCP when the sender outruns the receiver. The receiver tells the sender to stop sending, so the sender stops sending. As you are using blocking I/O, the client blocks in send() internally.
There is no problem here to solve.
The problem was that handlers on the server side were using some not thread-safe resources (like Jedis connection) so it was all stack on the server side.
Doing it thread safe solved the issue.

ServerSocket constructor in laymans terms

What would this statement do:
ServerSocket ss=new ServerSocket(4646);
Please explain in layman terms.
The statement effectively tells the JVM to listen on port specified (4646) for incoming connections. By itself it doesn't mean anything since you will have to take incoming connections to that port and use them to build normal Socket objects that will be then used for ingoing/outgoing data.
You could say that the ServerSocket is the object through which real TCP sockets between clients and the server are created. When you create it, the JVM hooks to the operating system telling it to dispatch connections that arrive on that port to your program.
What you typically do is something like:
public AcceptThread extends Thread {
public void run() {
ServerSocket ss = new ServerSocket(4646);
while (true) {
Socket newConnection = ss.accept();
ClientThread thread = new ClientThread(newConnection);
thread.start();
}
}
}
So that you will accept incoming connections and open a thread for them.
Straight from the ServerSocket Java docs:
Creates a server socket, bound to the specified port.
What's a server socket?
This class implements server sockets. A server socket waits for requests to come in over the network. It performs some operation based on that request, and then possibly returns a result to the requester.
public ServerSocket(int port) throws IOException
documentation:
Creates a server socket, bound to the
specified port. A port of 0 creates a
socket on any free port.
That would bind your ServerSocket to port 4646 on the local machine.
You could then accept sockets on this connection with
// pick up server side of the socket
Socket s = ss.accept();
Now, your client can connect to your server, establishing a socket connection, like this
// pick up client side of the socket, this is in a different program (probably)
Socket connectionToServer = new Socket("myserver",4646);

What to do when ServerSocket throws IOException and keeping server running

Basically I want to create a rock solid server.
while (keepRunning.get()) {
try {
Socket clientSocket = serverSocket.accept();
... spawn a new thread to handle the client ...
} catch (IOException e) {
e.printStackTrace();
// NOW WHAT?
}
}
In the IOException block, what to do? Is the Server socket at fault so it need to be recreated? For example wait a few seconds and then
serverSocket = ServerSocketFactory.getDefault().createServerSocket(MY_PORT);
However if the server socket is still OK, then it is a pity to close it and kill all previously accepted connections that are still communicating.
EDIT: After some answers, here my attempt to deal with the IOException. Would the implementation be guaranteeing keeping the server up and only re-create server socket when only necessary?
while (keepRunning.get()) {
try {
Socket clientSocket = serverSocket.accept();
... spawn a new thread to handle the client ...
bindExceptionCounter = 0;
} catch (IOException e) {
e.printStackTrace();
recreateServerSocket();
}
}
private void recreateServerSocket() {
while (keepRunning) {
try {
logger.info("Try to re-create Server Socket");
ServerSocket socket = ServerSocketFactory.getDefault().createServerSocket(RateTableServer.RATE_EVENT_SERVER_PORT);
// No exception thrown, then use the new socket.
serverSocket = socket;
break;
} catch (BindException e) {
logger.info("BindException indicates that the server socket is still good.", e);
bindExceptionCounter++;
if (bindExceptionCounter < 5) {
break;
}
} catch (IOException e) {
logger.warn("Problem to re-create Server Socket", e);
e.printStackTrace();
try {
Thread.sleep(30000);
} catch (InterruptedException ie) {
logger.warn(ie);
}
}
}
}
If in doubt, you could try re-creating the server socket, using the same port. If the socket has been closed, then the creation will succeed and you can continue processing new connections. The old connections are gone, but that's outside of your control since the socket was closed. If the socket was not closed, then creating a new instance will fail since the port is still in use, which you can just ignore - i.e. don't replace the current server socket reference.
In general, clients should also assume that connections will be broken and that reconnection is necessary. In other words, it's not just the server that has to be robust - clients should also anticipate connection errors and reconnect.
You can get an IOException on an accept() if the server socket is closed (by you) or you run out of resources, e.g. file handles. Either way, there is not much you can do about it. If the serverSocket is closed (you can test for this) you probably had a good reason to do this. If you run out of resources, you will either have to increase your resource limit, which requires a restart of your application, or you have a resource leak.
Make sure you differentiate between different IOExceptions you might receive. Is it an exception on creating a connection? Is it an exception once a connection has already been established?
The only code you gave is for accept()ing. Generally speaking, an IOException usually means an error on any layer on the physical network.
Probably the best fallback behavior you can implement is to wait for a certain time quantum, and then try to reconnect. Assume you most possibly will not be able to reconnect, since you have lost network connection for more than a temporary period. Make sure you handle this gracefully. As #mdma mentioned, this must be supported by your clients as well.
However if the server socket is still
OK, then it is a pity to close it and
kill all previously accepted
connections that are still
communicating.
Please note that closing the server socket will NOT close previously accepted connections. As soon as a connection has been accepted it lives a separate, joyful life at a different port.

Categories