I'm trying to implement a server that accepts connections but times out and closes that specific connection only if it hasn't received anything from that connection after N milliseconds.
From my possible misunderstanding of ServerSocket's setSoTimeout(int milliseconds) method, I thought this behaviour could be accomplished by passing N to setSoTimeout.
What I'm experiencing is once a client makes a connection, and doesn't send anything over that connection for N seconds, the server catches a SocketTimeoutException, but then completely stops execution and ends the process running the server program. Here is my server's listen method:
private static void listen() throws IOException {
while(true) {
try {
Socket clientSocket = serverSocket.accept();
Connection connexion = new Connection(clientSocket);
connexion.start();
} catch (SocketTimeoutException e) {
System.out.println("Socket timed out!");
break;
}
}
}
I successfully catch the SocketTimeoutException, ignore it (bad I know!) and assume that the client connection that caused the exception gets closed. Then I just break out of the catch block to continue accepting other client connections. What am I missing here?
You need to add continue; instead of break; in the try-catch clause. The server will keep listening.
assume that the client connection that caused the exception gets closed.
Incorrect assumption. The accept() is what has timed out. The client connection hasn't done anything yet.
Then I just break out of the catch block to continue accepting other client connections.
Err, no, you break out of the catch block to stop accepting other client connections. You're breaking out of the accept loop.
What am I missing here?
A continue instead of a break.
If you don't care about the SocketTimeoutException why are you setting a socket timeout on the ServerSocket? Just remove that.
Related
I have a ServerSocket waiting to accept connections. Upon receiving a certain event on another thread, I close the socket so it no longer waits for connections. I receive a java.net.SocketException with "Socket closed" message. The problem is, how to identify the "Socket closed" exception. I can use the exception message to do this, but I feel it's not how I should handle exceptions.
I looked up the documentation:
https://docs.oracle.com/javase/9/docs/api/java/net/SocketException.html
There are a number of subclasses to the SocketException class, but I couldn't find anything that refers to the "Socket closed" thing. Is it ok to use the exception message to identify it? Could this message ever change, maybe on another platform or something?
Here's some code:
try {
// Wait for connection,
connectionSocket = serverSocket.accept();
} catch (SocketException e) {
// How to identify the "Socket closed" exception?
// ...
} catch (IOException e) {
e.printStackTrace();
}
The better way to do this is:
Define a 'stop' flag.
Set it when you want the server to stop accepting.
Set a shortish socket timeout on the server socket, say a few seconds.
When you get a connection or incur the timeout, check the flag, and proceed accordingly.
Explanation
I'm revisiting the project I used to teach myself Java.
In this project I want to be able to stop the server from accepting new clients and then perform a few 'cleanup' operations before exiting the JVM.
In that project I used the following style for a client accept/handle loop:
//Exit loop by changing running to false and waiting up to 2 seconds
ServerSocket serverSocket = new ServerSocket(123);
serverSocket.setSoTimeout(2000);
Socket client;
while (running){ // 'running' is a private static boolean
try{
client = serverSocket.accept();
createComms(client); //Handles Connection in New Thread
} catch (IOException ex){
//Do Nothing
}
}
In this approach a SocketTimeoutException will be thrown every 2 seconds, if there are no clients connecting, and I don't like relying on exceptions for normal operation unless it's necessary.
I've been experimenting with the following style to try and minimise relying on Exceptions for normal operation:
//Exit loop by calling serverSocket.close()
ServerSocket serverSocket = new ServerSocket(123);
Socket client;
try{
while ((client = serverSocket.accept()) != null){
createComms(client); //Handles Connection in New Thread
}
} catch (IOException ex){
//Do Nothing
}
In this case my intention is that an Exception will only be thrown when I call serverSocket.close() or if something goes wrong.
Question
Is there any significant difference in the two approaches, or are they both viable solutions?
I'm totally self-taught so I have no idea if I've re-invented the wheel for no reason or if I've come up something good.
I've been lurking on SO for a while, this is the first time I've not been able to find what I need already.
Please feel free to suggest completely different approaches =3
The problem with second approach is that the server will die if an exception occurs in the while loop.
The first approach is better, though you might want to add logging exceptions using Log4j.
while (running){
try{
client = serverSocket.accept();
createComms(client);
} catch (IOException ex){
// Log errors
LOG.warn(ex,ex);
}
}
Non-blocking IO is what you're looking for. Instead of blocking until a SocketChannel (non-blocking alternative to Socket) is returned, it'll return null if there is currently no connection to accept.
This will allow you to remove the timeout, since nothing will be blocking.
You could also register a Selector, which informs you when there is a connection to accept or when there is data to read. I have a small example of that here, as well as a non-blocking ServerSocket that doesnt use a selector
EDIT: In case something goes wrong with my link, here is the example of non-blocking IO, without a selector, accepting a connection:
class Server {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
while(true) {
SocketChannel sc = ssc.accept();
if(sc != null) {
//handle channel
}
}
}
}
The second approach is better (for the reasons you mentioned: relying on exceptions in normal program flow is not a good practise) allthough your code suggests that serverSocket.accept() can return null, which it can not. The method can throw all kinds of exceptions though (see the api-docs). You might want to catch those exceptions: a server should not go down without a very good reason.
I have been using the second approach with good success, but added some more code to make it more stable/reliable: see my take on it here (unit tests here). One of the 'cleanup' tasks to consider is to give some time to the threads that are handling the client communications so that these threads can finish or properly inform the client the connection will be closed. This prevents situations where the client is not sure if the server completed an important task before the connection was suddenly lost/closed.
I am writing an application that streams data that clients can then listen to and receive. However I am running into an issue with closing a socket when a client is no longer listening.
What I do is create a ServerSocket, when then waits for a connection and once it is connected, I start streaming the data. However, once the client is no longer connected, I am stuck in a loop of streaming and cannot tell if anyone is listening. Is there a way around this?
try {
serverSocket = new ServerSocket(STREAM_PORT);
Socket clientSocket = serverSocket.accept();
PrintWriter pw = new PrintWriter(clientSocket.getOutputStream(), true);
while (true) {
pw.println("some data");
}
} catch (SocketException e) {
// Never occurs when client disconnects
} catch (IOException e) {
// Never occurs when client disconnects
}
I have tried using socket.isClosed(), but it always returns false. Am I approaching this from the wrong angle, or is there a way to do it. I would ideally not want the client to have to send the server a "end" command.
EDIT: Edited to reflect what current code I am running after #Rod_Algonquin suggestion
As you are using PrintWriter, which swallows I/O exceptions, you need to call checkError() after each write to see if an error has occurred.
When using NIO, I have the following checks on the server side:
if (key.isReadable()) {
readBuffer.clear();
SocketChannel channel = (SocketChannel) key.channel();
int read = channel.read(readBuffer);
if (read == -1) {
channel.close();
channel.keyFor(selector).cancel();
} else {
readBuffer.flip();
System.out.println(charset.decode(readBuffer));
}
}
However, it is often the case that read will throw java.io.IOException: An existing connection was forcibly closed by the remote host. On the client side, this is what I do to close the connection:
public void close() throws IOException {
connection.close();
connection.keyFor(selector).cancel();
selector.close();
}
If that is not the graceful way, what is?
The server should send a message to the client to close its connection. It can only do this if the buffers are not full, but it will allows the client to gracefully close the connection.
A simpler solution is for the client to expect this exception and handle it silently. However, the poison pill message is more reliable IMHO as you can tell whether the connection was intended to be closed by the server.
BTW The client can send the same message to the server. After sending the message, you might want to wait for the other end to hang up before closing the connection yourself (or timing out)
You get this exception if you have written to the connection after the peer had already closed it. In other words, an application protocol error.
Solution: don't.
I have implemented a socket with a server and single client. The way it's structured currently, the server closes whenever the client closes. My intent is have the server run until manual shutdown instead.
Here's the server:
public static void main(String args[])
{
;
try
{
ServerSocket socket= new ServerSocket(17);
System.out.println("connect...");
Socket s = socket.accept();
System.out.println("Client Connected.");
while (true)
{
work with server
}
}
catch (IOException e)
{
e.getStackTrace();
}
}
I've tried surrounding the entire try/catch loop with another while(true) loop, but it does nothing, the same issue persists. Any ideas on how to keep the server running?
It looks like what's going to happen in your code there is that you connect to a client, infinitely loop over interactions with the client, then when someone disrupts the connections (closes clearning, or interrupts it rudly - e.g., unplug the network cable) you're going to get an IOException, sending you down to the catch clause which runs and then continues after that (and I'm guessing "after that" is the end of your main()?)...
So what you need to do is, from that point, loop back to the accept() call so that you can accept another, new client connection. For example, here's some pseudocode:
create server socket
while (1) {
try {
accept client connection
set up your I/O streams
while (1) {
interact with client until connection closes
}
} catch (...) {
handle errors
}
} // loop back to the accept call here
Also, notice how the try-catch block in this case is situated so that errors will be caught and handled within the accept-loop. That way an error on a single client connection will send you back to accept() instead of terminating the server.
Keep a single server socket outside of the loop -- the loop needs to start before accept(). Just put the ServerSocket creation into a separate try/catch block. Otherwise, you'll open a new socket that will try to listen on the same port, but only a single connection has been closed, not the serverSocket. A server socket can accept multiple client connections.
When that works, you probably want to start a new Thread on accept() to support multiple clients. Simplest way to do so is usually to add a "ClinentHandler" class that implements the Runnable interface. And in the client you probably want to put reading from the socket into a separate thread, too.
Is this homework / some kind of assignment?