Sethiel
Sethiel

Reputation: 312

General sockets proxy server effectively working with keep-alive connections as well (Java)

I am trying to create a general TCP socket proxy server in Java - any connection that gets accepted on server socket is both-ways connected to another socket created to the intended target.

I've googled a lot and not a single article, tutorial or any other piece of advice I've found gives solution to the basic problem I've encountered. First the backbone of the code:

is = socket1.getInputStream();
os = socket2.getOutputStream();

int read = 0;
while ((read = is.read()) != -1) {
    os.write(read);
}

The reason why this is not working as expected is this:

Writing and reading from the sockets one-byte-at-a-time is slow. So I need to use BufferedInputStream/BufferedOutputStream or anything that does the same. When I use BufferedOutputStream, everything is just buffered and is only written when I tell it to be written (or when the buffer fills, which is not interesting case) by flushing or closing.

Now it gets interesting. Pretty much every HTTP client doesn't actually close the connection/stream after sending a whole request (keep-alive connection). So I don't actually know when can I flush the output stream without reading the content of the transmission, determining if it's HTTP and if so, parse it's headers, pick the Content Length header and by that read only the specified amount of bytes of the body.

The intention here is to do this in Java 1.5 (this is a soft limit, but would be very nice) and keep the increase in response delay as low as possible (10 ms is OK, 50 ms sucks, 100 ms is unacceptable). I'll be grateful for any help.

CLARIFICATION: Maybe I wasn't clear enough, assuming by the first response. The issue here is that the read() method will not return -1 after one HTTP request, because the connection is kept open for another request ... so the loop doesn't end after that single request, the read() call hangs blocking and waiting for another input. Also, the buffered stream will not work without me calling flush(), but I don't know when to call it - don't know how to recognize end of HTTP request without parsing the content of the transmission.

Upvotes: 0

Views: 331

Answers (1)

MadConan
MadConan

Reputation: 3767

First question: Am I right, or am I missing something and there is an easy way to overcome this?

I think you're making some assumptions that aren't true. I've done a lot of stream read/writing and have never run into the issues you are describing. The whole idea behind Java, W3, etc, is not needing to know the details about how the interface implementation works. I agree there are corner cases when the behavior of the interface doesn't follow the implied (and in some cases literal) contract, but it's usually best to go with the assumption that the client is following the "rules."

You should know what protocol is coming through what port just like any server. Pick a port (80?) and know that any traffic coming into that port is HTTP traffic. Likewise for FTP, SFTP, etc.

For your point about the streams being slow and buffering, have you tried using a byte array and specifying the buffer size?

private final static int BUFFERSIZE = 1024 << 10;  // 1 MB
...
// .. in some method
BufferedInputStream in = new BufferedInputStream(inSocket.getInputStream(), BUFFERSIZE);
BufferedOutputStream out = new BufferedOutputStream(outSocket.getOutputStream(),BUFFERSIZE);
byte[] bytes = new byte[BUFFERSIZE];
int bytesRead;
while((bytesRead = in.read(bytes)) != -1){
       out.write(bytes,0,bytesRead);
}
// omitting try catch finally logic
in.close(); 
out.close();

Upvotes: 1

Related Questions