Is sockets and input/output-streams thread-safe?
Hi
When creating a simple socket client application I would usually use BufferedReader on the inputstream, PrintWriter on the outputstream and a regular socket as the foundation running within its own thread and responding to events from the Swing UI. Now the question is:
Is for instance the Socket.close() and PrintWriter.write() methods thread safe? Will it lead to problems if these are called from other or perhaps even several other threads? If so, how can I fix this?
I have been googling for this for a while without any significant results, and no tutorials/examples of this scenario has done anything to protect against potential thread issues.
Writing to a socket by multiple threads is thread-safe as long as the other end can make sense of the intereleaved data. Reading from a socket by multiple threads is thread-safe as long as this end can make sense of the interleaved data.
I don't know why anyone would want to do either of these.
Reading and writing simultaneously presents no problems at all.
I would avoid using PrintWriter and PrintStream on a socket as they swallow exceptions.
What should I use instead of PrintWriter for sending textstrings to a socket then?
I am not sure what you mean by interleaved, but as far as I know for my usage, only one thread (the socket thread) will read the data so this is probably no problem :)
But lets say I have a socket running in its own thread where the BufferedReader.readLine() blocks the thread. If 2 threads at the same time use the outputstream for sending data, wont this lead to problems? Perhaps I should make a synchronized sendData() method?
Same goes for close() I assume. Lets say one thread is closing the socket/stream. Now lets say that another thread try to do the same 1 ms after the first one. Wont this lead to issues if I dont have a synchronized close() method?
Message was edited by:
invictus2
I gather you have a line-based protocol that sends strings (not binary data).
Use an OutputStreamWriter. For maximum character set compatibility, create both the OutputStreamWriter and the reading end with an explicit character set, e.g. new OutputStreamWriter(socket.getOutputStream(), "UTF-8").
If you have two threads that send one-line messages: writer.write("hello\n"); then that will be thread safe because Writer is synchronized.
However, if you send multiple messages and expect them to arrive without getting mixed up, you need to synchronize. E.g.:
// These writes can be intertwined with another thread's writes
writer.write("message: lines=2\n");
writer.write("first line of message data\n");
writer.write("second line of message data\n");
// These are supposed to arrive as a single line,
// but another thread can mess them up.
// This too needs a sync block around all the writes.
writer.write("hello, my name is ");
writer.write(myName);
writer.write(", who the hell are you?");
writer.write("\n");
Synchronization is very fast in Java, so the cost of doing it is low. Having a centralized sendData() method is a good idea if that fits nicely into your application's logic.
I'd synchronize close() if that can be called from several threads. SocketOutputStream.close() isn't thread safe. I wonder if that is intentional...
Reading from a socket in several threads can be problematic because TCP/IP chops up writes to whatever packes it feels like. So if you try to read one line or some other kind of message, you end up getting one fragment in one thread and another in another thread. There may be applications where that doesn't matter, but in many cases you don't want that. But since you have a single reader thread this isn't an issue for you.
Incidentally, looking at the implementation of java.net.SocketOutputStream.write(int):
public void write(int b) throws IOException {
temp[0] = (byte)b;
socketWrite(temp, 0, 1);
}
That ain't thread safe! Also, the Sun implementation of Socket.socketWrite0() breaks up writes greater than 64k. So I'd suggest being safe and synchronizing, either by syncing your sendData() or by using something like java.io.Writer if you don't need to sync across several write()s.
What reader/inputstream has the ability to read lines that also support explicit character sets?
My guess (which perhaps isnt the best) would be:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
However, I must admit I am not very comfortable with this whole new something(new something_else(new something(new something_else) ))) kind of syntax, so I would prefer one single "do it all" solution instead of wrapping objects inside of eachother...if possible :p
For output I could just use OutputStreamReader as you already suggested as writing lines are as easy as writing a string and adding a \n or \r\n at the end.