A reliable way to read socket data - java

The application that I am working on has two parts. The server part runs on a Linux machine. The client part, an Android application, queries the server and gets necessary response. Both the parts are written in Java, use socket-based communication, and transfer textual data.
Right after sending the request, here is how the client receives the response:
public static String ReadAvailableTextFromSocket(BufferedReader input) throws IOException {
if (input.ready() == false) {
return null;
}
StringBuilder retVal = new StringBuilder();
while(input.ready()) {
char ch = (char) input.read();
retVal.append(ch);
}
return retVal.toString();
}
However, this doesn't seem to be that reliable. The input is not always ready because of server response time or transmission delays.
Looks like input.ready() is not the right way to wait for getting data.
I am wondering if there is a better way to accomplish this. Perhaps there is some standard practice that I could use.

Perhaps you should use Threads. Keep a listener thread in a while(true) loop that reads more data as it comes in, and simply buffers the data in a data structure (let's say a queue) shared with the main thread. That way, the main thread could simply dequeue data as needed. If the queue is empty, it can infer that no new data was received.
Edit: see this multithreaded chat server/client code as an example.

Here is how I solved this problem. As I am responsible for writing both, the client side as well as the server side, when a request comes to the server, the first line of information I send as the response is the number of bytes the client can expect. This way, the client first waits to read a line. Once the line is read, the client now knows how many bytes of data to expect from the server.
Hope this helps others.
Regards,Peter

Related

multi function socket java, socket vs NIO socket

Take it easy with me, I'm new in socket programming.
I'm about making a program which is similar to Teamviewer. I could make a simple Server/client application which is multithreaded. But the problem is I couldn't figure out how to do something like :
Let's say I have Server and a client connected to server.
I could transfer a file from client to server. I could make a simple chat with a client. But my problem is : How can I chat with the client while file is transferring in the same time? I mean I couldn't make more than one function in a time.Because we have just one input and one output for both server and client. So how could I send more that one function to client and how could the client read more that one function in a time and respond to requests ?
I did something like : I sent a request to client and I got the response in a new thread which contains a new DataInputStream but I couldn't figure out how the main DataInputStream will receive a new response from the client because what is receiving the requests in this way is the new DataInputStream. I'm really lost in this situation because I feel my concept is completely wrong but I couldn't figure out the right concept to do something like that.
Is it possible with ServerSocket or should I look at NIO Socket ?
NOTE :
I don't want a piece of code, I would like to understand the concept of the whole operation for something like that. Thank you
Use a packet based massaging system over a single connection
Outgoing
{"type":"chat", "message":"hi"}
{"type":"xfer", "fileName":"fileX", "data":"some_binary_data"}
Incoming
switch(getString("type")){
case "chat":
System.out.println("User said" + getString("message"));
break;
case "xfer":
File f = new File(getString("fileName"));
f.write(getString("data"));
break;
...
}
Obviously if the binary file is fairly large, you should break it into many different messages and reassemble it on the other side, this would allow chat messages to make their way across the wire while the transfer is also still taking place.

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).

Asynchronous SSE Listener in Java Servlet

I am new here so forgive me if I am not familiar with standard operating procedure, but I have researched this topic at length and haven't found a lot of info.
I am trying to implement a client in a Java Http Servlet that can subscribe to a server-sent-event stream and parse data from that stream. Every time I have a client POST a request to my Http servlet, I need to pass on some data from that client to another server and then open an SSE listener, as that is how the other server will notify me it has data for me to hand back to the client.
It needs to be asynchronous and probably multi-threaded because I will have many requests from the client happening in a short time frame and I need to catch every event coming back from the server. The data I pass back from the server to the client can be large so I need threading so I don't miss new events coming in.
I am at a loss for where to start. I have tried implementing some of the example code using the Jersey SSE API (https://jersey.java.net/documentation/latest/sse.html) but when I implement their asynchronous SSE event handling example, the events coming in happen too fast for my handler to process all the data back to the client and the function gets called again from a new event before it finishes, or at least that's what seems to be happening.
Here is a synopsis of what I have written so far:
Client client = ClientBuilder.newBuilder().register(SseFeature.class).build();
WebTarget target = client.target("Target URL");
EventSource eventSource = new EventSource(target) {
#Override
public void onEvent(InboundEvent inboundEvent){
if ("in".equals(inboundEvent.getName())) {
//Check if the event is of the type we care about
//If it is, open an input stream to read the payload and store in a byte array via an HttpURLConnection object
//Open an output stream and stream the payload to a client via an HttpServletResponse Object - This never seems to happen
}
}
};
}
I know it's sloppy, I'm not as familiar with Java so I am just piecing things together so I apologize for that.
This gets called from within my servlet class but it never makes it to the point where I write to the output stream, I think because it's getting interrupted by another event coming in. If anyone has insight into how I can make this work, or another way to do it, I would greatly appreciate it. Thanks.
I recommend you the JEaSSE library (Java Easy Server-Sent Events): http://mvnrepository.com/artifact/info.macias/jeasse
You can find some usage examples here:
https://github.com/mariomac/jeasse

How do I communicate with all threads on a Multithreaded server?

Ok. I'm trying to grasp some multithreading Java concepts. I know how to set up a multiclient/server solution. The server will start a new thread for every connected client.
Conceptually like this...
The loop in Server.java:
while (true) {
Socket socket = serverSocket.accept();
System.out.println(socket.getInetAddress().getHostAddress() + " connected");
new ClientHandler(socket).start();
}
The ClientHandler.java loop is:
while(true)
{
try {
myString = (String) objectInputStream.readObject();
}
catch (ClassNotFoundException | IOException e) {
break;
}
System.out.println(myClientAddress + " sent " + myString);
try {
objectOutputStream.writeObject(someValueFromTheServer);
objectOutputStream.flush();
}
catch (IOException e) {
return;
}
}
This is just a concept to grasp the idea. Now, I want the server to be able to send the same object or data at the same time - to all clients.
So somehow I must get the Server to speak to every single thread. Let's say I want the server to generate random numbers with a certain time interval and send them to the clients.
Should I use properties in the Server that the threads can access? Is there a way to just call a method in the running threads from the main thread? I have no clue where to go from here.
Bonus question:
I have another problem too... Which might be hard to see in this code. But I want every client to be able to receive messages from the server AND send messages to the sever independently. Right now I can get the Client to stand and wait for my gui to give something to send. After sending, the Client will wait for the server to send something back that it will give to the gui. You can see that my ClientHandler has that problem too.
This means that while the Client is waiting for the server to send something it cannot send anything new to the server. Also, while the Client is waiting for the gui to give it something to send, it cannot receive from the server.
I have only made a server/client app that uses the server to process data it receives from the Client - and the it sends the processed data back.
Could anyone point me in any direction with this? I think I need help how to think conceptually there. Should I have two different ClientHandlers? One for the instream and one for the outstream? I fumbling in the dark here.
"Is there a way to just call a method in the running threads from the main thread?"
No.
One simple way to solve your problem would be to have the "server" thread send the broadcast to every client. Instead of simply creating new Client objects and letting them go (as in your example), it could keep all of the active Client objects in a collection. When it's time to send a broadcast message, it could iterate over all of the Client objects, and call a sendBroadcast() method on each one.
Of course, you would have to synchronize each client thread's use of a Client object outputStream with the server thread's use of the same stream. You also might have to deal with client connections that don't last forever (their Client objects must somehow be removed from the collection.)

Java TCP Server send more messages in one flush

using this code:
Java Server side:
...
out = new PrintWriter(this.client.getOutputStream(), true);
...
public void sendMsg(String msg) {
out.println(msg);
//out.flush(); // we don't flush manually because there is auto flush true
}
C# Client side:
while(connected) {
int lData = myStream.Read(myBuffer, 0, client.ReceiveBufferSize);
String myString = Encoding.UTF8.GetString(myBuffer);
myString = myString.Substring(0, lData);
myString = myString.Substring(0, myString.Length-2);
addToQueue(myString);
}
variable myString have many messages that server should send them one by one like
hello \r\t hello \r\t ...
they should come separately like
hello \r\t
hello \r\t ...
which means when i wait one by one they come instantly all of them in a row, how can i make it to send one by one in separate flush.
Note I send 30~ messages in a row in one second (1s), i want them separate.
TCP supports a stream of bytes. This means you have no control how the data arrives regardless of how you send it. (Other than it will comes as bytes) You should rethink your protocol if you depend on it coming in any particular manner.
You can reduce the amount of bunching of data but all this does is reduce latency at the cost of throughput and should never be relied upon. This can be reduce (but not eliminated) by turning off Nagle and reducing co-alessing setting in your TCP driver if you can change these.
i want them separate.
You can want it but TCP does not support messages as you would want them.
The solution in you case is for your reader to match your writers protocol. You send lines so you should read lines at a time, e.g. BufferedReader.readLine(), not blocks of whatever data happens to be in the buffer.

Categories

Resources