I have a problem with handling socket in java.
I am running a TCP server with multiple client connections.
For performance reason, I used a simple thread pool to handle packets.
Please see code below
public enum LazyWorkCenter {
instance;
LazyWorkCenter() {
lazyWorker = new NamedThreadPoolExecutor(3,3, 0L,TimeUnit.MILLISECONDS, "LazyWorker");
}
private ExecutorService lazyWorker ;
public void executeLazy(Runnable lazyTask) {
lazyWorker.execute(lazyTask);
}
}
public class TcpServerForClient {
DataOutputStream out = null;
DataInputStream in = null;
public void onConnect(ServerSocket socket) throws IOException {
Socket client = server.accept();
client.setSoTimeout(1000 * 10);
out = new DataOutputStream(client.getOutputStream());
in = new DataInputStream(client.getInputStream());
}
public void sendToClient( byte[] buffer) {
Runnable lazy = () -> {
out.write(buffer);
out.flush();
};
LazyWorkCenter.instance.executeLazy(lazy);
}
}
multiple threads might access to sendToClient.
this code usually works fine, but some times(probably when flooding?) It hangs without any Exception until I shutdown client connection manually.
After I shutdown client connection, then I got tons of exception flooding SocketException: Broken pipe from out.write(buffer);
7053555 [LazyWorker-1] 09:57:35.268 [ERROR] [LazyWorker-1#c.t.s.TcpServerForClient:44] - error found
java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method) ~[na:1.8.0_91]
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) ~[na:1.8.0_91]
at java.net.SocketOutputStream.write(SocketOutputStream.java:153) ~[na:1.8.0_91]
at java.io.DataOutputStream.write(DataOutputStream.java:107) ~[na:1.8.0_91]
at java.io.FilterOutputStream.write(FilterOutputStream.java:97) ~[na:1.8.0_91]
My major Problem is that server could hang until client quit connection.
I guess If I can set a time out for writing then Server might close out connection by itself but I could not find appropriate way for this.
I have tried socket.setSoTimeOut(); as well but it seems only for receiving data from client.
Any hint or advice will be very appreciated.
Please let me know If more code or information is needed to figure this out.
Thanks in advance.
How can I set Socket write timout in java?
You can't. There is no socket write timeout in Java. Unless you make the hyperjump to NIO, non-blocking mode, Selectors, etc.
I got tons of exception flooding SocketException: Broken pipe from out.write(buffer);
You should only get one. You should close the socket when you get this exception.
Related
Why does the socket close when I try to write to the output stream?
The stacktrace:
Exception in thread "main" java.lang.RuntimeException: java.net.SocketException: Socket is closed
at chatx.server.handler.ExceptionHandler.handle(ExceptionHandler.java:14)
at chatx.server.handler.ExceptionHandler.handle(ExceptionHandler.java:6)
at chatx.server.Client.authenticate(Client.java:75)
at chatx.server.handler.SocketHandler.handle(SocketHandler.java:23)
at chatx.server.handler.SocketHandler.handle(SocketHandler.java:12)
at chatx.server.Server.listen(Server.java:117)
at chatx.server.Server.start(Server.java:61)
at chatx.server.Main.main(Main.java:30)
Caused by: java.net.SocketException: Socket is closed
at java.base/java.net.Socket.getInputStream(Socket.java:905)
at chatx.server.Client.authenticate(Client.java:65)
... 5 more
The pre-authentication phase:
Client client = (Client) Optional.ofNullable(clientManager.getClient(socket.getInetAddress())).orElse(clientManager.addClient(new Client(socket)));
client.sendPacket(new PacketAuthentication(client.getUsername(), client.getPassword().toCharArray()));
if (client.authenticate()) {
System.out.printf("[Address: %s] Authenticated as %s.\n", client.getAddress(), client.getName());
client.start();
} else {
client.interrupt();
clientManager.removeClient(client);
System.out.printf("[Address: %s] Failed to authenticate.\n", client.getAddress());
}
The authentication phase:
try {
JsonObject o = new GsonBuilder().create().fromJson(new String(socket.getInputStream().readAllBytes()), JsonObject.class);
if (o.has("packet_id") && o.get("packet_id").getAsInt() == PacketAuthentication.ID) {
JsonObject data = o.getAsJsonObject("data");
String username = data.get("username").getAsString();
String password = data.get("password").getAsString();
return this.username.equals(username) && this.password.equals(password);
}
} catch (IOException ex) {
Server.getInstance().getExceptionHandler().handle(ex);
}
The client-side authentication phase:
IClient client = new Client(new Socket("127.0.0.1", 8888));
client.sendPacket(new PacketAuthentication("Dummy", "Dummy123".toCharArray()));
The exception occurs in the authentication phase when socket.getInputStream() is called.
The idea was to let the client send an authentication packet on login and verify the information on server-side.
Edit:
I might have found the issue.
I am using a try-with-resources when writing to the OutputStream and I just realise that try-with-resources let the OutputStream close, thanks to #MousaHalaseh
But now I am getting another exception about 'Connection reset':
Exception in thread "main" java.lang.RuntimeException: java.net.SocketException: Connection reset
at chatx.server.handler.ExceptionHandler.handle(ExceptionHandler.java:14)
at chatx.server.handler.ExceptionHandler.handle(ExceptionHandler.java:6)
at chatx.server.Client.authenticate(Client.java:86)
at chatx.server.handler.SocketHandler.handle(SocketHandler.java:23)
at chatx.server.handler.SocketHandler.handle(SocketHandler.java:12)
at chatx.server.Server.listen(Server.java:117)
at chatx.server.Server.start(Server.java:61)
at chatx.server.Main.main(Main.java:28)
Caused by: java.net.SocketException: Connection reset
at java.base/java.net.SocketInputStream.read(SocketInputStream.java:210)
at java.base/java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.base/java.io.InputStream.readAllBytes(InputStream.java:238)
at chatx.server.Client.authenticate(Client.java:76)
... 5 more
Edit 2:
Fixed the issue in Edit 1, by closing one end of the outputstream.
the error is pretty clear Socket is closed, things that might help troubleshoot this Exception:
Make sure you're not using socket.close(); in the client side.
You're not closing any of the input or output streams or even shutting down the input/output, as from the documents
Closing the returned InputStream will close the associated socket.
Closing the returned OutputStream will close the associated socket.
Your connection does not get timed out.
You do have a valid TCP connection in your machine.
Edit:
java.net.SocketException: Connection reset generally means that one end of the stream is closed, which you said that yourself
Fixed the issue in Edit 1, by closing one end of the outputstream.
Keep the output stream open, and you may have to add:
socket.setKeepAlive(true);
hope things work out for you.
I've got a very simple multithreaded server that just prints back the client's input. The problem I'm having is that the client is crashing out after more than one use of outToServer.writeBytes().
My source code for the client is here:
public class Client {
public void run() throws Exception{
String sentence;
Socket clientSocket = new Socket("localhost", 25565);
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
while (true){
sentence = inFromUser.readLine();
if(!sentence.equalsIgnoreCase("exit")){
outToServer.writeBytes(sentence + '\n');
} else {
break;
}
}
clientSocket.close();
}
}
I've done some research on the error and it might be my college network killing the connection, but it doesn't make much sense given that it allows the first connection.
Also, here's the error:
java.net.SocketException: Software caused connection abort: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(Unknown Source)
at java.net.SocketOutputStream.write(Unknown Source)
at java.io.DataOutputStream.writeBytes(Unknown Source)
at com.cs.Client.run(Client.java:21)
at com.cs.Main.main(Main.java:14)
At line 21 in Client.java is the line with writeBytes in it
Opening a connection is not like opening a door. A connection is a virtual concept and not a guaranteed path.
So when you open a connection, basically you have reconfigured some operating system managed area of memory to know that when you write data into a particular memory location, it needs to copy that data on the wire.
This means that opening a connection is not always a shared event (as in the remote machine might not fully guarantee a path to the program, or even might not be aware that a path to the remote program was requested).
So, in network programming, despite APIs that are worded to imply otherwise, you don't have a functional connection until you get the first response from the remote machine.
I'd see if you can fire up wireshark and see if you can capture the data prior to send, and I'd check any connection parameters, and attempt to verify connection reachability independently of the program.
The above procedure will help you quickly identify which network componet is at fault from the client's point of view; however, 90% of the time, it is something really trivial, like a software firewall blocking the desired port.
Also, you can use telnet to provide similar functionality, but connecting to a non-standard port.
telnet hostname 25565
Good luck, and your code seems pretty reasonable, I'd spend a little time making sure that you aren't focusing on the code when the environment might be at fault.
I am having a lot of trouble in understanding the differences about how Java handles Sockets on Windows and Linux - Particularly when one of the sides (Client or Server) closes the connection abruptly.
I have written the following very simple Server and Client classes to keep my point as simple, objective and as easy for you to understand as possible:
SimpleClient.java:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class SimpleClient {
public static void main(String args[]) {
try {
Socket client_socket = new Socket("127.0.0.1", 9009);
// Used to read from a terminal input:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// Used for client/server communication:
BufferedReader in = new BufferedReader(new InputStreamReader(client_socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client_socket.getOutputStream()));
while(true) {
System.out.print("Command: ");
String msg = br.readLine();
// Send:
out.write(msg);
out.newLine();
out.flush();
// Receive:
int ifirst_char;
char first_char;
if((ifirst_char = in.read()) == -1) { // Server Closed
System.out.println("Server was closed on the other side.");
break;
}
first_char = (char) ifirst_char;
msg = String.valueOf(first_char);
msg += in.readLine();
// Shows the message received from the server on the screen:
System.out.println(msg);
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
SimpleServer.java:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class SimpleServer {
public static void main(String args[]) {
try {
ServerSocket server_socket = new ServerSocket(9009);
Socket client_socket = server_socket.accept();
while(true) {
BufferedReader in = new BufferedReader(new InputStreamReader(client_socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client_socket.getOutputStream()));
// Receive:
int ifirst_char;
char first_char;
if((ifirst_char = in.read()) == -1) { // Client Closed
System.out.println("Client was closed on the other side.");
break;
}
first_char = (char) ifirst_char;
String msg = msg = String.valueOf(first_char);
msg += in.readLine();
msg = "Server Received: " + msg;
// Send:
out.write(msg);
out.newLine();
out.flush();
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
Of course I could implement a code for properly shutting down the client or the server, but the objective, as I said, is to simulate an abrupt shutdown on either side, where no "disconnection code" could be sent or received. That's why I created these 2 very simple classes.
On Linux, it runs pretty well:
$ java SimpleClient
Command: echo
Server Received: echo
Command: test
Server Received: test
Command: (server now was closed on the other side)
Server was closed on the other side.
$
On Windows, however:
C:\simplesocket>java SimpleClient
Command: echo
Server Received: echo
Command: test
Server Received: test
Command: (server now was closed on the other side)
java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(Unknown Source)
at java.net.SocketOutputStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder.implFlush(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at java.io.BufferedWriter.flush(Unknown Source)
at SimpleClient.main(SimpleClient.java:32)
Let's say I try to ignore this Exception by modifying the following lines on my SimpleClient.java:
// Send:
try {
out.write(msg);
out.newLine();
out.flush();
}
catch(Exception e) {}
Another Exception is thrown:
C:\simplesocket>java SimpleClient
Command: echo
Server Received: echo
Command: test
Server Received: test
Command: (server now was closed on the other side)
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
at sun.nio.cs.StreamDecoder.read(Unknown Source)
at java.io.InputStreamReader.read(Unknown Source)
at java.io.BufferedReader.fill(Unknown Source)
at java.io.BufferedReader.read(Unknown Source)
at SimpleClient.main(SimpleClient.java:42)
I don't know if the corresponding lines on the code will be the ones pointed out on these Exceptions, but the first one is thrown on out.flush() and the second one on in.read().
So basically, as you can see on Linux, even after abruptly closing the server:
1. It doesn't throw an Exception when I try to send the data.
2. And more importantly, when I try to receive it, the first char is "-1" and received correctly.
On Windows, it throws Exceptions both when sending and more importantly on receiving - when calling the read() method - I cannot get the "end of the stream" (-1) code.
Which leads to some questions:
1. Why is there such a big difference on Windows x Linux? Why on Linux these Exceptions are not thrown while on Windows they are?
2. Shouldn't Java, with all its cross-platform qualities, try to minimize the differences on running in both the Systems? (by the way I'm using JDK 7 on both)
3. Is there a way to change the code for an abrupt shutdown and get it to work more "Linux-like" on Windows, without throwing all these Exceptions and getting the -1 on my in.read()??
4. If not, any external API recommended?
I've tried to search the web for hours on this specific topic but without success.
I have also tried many solutions like calling methods like isConnected(), isBound(), isClosed(), etc. in the client_socket on the client side without success. They always say that there is an active connection and no problem with it, even after shutting down the server.Hopefully someone would take the time to answer at least one of these questions.
You have my most sincere thanks in advance for any answers.
Your code doesn't do any close, so I'll assume you actually mean that one endpoint process is stopped aka killed.
Unix socket sd's are "just" fd's, and when a Unix process ends without closing an fd, including the case where a JVM stops
and you hadn't called close (or shutdown-WR), the fd is closed by the OS, which for TCP socket does (at least tries)
the normal aka graceful close: FIN/ACK exchange with FIN-WAITs and TIME-WAIT. The only way I know to make Unix socket
do graceless close at the TCP level (RST) is to set linger to 0 before closing (either explicitly or by exiting).
It is also possible and not uncommon for a middlebox to forcibly break your connection with RST; for example I've seen firewalls
that RST you after 15min inactivity. I've also more rarely seen middleboxes that fake FIN, or that try to but do it wrong.
Windows sockets (WinSock) are a different API than files. If a Windows process ends without calling closesocket (similar to
but separate from close) or at least shutdown-WR, Winsock does RST. To get graceful close (FIN) on Windows you (via JVM)
must call one of those. JVM presumably could track java.net.Sockets (but not any in JNI) and do this for you on JVM exit, but
it doesn't; you could request an enhancement. Even that might not work if you externally kill it with TaskMgr or similar, and
might not work right if you hit a JVM fault: the JVM tries to catch faults and give a minidump, which would be a place to try
to cleanup sockets, but if there was a JVM bug it might fail again -- and IME most JVM faults are due to JVM bugs.
If it's enough to handle code bugs (leaks) and signals but not JVM bugs and failures, you could just subclass Socket
so that if forces (graceful) close on .finalize and on exit using Runtime.addShutdownHook, and use that instead.
In either Unix or Windows sockets, received FIN is treated as end-of-file, like any other file for example a disk file.
Received RST is returned as an error [WSA]ECONNRESET to JVM, which raises an exception. It wouldn't be good to hide this
difference, because for apps other than yours it can be significant -- enough so that some protocols have had to be changed
to prevent fake-FIN being a security vulnerability notably SSLv2 and HTTP/0.9.
If you also consider cases where the peer system fails (not just the JVM), or certain parts of the network fail,
or your network interface fails, the Exceptions you can get are quite a bit more varied. IMHO don't try to handle those,
just report what you see and let the sysadmins and netadmins sort it out. I've seen cases where a programmer got Exception X
due to problem P in laboratory conditions and coded for that, but in the real world exception X happened for very different
reasons and the "helpful" handling actually made it harder to solve the problem.
Asides: the server should create the BufferedReader before not inside the while(true)do-a-line loop;
if you ever get/want a client that sends more than one line at a time, the code shown will lose data.
You don't need that hair for if first_char==-1 else convert to String; just use in.readLine, it returns null in exactly
the same case where initial in.read returns -1, which for (TCP) Socket is when FIN is received.
Conversely the client readLine from System.in should be checked; if someone types ^Z or ^D or whatever you'll get NPE.
I have not been able to find a satisfying answer to this question anywhere. Could someone with an understanding of the internals please explain this?
I wrote a simple client/server to demonstrate this issue. The server reads one line of text then closes the socket. The client writes one line of text, waits 10 seconds, then writes two more lines of text. The second write (after 10 seconds) fails but the first write always succeeds.
Why can't the BufferedWriter throw an exception on the first write itself? After all the socket was normally closed a long time before. The code also does a read on the socket right before the first write, returns -1 to show that the input side has already detected the socket close. Why can't the output side also know this?
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(9000);
Socket s = ss.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
System.out.println(in.readLine());
s.close();
System.out.println("Socket closed");
}
}
public class Client {
public static void main(String[] args) throws IOException, InterruptedException {
Socket s = new Socket("localhost", 9000);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
out.write("Hello, World!\n"); out.flush();
Thread.sleep(10000);
System.out.println("Read from socket returns: " + s.getInputStream().read());
out.write("First write\n"); out.flush();
System.out.println("First write succeeded without detecting socket closed");
out.write("Second write\n"); out.flush();
System.out.println("Second write succeeded without detecting socket closed");
}
}
A remote close is indistinguishable from a remote shutdown for output. This end receives a FIN in both cases, meaning the peer has stopped sending. There is no indication that he has stopped receiving, even in fact if he has shutdown for input. So the only way the sender can detect is by getting an RST on sending, and that can't happen on the first send, by definition, unless maybe the sent data is larger than the socket send buffer.
we looked at this on a project. I am of the opinion that the Internet Protocol more or less guarantee's the TCP/IP socket will do this.
The IP protocol is intended to do the best-job-possible to route a packet. You will only ever know a connection was gone at the other end after a write/delivery has failed. Remembering that the internet was designed to be resilient and try different routes, etc to get the message delivered.
Different network and data-link transports might work differently. A long while back I had to do a session layer over tcp/ip and this problem sounds oddly familiar.
It seems that you could work around it by sending a couple of test bytes before your main send.
I wrote a simple server client socket program and when I recompile the server I get:
java.net.BindException: Address already in use: JVM_Bind
at java.net.DualStackPlainSocketImpl.bind0(Native Method)
at java.net.DualStackPlainSocketImpl.socketBind(Unknown Source)
at java.net.AbstractPlainSocketImpl.bind(Unknown Source)
at java.net.PlainSocketImpl.bind(Unknown Source)
at java.net.ServerSocket.bind(Unknown Source)
at java.net.ServerSocket.<init>(Unknown Source)
at java.net.ServerSocket.<init>(Unknown Source)
Therefore my question is how to kill the socket under windows 7? Is there a possible solution to kill it in eclipse?
I appreciate your answer!!
Kill the jvm this fixed the issue when I ran into it. Are you closing the socket in your code before you stop your simple server?
Like RGdev I assume that you still have a javaw process running in the background which keeps the connection open. But it could also be a different server program on your machine which hogs the port you want to use.
You can find out which processes are listening to which port with the netstat command in the cmd shell. The following parameters list (a) all connections including servers, (b) shows the executable which opened the connection and (n) suppresses the substitution of port numbers with service names for well-known ports.
netstat -abn
Here is my code for server side:
import java.io.*;
import java.net.*;
public class ServerSide {
public static void main(String[] args) {
try
{
ServerSocket myServerSocket = new ServerSocket(9999);
System.out.println("Server is waiting on host" + InetAddress.getLocalHost().getCanonicalHostName() + "port= "+ myServerSocket.getLocalPort());
Socket skt = myServerSocket.accept();
BufferedReader myInput = new BufferedReader(new InputStreamReader(skt.getInputStream()));
PrintStream myOutput = new PrintStream(skt.getOutputStream());
String buf = myInput.readLine();
System.out.println("Server readLine");
if(buf!=null)
{
System.out.println("Buf = " + buf);
myOutput.print("Got it?");
}
else
{
System.out.println("Nothing returned in server sidex`x ");
}
skt.close();
System.out.println("Server shutdown");
}
catch(IOException e)
{
e.printStackTrace();
System.out.println("Whooops");
}
}
}
As you can see for clean-up I've written:
skt.close();
But maybe this is not your problem, Maybe your problem is which I had 1 hour ago ;) I used to run a program but the result is not what I expected so I modify it and run it again but the port was busy or already in use! What I do on eclipse? Under the Console where you get the error, on the right side of the window there is red colour rectangle button! It say "Terminate". If you click on that your port will be free. By the way don't forget to check the console for both(Server/Client) sides.
You can also get this error message, when the process already terminated. TCP has a time wait state. This state is used to ensure that no TCP packets from an old connection can be delivered to a new process listening at the same port. Normally you should use the ServerSocket.setRuseAddress(true) to avoid this issue.