Persistent Connections
Hi everybody,
I'm new here and I'm also a newbie in this topic: Java networking. So I have a question to you, because I didn't find a solution in this forum. If there is already posted one, I'm sorry for opening a new thread - maybe you can link the answer to me :-)
So, here is my problem:
I have an project where a ServerSocket is created which waits for incoming requests by calling the accept()-method. If there is a request a socket will be created (which is the return value of the accept()-method). I put the socket object to a new class called "MyConnection" which works as a thread (i.e. implements Runnable and has a run()-method). So I start this thread and in the run()-method the request is read and analyzed. A servlet finally reads the request and writes the response - if I understand right. After all, the socket is closed. If an new request comes from a client, an new MyConnection will be created, then a new socket, etc.
Here is a short overview of my implementation structure:
Server-Class
ServerSocket serverSocket =new ServerSocket(port, backlog, address);
...
Socket socket =null;
...
while(this.isRunning())
{
socket = serverSocket.accept();
synchronized(socket)
{
MyConnection connection =new MyConnection(this, socket);
Thread thread =new Thread(connection);
thread.start();
}
}
MyConnection-Class (run()-method)
...
InputStream socketInStream = socket.getInputStream();
// do something with request (analyze header...)
...
// dispatch to a servlet via a RequestDispatcher
...
// close socket
socket.close();
So that works fine, but now my task is to use persistent connections which are supported with HTTP 1.1. That means, I must have only one MyConnection during the whole time, which can get all requests and which is not closed. Am I right? So, how do I implement that? I don't really have an idea...
I hope you can give me a hint.
Thanks,
jacquipre.
[2490 byte] By [
jacquiprea] at [2007-11-27 8:51:46]

# 1
A persistent connection is closed by the client, or on a server-side timeout. So instead of dealing with just one request, your connection class loop should look like this:
socket.setSoTimeout(timeout);
while (get a request)
send(a reply);
// EOS on 'socket'
socket.close();
ejpa at 2007-7-12 21:05:43 >

# 2
So you mean that the MyConnection class now has a loop in the run()-method? How do I know when a new request is coming? How do I know when an EOS is sent?Sorry, if I'm not really understand.
# 3
You know a new request is coming when it arrives, and you block in the read until it does or the timeout occurs, in which case you get a SocketTimeoutException. If the read() returns -1 it is EOS. Ditto readLine() returning null, or readXXX for any other XXX throwing EOFException.
ejpa at 2007-7-12 21:05:43 >

# 4
Ok, so I changed my code to the following:
Server-Class
ServerSocket serverSocket = null;
try
{
serverSocket = new ServerSocket(1234, 10000, InetAddress.getByName("127.0.0.1"));
while (true) // server is running
{
Socket socket = serverSocket.accept();
synchronized(socket)
{
MyConnection connection = new MyConnection(socket);
Thread thread = new Thread(connection);
thread.start();
}
}
}
catch (IOException ioe)
{
System.out.println(ioe);
}
finally
{
// close server socket
}
MyConnection-Class (run()-method)
....
socket.setSoTimeout(90000);
....
BufferedReader in = null;
OutputStreamWriter out = null;
try
{
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new OutputStreamWriter(socket.getOutputStream());
String line = null;
while ((line = in.readLine()) != null)
{
out.write("Server [ "+line+" ]\r\n");
out.flush();
}
}
catch (SocketTimeoutException stoe)
{
// what should be done here? socket.close() ?
}
catch (IOException ioe)
{
}
finally
{
// close all streams and socket ?
}
If there is nothing more to read (i.e. readLine() == null) the run-method is leaved, isn't it? But the socket is still open for a new request?
If there is a socket timeout the exception is thrown and the socket is closed, right? I don't really see the difference to the normal (not persistent version I posted earlier). Persistent connection means that there is only one connection, right? So in this case only one socket object (i.e. one MyConnection). If the connection is timed out or closed by the client how to close the MyConnection object (i.e. Thread)... ?
Sorry! Maybe this is a very easy problem but for me it's completely new.
# 5
> If there is nothing more to read (i.e. readLine() ==
> null) the run-method is leaved, isn't it?
It's your run() method, what does it do?
> But the socket is still open for a new request?
The socket is open until you close it. But the null, meaning EOS, meaning end-of-stream, means that it is already closed at the other end. So there will never be another request. Close it.
> If there is a socket timeout the exception is thrown
Yes, a SocketTimeoutException.
> and the socket is closed, right?
Only if you close it. Not before.
You should close it on any IOException, including but not limited to SocketTimeoutException. There is no exception that closes it for you.
> I don't really see the difference to the normal (not persistent version
> I posted earlier).
You don't see a rather large difference? between (i) reading one request and replying to it and then closing the socket, and (ii) looping reading requests and sending replies until there are no more requests.
> Persistent connection means that there is only one connection, right?
Not really, it means that a connection can be used for more than one request.
> how to close the MyConnection object (i.e. Thread)...
Return from the run() method.
> Sorry! Maybe this is a very easy problem but for me
> it's completely new.
Ok, but it is really very simple. Have a look at the pseudo-code I gave above. The only difference from what you had at first is the loop and the timeout.
ejpa at 2007-7-12 21:05:43 >

# 6
Ok, I now understand:
// set socket timeout
...
try
{
while ((line = in.readLine()) != null) // if null, socket is already closed, return from run-method
{
// analyze request and write response
}
}
catch (IOException ioe)
{
// close stream and socket (maybe an SocketTimeoutException occurs)
// and return from run-method
}
It's really very easy, so sorry :-) and thanks for your patience.
# 7
Hey, it's me again!
Ok, I thought I understand persistent connections and tried to update my project to this alternative, but now I've a problem. Maybe, I must explain this in more detail.
So our server get web requests from a client i.e. browser. At the moment for each request a Connection is created. The Connection is a thread class. In this connection class the request is analyzed, that means we read the request line by line until the line is empty. If this is the case a response object is created , some headers are set and the request as well as the response are forwarded to a dispatcher which decides what servlet the receiver is. The servlet finally writes the response. If this is ready the socket is closed. Now, the server class waits for new incoming request(?) by using the serverSocket.accept()-method, which returns a socket. With this socket again a Connection is created... and so on. What we want is that only one connection is created where all the request are processed without closing all the sockets. So we thought on persistent connections. I was wondering how to realize that a new request is coming without closing the socket, i.e. when the request line is empty (what it normaly is after the http header). Maybe I made a big error in reasoning... sorry.
Here is some code again, maybe you understand better what I mean.
Server Class:
...
Socket socket = null;
while (this.isRunning()) {
try {
// listen for connections
socket = serverSocket.accept();
synchronized (socket) {
MyConnection connection = new MyConnection(this, socket);
Thread thread = new Thread(connections, connection, "DEBUG_" + counter );
thread.setDaemon(true);
thread.start();
}
}
}
MyConnection - run
...
socketInStream = socket.getInputStream();
in = new BufferedReader(new InputStreamReader(socketInStream));
// read the first line
line = in.readLine();
// analyze it: set protocol, set method (GET,...)
...
// read the rest until there is an empty line
while((line = in.readLine()) != null) {
if( line.trim().length() <= 0 ) break;
// save header information
...
}
...
// request ready, create request and response for servlet
HttpServletResponse response = new HttpServletResponse(this);
HttpServletRequest request = new HttpServletRequest( this.getConnectionSession( ) ,this);
// dispatch it
this.server.getDispatcher().dispatch(request, response);
// if ready, close stream
in.close();
...
// in finally-block close socket
socket.close();
Ok. You see that the socket is closed after the request. So in the server class a new MyConnection is created when the accept()-method retrieves a new connection on the server socket. Hmm.. I don't really know how to prevent that, so that the previous connection stays alive and a new request can be handled by the same connection i.e. the same socket.
:-(
-- jacquipre
# 8
You have to read the request until you have the whole request. At present you're reading it until you have encountered EOS, which could be any number of requests. You need to take note of the Content-length: header to know when to stop reading the current request, or whatever else the HTTP protocol provides for that, don't ask me, and then process it and send the response. You then don't close the socket, you go back to the reading loop to read the next request. You only close the socket when you get EOS.
ejpa at 2007-7-12 21:05:43 >

# 9
Ok, what I've now done is to separate the requests and the connections like this:
Server
ServerSocket serverSocket = null;
Vector connections = new Vector();
while (true) // server is running
{
// now listen for connections
Socket socket = serverSocket.accept();
synchronized(socket)
{
MyConnection connection = new MyConnection(socket);
Thread thread = new Thread(connection);
thread.start();
connections.add(connection);
}
}
MyConnection
while (true)
{
line = in.readLine();
if (line.trim().length() <= 0)
{
// request end (GET-Header)
MyRequest request = new MyRequest(this, requestString);
requestString = "";
}
else
{
requestString += line + "\n";
}
}
MyRequest
OutputStreamWriter out = new OutputStreamWriter(connection.getSocket().getOutputStream());
out.write("Response...");
out.flush();
So, I think I can receive many requests on the same socket in the MyConnection class. Everytime a request ends I create a new MyRequest object which finally sends a response to the client. Do you think this make sense? Do I have to open a new thread for MyRequest class? I save all connections in a list (vector); if a connection will throw an timeout error, I can close this connection. So there must be a connection watcher, I think, which checks all connections for activity...? Do you have any other (better) proposals? If so, I'm glad if you would answer me...
Thanks.
# 10
> while (true)
> {
> line = in.readLine();
if (line == null)
break; // EOS
And don't create a new PrintStream in MyRequest, use the same one for the life of the socket. Don't use PrintStream at all actually, unless you like losing exceptions.
> Do I have to open a new thread for MyRequest class?
No. That connected client is waiting for the response on that socket, so he won't be sending you another request on it till you reply.
> So there must be
> a connection watcher, I think, which checks all
> connections for activity...?
No. All you need is catch (SocketTimeoutException exc) in the above, as you were told five days ago, and call Socket.setSoTimeout(timeout) somewhere in the above before the read, probably as soon as you accept the socket.
ejpa at 2007-7-12 21:05:43 >

# 11
> And don't create a new PrintStream in
> MyRequest, use the same one for the life of the
> socket. Don't use PrintStream at all actually, unless
> you like losing exceptions.
Ok, I changed it like that:
MyConnection
out = new BufferedOutputStream(socket.getOutputStream());
...
MyRequest request = new MyRequest(this, requestString, out);
MyRequest
// test
out.write(("Response").getBytes());
out.flush();
> > Do I have to open a new thread for MyRequest
> class?
>
> No. That connected client is waiting for the response
> on that socket, so he won't be sending you another
> request on it till you reply.
Yes, that was exactly what I'm thinking about. No second request will come on the socket until there was no response sent.
> > ... connection watcher...
>
> No. All you need is catch (SocketTimeoutException
> exc) in the above, as you were told five days ago,
> and call Socket.setSoTimeout(timeout) somewhere in
> the above before the read, probably as soon as you
> accept the socket.
Yes, this is actually what I'm doing but I didn't post it in the code above...
I think I got it now... Thanks for your kind help, ejp!
