Data read through socket on a remote server is empty - java

We have a client-server software made in java.
Client part sends a string:
dataOutputStream.writeUTF("User");
Server reads this string:
String login = dataInputStream.readUTF();
System.out.println("User accepted: " + login);
The server part is launched on amazon ec2.
We have 2 offices and from 1 office everything works fine:
server accepts the socket and read the data correctly,
but from another office the server accepts the socket, however reads empty strings.
All the ports are opened for our IPs in the EC2 control panel.
I have installed WireShark and checked if correct data goes to the server and yes, it is correct.
Both offices are running the same client part.
The only difference i know is that office 1 have Win 7 and 8 (works on both, tried different machines) and office 2 have Win 10 (doesn't work, tried different machines)
What could be the reason of such behavior? What we could check / try to resolve it?

DataInputStream.readUTF() cannot possibly return an empty String, by which I assume you mean a String object whose length() is zero, unless an empty string was presented to writeUTF() at the other end. It can however throw an EOFException if the data has somehow been truncated in flight.
You'll have to post some code to elucidate this further.

Related

Java TCP Socket Programming: Client and Server communicate well on the same computer, but fail to send data to each other over LAN

I am trying to set up a program where the server can communicate with multiple clients. The program is written in Java. I got it all working on the same machine so I decided to try LAN. I converted the program to JAR files and I tried connecting my laptop to my PC (both are on the same network). The connection is successful but unfortunately only 1 message arrives to the server. As you can see in the code below, I send multiple messages (Meaning that i write multiple times) via DataOutputStream. One defines the datatype (in the following example 0 means that it's a String) and the other sends the actual message data. I also print the size of the packets in bytes and it always matches the size of the DataOutputStream instance.
DataOutputStream dOut = new DataOutputStream(clientSocket.getOutputStream());
String str = "Hello";
//Type
System.out.println("Type output size: 1");
dOut.writeByte(0);
//Message
System.out.println("Message output size: " + (str.getBytes(StandardCharsets.UTF_8).length + 2));
dOut.writeUTF(str);
System.out.println("Length of all: " + (dOut.size()));
dOut.flush();
So now when the data from the client is sent we need to handle it on the server, which the code below does. It retrieves the InputStream from the Socket called client and inserts it into the DataInputStream. This is were it gets weird on LAN as the stream only contains the first message.
InputStream stream = client.getInputStream();
DataInputStream dIn = new DataInputStream(stream);
while(dIn.available() > 0) {
byte type = dIn.readByte();
switch(type) {
case 0:
System.out.println(dIn.readUTF());
break;
case 1:
System.out.println(dIn.readInt());
break;
case 2:
System.out.println(dIn.readByte());
break;
default:
throw new IllegalStateException("Unexpected value: " + type);
}
}
If you run the Client in the IDE on lets say a laptop connected to the same network and then you run the Server on a PC connected to the same network it will work. However, not if the programs are in JARS.
The actual stacktrace is the following:
java.net.SocketException: Connection reset
at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:323)
at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350)
at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:961)
at
java.base/java.io.DataInputStream.readInt(DataInputStream.java:393)
The stacktrace does not tell me anything, but it points at case 0: in the switch case. It can't read the String as the DataInputStream does not contain any data (I guess?).
I would also like to state that the Server is multithreaded! I have one thread that adds the sockets when they are accepted through ServerSocket.accept() and I use the second (main thread) to read the data sent from clients.
I have selected the code above as I believe that the issue lies within it, however I am new to Socket Programming and I know that some of you would like to see other parts of the code. I will add more relevant code when I am asked.
I do not know why it acts like this, does anyone know why?
What have i tried?
I have tried waiting for packets - but that has only resulted in the
Server looping forever. With waiting for packets I mean not going forward until the DataInputStream contains enough bytes.
I have disabled Nagels Algorithm through setTCPNoDelay(false).
Tried to send different datatypes, but that also failed
I tried changing the first packet to a String which resulted in the String showing up in the DataInputStream.
I have tried portforwarding the port used and I have tried disabling the firewall on both computers.
Update 1
I have been taking advice from the comments which has led a to a few discoveries:
Closing the DataOutputStream successfully sends all packets to the client.
It is also possible to build your own buffer and decode it in the server. However, it is still not possible to send any more messages after this.
It worked as a JAR because IntelliJ was being nice (Eclipse threw the same error when running in IDE)
Update 2:
I think this post is relevant. It states that SocketException is sent when a client closes it's socket "ungracefully". And because my Client closes (as it is not in a loop) and I don't close the socket properly - it will close "ungracefully" and the data will be lost. Hence the error.
The issue is now solved, and the solution is quite logical. My client does not operate in a loop, rather it sends the data and closes the program. That sounds fine, but I forgot to properly close the socket of the client.
The reason why the second 'packet' never arrived was due to me doing this tiny mistake. The packet was on it's way through the local network but the client socket improperly closed it's socket before the packet arrived to the server, which is why I got a SocketException error. See this.
I solved the issue by putting socket.close() ,where socket is the client's socket, after I had sent all the messages I wanted to send.

Java WebSocket message limit

I'm trying to create communication between simple Java App (using java.net.http.WebSocket class) and remote google-chrome run using google-chrome --remote-debugging-port=9222 --user-data-dir=.
Sending and receiving small messages works as expected, but there is an issue in case of bigger messages, 16kb.
Here is part of java source:
var uri = new URI("ws://127.0.0.1:9222/devtools/page/C0D7B4DBC53FB39F7A4BE51DA79E96BB");
/// create websocket client
WebSocket ws = HttpClient
.newHttpClient()
.newWebSocketBuilder()
.connectTimeout(Duration.ofSeconds(30))
.buildAsync(uri, simpleListener)
.join();
// session Id attached to chrome tab
String sessionId = "...";
// send message
String message = "{\"id\":1,\"method\":\"Runtime.evaluate\",\"params\":{\"expression\":\"document.body.style.backgroundColor = 'blue';\",\"returnByValue\":true,\"awaitPromise\":true,\"userGesture\":true},\"sessionId\":\"" + sessionId + "\"}";
// this works
ws.send(message, true);
// generate big string contains over 18k chars for testing purpose
String bigMessage = "{\"id\":2,\"method\":\"Runtime.evaluate\",\"params\":{\"expression\":\"[" + ("1,".repeat(9000)) + "1]\",\"returnByValue\":true,\"awaitPromise\":true,\"userGesture\":true},\"sessionId\":\"" + sessionId + "\"}";
// this doesn't work
ws.send(bigMessage, true);
Here is stack:
java.net.SocketException: Connection reset
at java.base/sun.nio.ch.SocketChannelImpl.throwConnectionReset(SocketChannelImpl.java:345)
at java.base/sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:376)
at java.net.http/jdk.internal.net.http.SocketTube.readAvailable(SocketTube.java:1153)
at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.read(SocketTube.java:821)
at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowTask.run(SocketTube.java:175)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:198)
...
I've tried basically the same by using puppeteer (nodejs library) and it works as expected.
I can't find any resource online about this issue.
Is there anything I'm missing in my example?
Here is url to simple example:
https://github.com/zeljic/websocket-devtools-protocol
Based on what I've seen so far, my best guess would be that Chrome Dev Tools do not process fragmented Text messages on that exposed webSocketDebuggerUrl endpoint. Whether Chrome Dev Tools can be configured to do so or not, is another question. I must note, however, that RFC 6455 (The WebSocket Protocol) mandates it:
Clients and servers MUST support receiving both fragmented and unfragmented messages.
There's one workaround I can see here. Keep in mind that this is unsupported and may change in the future unexpectedly. When running your client, specify the following system property on the command line -Djdk.httpclient.websocket.intermediateBufferSize=1048576 (or pick any other suitable size). As long as you keep sending your messages with true passed as boolean last argument to the send* methods, java.net.http.WebSocket will send messages unfragment, in a single WebSocket frame.
Well I had a similar issue when sending a big string by using web-sockets in java with a tomcat server.
There can be payload limit to send or receive in websocket server .
checkout org.apache.tomcat.websocket.textBufferSize in tomcat's doc. By default it is 8192 bytes try increasing the size.

Sending messages on same socket connection while a file is being sent

My app can transfer files and messages between server and client. Server is multithreaded and clients simply connects to it. While file is being transferred, if sender sends a message, it will be consumed as bytes of file.
I don't want to open more ports,
Can I establish a new connection to the server for file transfer? Or I
should open a separate port for files.
I don't want to block communication while a file is being transferred.
The question was marked as a duplicate but its not, i am trying to send messages and files simultaneously not one by one. I can already receive files one by one. Read again.
Also, as server is multithreaded, I cannot call server socket.accept() again to receive files in new connection because main thread listening for incoming will try to handle it instead. Is there a way around?
Seems to me like trying to multiplex files and messages onto the same socket stream is an XYProblem.
I am not an expert on this, but it sounds like you should do some reading on "ports vs sockets". My understanding is that ip:port is the address of the listening service. Once a client connects, the server will open a socket to actually do the communication.
The trick is that every time a client connects, spawn a new thread (on a new socket) to handle the request. This instantly frees up the main thread to go back to listening for new connections. Your file transfer and your messages can come into the same port, but each new request will get its own socket and its own server thread --> no collision!
See this question for a java implementation:
Multithreading Socket communication Client/Server
you could use some system of all the lines of a file start with a string like this (file:linenum) and then on the other side it puts that in a file then to send text you could do the same thing but with a tag like (text)
Server:
Scanner in = new Scanner(s.getInputStream());
while(true) {
String message = in.nextLine();
if(message.length > 14 && message.substring(0,6).equalsIgnoreCase("(file:") {
int line = Integer.valueOf(message.substring(6).replaceall(")", ""));
saveToFile(message.substring(6).replaceAll(")","").replaceAll("<1-9>",""));
} else {
System.out.println(message);
}
}
I think that code works but I haven't checked it so it might need some slight modifications
You could introduce a handshake protocol where clients can state who they are (probably happening already) and what they want from the given connection. The first connection they make could be about control, and perhaps the messages, and remain in use all the time. File transfer could happen via secondary connections, which may come and go during a session. Having several parallel connections between a client and a server is completely normal, that is what #MikeOunsworth was explaining too.
A shortcut you can take is issuing short-living, one-time tokens which clients can present when opening the secondary connection and then the server will immediately know which file it should start sending. Note that this approach easily can raise various security (if token encodes actual request data) and/or scalability issues (if token is something completely random and has to be looked up in some table).

Sending Player to another Server through Bungee

So I've tried to send players to the lobby server when they type "/lobby" by using this code:
Bukkit.getServer().dispatchCommand((Player) player, "server lobby");
But in game, it returns "Unknown Command. Type "/help" for help."
I've also read to try this code, but I don't know what to put in place of "a", "b", and "c"
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("a");
out.writeUTF("b");
//applies to the player you send it to. aka Kick To Server.
Player player = Bukkit.getPlayerExact("c");
player.sendPluginMessage(this, "BungeeCord", out.toByteArray());
So my question is why does the first method not work? Also, how do you make the second solution work? Is there code I need to put in the bungee server?
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("Connect");
out.writeUTF("ServerName");
player.sendPluginMessage(PluginObject, "BungeeCord", out.toByteArray());
This is what you are looking for. To answer your question, "a" is always "Connect". "b" is the name of the server that you want to connect to (In your case this is "lobby"), and "c" will the name of the player (This is because in the example that you've supplied your getting the player by their name, however if you've already got the player object, this isn't needed).
And just so that you understand why dispatchCommand doesn't work, it's because when you do /server on your client it sends a packet to the Bungeecord as that's what your client is connected to, and it will intercept that command packet read it and see that it's a server command. It will then proceed to connect you to the right server. If you use dispatchCommand on the Bukkit server it will execute the command as though it just received an command packet, which means it never sends out any sort of message to the bungeecord or to the client to get it to execute a command. The dispatchCommand method will only be run on the bukkit server.
The reason that the second solution works is because in the Minecraft protocol there's this thing called the plugin messaging channel, for custom messages between the client and the server. And yet again as the Bungeecord sits in the middle of the client and the server, it listens on certain messages, and any of them tagged with Connect will connect a player to the specified server. I hope this makes sense.

Sending object and commands to a server

I'm new to Java socket programming, looking for a good approach to send either commands or objects to a server via Java sockets. The objects shall be stored on the server, the commands shall request data from the server.
At first the server doesn't know what he receives in the input stream, so he has to examine it, but I'm not sure how to do that. I would take the input stream, convert it to a String and then check the first chars to decide if they form a command or not. The problem I have is that InputStream.toString() returns something like
java.net.SocketInputStream#437d51a6
Thanks for your opinions and ideas.
Here is my first bad approach:
String input = inputStream.toString(); // this doesn't work
String startString =
input.toString().substring(0, Math.min( input.toString().length(),3));
if(startString.equals(COMMAND)){
// process command, e.g. to request data from the server
}
else {
// extract object to send data to the server
}
There is nothing wrong with "simple" text commands. Have a look at SMTP or HTTP, it's just plain text.
And there is a good reason for that: You can just telnet into your server, and type in the commands. This is a great help, because you can query your server without a special client.
Example for telnet into a local web server (I just typed in "GET /")
$ telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.localdomain.
Escape character is '^]'.
GET /
<html>
<body>
<h1>Welcome on xxx</h1>
</body>
</html>
Connection closed by foreign host.
Further your service is not tied to a special language or a special binary format respectively.
I have found out how this works. The trick is to wrap the inputStream into a Scanner object like this:
Scanner s = new Scanner(inputStream);
String str = s.nextLine();
One approach may be creating classes for data types as well as for commands.
You can then use writeObject method of ObjectOutputStream ( http://docs.oracle.com/javase/7/docs/api/java/io/ObjectOutputStream.html ) to send command/data to server using default Java serialization (that's assuming that needed classes are also present in the server classpath).
Server can then get them from ObjectInputStream and easily act accordingly to their type. You will have to cast them from Object type, but you can check their real type using .getClass() or instanceof if you need to.

Categories

Resources