Java sending message between server and client without newline character - java

I have a client which is connecting to a server. The server and the client exchange datas in string format. The problem is that, the server does not take '\n' character at the end of the message and because of this the client blocked in readLine() method. Unfortunately the server-side can't be changed. How can read from stream that kind of message which does not have '\n' at the end?
My client code:
public class json
{
private static Socket socket;
public static void main(String args[])
{
String sendMessage = "";
Gson gson = new Gson();
JSON_package authentication = new JSON_package();
authentication.setType("Identifying");
authentication.setSource("exampleClient");
Package_Parser pp = new Package_Parser();
sendMessage = gson.toJson(authentication);
sendMessage = authentication.buildPackage(sendMessage);
try
{
String host = "host_address";
int port = port_number;
InetAddress address = InetAddress.getByName(host);
System.out.println("Connecting.");
socket = new Socket(address, port);
System.out.println("Connected.");
//Send the message to the server
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
bw.write(sendMessage);
bw.flush();
System.out.println("Message sent to the server : "+sendMessage);
//Get the return message from the server
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
StringBuffer buffer = new StringBuffer();
String message = br.readLine();
message = pp.Parser(message);
System.out.println("Message received from the server : " +message);
}
catch (Exception exception)
{
exception.printStackTrace();
}
finally
{
//Closing the socket
try
{
socket.close();
System.out.println("Closed.");
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}

You can try to use ready and read(char c) methods.
Here is one example:
StringBuffer sb = new StringBuffer();
while (br.ready()) {
char[] c = new char[] { 1024 };
br.read(c);
sb.append(c);
}

The easiest solution is to read the message character per character, but the main problem here is to know when the message is complete. In a line-oriented protocol this is simple, the newline that was sent is the "separator" between messages. Without, there are two possible situations where this problem is easy to solve:
Case 1: the message always has a fixed character at the end, that can't occur in the message
// let's pretend ! is the end of message marker
final char endMarker = '!';
// or of course StringBuffer if you need to be treadsafe
StringBuilder messageBuffer = new StringBuilder();
// reads to the end of the stream or till end of message
while((value = br.read()) != -1) {
char c = (char)value;
// end? jump out
if (c == endMarker) {
break;
}
// else, add to buffer
messageBuffer.append(c);
}
// message is complete!
String message = messageBuffer.toString();
Case 2: the message has a fixed length
// let's pretend message is always 80 long
int messageLength = 80;
StringBuilder messageBuffer = new StringBuilder();
int charactersRead = 0;
// reads to the end of the stream or till end of message
while((value = br.read()) != -1) {
char c = (char)value;
// end? jump out
if (++charactersRead >= messageLength) {
break;
}
// else, add to buffer
messageBuffer.append(c);
}
// message is complete!
String message = messageBuffer.toString();
In both cases you'll have to add some code to check the sanity of what you received, you may have received EOF during read.
If there is no obvious message separator and message have a variable length it will be a lot harder.

The point of readLine() is to read data where it really is guaranteed that the input will end with a newline. Generally, when parsing input, there has to be some token - some character or combination of characters in the input, which you can use to decide whether to
Wait for more input
Do something with the information you've gotten already
And possibly decide whether to go back to waiting for more input afterwards
If you cannot guarantee that a newline will be sent, then readLine() is the wrong tool for the job. Use something like the character-array read method of InputStreamReader instead. You will have to iterate the array of characters you read in, and figure out when you have enough input to work with. You could also use the one-character-at-a-time read() method of InputStreamReader which will result in simpler but probably less efficient code.
If you use the character-array version of read(), and if you go back to collecting input after parsing some, don't forget to put whatever is left over when you do get enough to parse back into the queue to handle on the next round.

Related

Accessing byte array values from socket inputStream

I am attempting to retrieve the byte values from an InputStream which is being sent to the socket. I have used many ways but it always prints me the address of the byte array instead of its contents.
Below is my code for Client and Server. When a packet is sent from the client to the server, the server instantiates a new Thread to handle the connection. So slaveSocket is the socket I want to use for this.
public class TCPClient {
public static void main(String[] args) throws IOException{
Socket socket;
String address;
int port;
String userInput;
String serverResponse;
PrintWriter out;
BufferedReader in;
//read characters from user
BufferedReader stdIn;
if (args.length != 2) {
System.err.println("Usage: java EchoClient <address> <port>");
System.exit(1);
}
byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
address = args[0];
port = Integer.parseInt(args[1]);
try{
//connect socket to server
socket = new Socket(address, port);
//Construct PrintWriter to write objects to the socket
out = new PrintWriter(socket.getOutputStream(), true);
//Construct BufferedReader to read input from the socket
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//Another reader to read characters typed by the user
stdIn = new BufferedReader(new InputStreamReader(System.in));
//scanner for menu option
Scanner scanner = new Scanner(System.in);
int menuOption;
System.out.println("Press 1 to read from file or 2 to write to file");
menuOption = scanner.nextInt();
if (menuOption == 1){
String filename = "";
String text = "";
System.out.println("Enter file name");
filename = scanner.next();
byte[] packet = new byte[512];
//Constructing the RRQ Packet
//Ading the OPCODE
packet[0] = 1;
//adding the filename
filename.getBytes(Charset.forName("UTF-8"));
byte[] filenameB = filename.getBytes(Charset.forName("UTF-8"));
System.arraycopy(filenameB,0,packet,1, filenameB.length);
//adding a 0
packet[filenameB.length +1] = 0;
//adding the mode
System.arraycopy(mode,0,packet,1+filenameB.length+1,mode.length);
//adding the last 0
packet[1+filenameB.length+1+mode.length+1] = 0;
out.println(packet);
}else if(menuOption == 2){
}
socket.close();
}catch(UnknownHostException e){
System.err.println("Dont know about host" + address);
System.exit(1);
}catch(IOException e){
System.err.println("Couldnt get I/O for the connection to " + address);
System.exit(1);
}
}
}
public class TCPServer {
public static void main(String[] args) throws IOException{
//port of the server
int port = 10000;
//Socket objects
ServerSocket masterSocket;
Socket slaveSocket;
//instantiate the server socket
masterSocket = new ServerSocket(port);
System.out.println("Server Started");
boolean flag1 = true;
while(true){
slaveSocket = masterSocket.accept();
System.out.println("Accepted TCP connection from: " +
slaveSocket.getInetAddress() + ", " + slaveSocket.getPort() + "...");
System.out.println("Initialising new Thread...");
new TCPServerThread(slaveSocket).start();
}
}
}
public class TCPServerThread extends Thread{
private Socket slaveSocket = null;
public TCPServerThread(Socket socket){
super("TCPServerThread");
this.slaveSocket = socket;
}
public void run(){
byte[] ClientPacket = new byte[512];
PrintWriter socketOutput;
InputStream socketInput;
try{
//send packet to client
socketOutput = new PrintWriter((slaveSocket.getOutputStream()), true);
//read packet from client
socketInput = new DataInputStream(slaveSocket.getInputStream());
ClientPacket = socketInput.readAllBytes();
System.out.println(new String(ClientPacket, StandardCharsets.UTF_8));
}catch (IOException e){
System.err.println(e);
}
}
}
You've hopelessly overengineered this.
Writer and Reader do character input and output. InputStream and OutputStream do byte input and output.
You turn byte-based stuff (and in the end, network ports are byte based, not character based) into character based stuff in dangerous ways and then are attempting to read and write bytes into and out of the char-based things.
The solution is simple. Just stop doing that. You have byte-based stuff, there is absolutely no need to involve Reader and Writer.
A bunch of lines that cause problems:
out.println(packet);
PrintStreams are debug aids. You can't use them for any of this. For example, this line will print newlines (definitely not something you'd want in a byte based stream system!), and will print 'objects' - it does that by invoking the .toString() method, and the toString method of arrays are mostly useless. That explains why you see what you see. This is not how you send bytes. You cannot send bytes to a PrintStream (which is a confused mess, as it tries to let you send characters to a byte based system. As I said, you use it for debugging and nothing else. You should not be using it here at all).
new InputStreamReader(socket.getInputStream())
This is dangerous. You're turning a byte based system (InputStream) into a char-based one (Reader) and this always means somebody is making an explicit, 'out of band' (not based on the data in that stream) decision about charset encoding. In this case, as per the docs of InputStreamReader, you get the 'platform default'. Starting with JDK18, it's guaranteed to be UTF-8 fortunately, but before that, who knows what it is. You never want to call this constructor to avoid the confusion. new InputStreamReader(socket.getInputStream, StandardCharsets.UTF_8).
Mostly, though, don't make a reader in the first place. You have no interest whatsoever in reading streams of characters, you just want bytes.
If you have smallish strings and the information about where they 'end' is done 'out of band' (example: The size in bytes (not characters) is sent first, then X bytes that are the string, UTF_8 encoded), you can just read that in as bytes, and then make a string off of that, bypassing any need for Readers and Writers. Reader and Writer is useful only if the entire stream is all character based, or if you have huge strings (hundreds of megabytes) where their end can only be surmised by interpreting the data as characters first. (Mostly, those are horrible protocols that shouldn't be used).
//Construct PrintWriter to write objects to the socket
No, you can't write objects to sockets. Objects aren't bytes. You can write bytes to a socket; some objects will let themselves be turned into bytestreams but this is decidedly not a trivial job, and PrintWriter can't do it at all.
catch (IOException e) { System.err.println(e);
Most code has no reasonable route to 'deal' with them, but the solution to that is to throw them onwards. Not to catch the exception, print a note of despair, and just keep going on like nothing happened. Doing it right is also less code, so, win-win.
stdIn = new BufferedReader(new InputStreamReader(System.in));
//scanner for menu option
Scanner scanner = new Scanner(System.in);
You're making 2 different ways to read standard input. That makes no sense. Pick one.
I tried to fix it for you:
public class TCPClient {
public static void main(String[] args) throws Exception { // always throw Exception from `main`.
if (args.length != 2) {
System.err.println("Usage: java EchoClient <address> <port>");
System.exit(1);
return; // Always return after System.exit.
}
byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
String address = args[0];
int port = Integer.parseInt(args[1]);
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter("\\R"); // split on newlines, not spaces. So much more logical.
// resources need to be safe-closed - use try-with!
try (var socket = new Socket(address, port);
var out = new BufferedOutputStream(socket.getOutputStream());
var in = socket.getInputStream()) {
System.out.println("Press 1 to read from file or 2 to write to file");
int menuOption = scanner.nextInt();
if (menuOption == 1) {
System.out.println("Enter file name");
String filename = scanner.next();
//Constructing the RRQ Packet
//Adding the OPCODE
out.write(1);
out.write(filename.getBytes(StandardCharsets.UTF_8));
out.write(0);
// The above is dangerous; NUL (0) is actually a valid char.
// A proper way to send strings is to send length in bytes
// first. I'll leave it to you to fix your protocol.
// If it can't be fixed, scan for `\0` chars and get rid of em.
//adding the mode
out.write(mode);
out.write(0);
}else if (menuOption == 2) {
}
}
}
Sending bytes one at a time can be slow (as it ends up sending an entire packet) but can also be useful - the data is just sent, instead of waiting perhaps for a long time for more data. In your case, you send it all in one go, so sending it all off very quickly is not a good idea. Hence, why the outputstream is wrapped in a BufferedOutputStream, which fixes that. You can always use flush() to force sending now, in case you want to keep the connection open (close(), naturally, also flushes).
It's fine if you want to use a byte[] packet instead, but it seems convoluted and unneccessary here. out.write(someByteArray), where out is an OutputStream of some sort, works fine. out.println(byteArray), where out is a Writer of some sort, or a PrintStream - doesn't work at all. (It would take the array, call toString() on it which isn't useful, then convert those bytes using some unknown charset and send that, and none of that is what you want).
You'll need to similarly eliminate PrintStream and the like from your server code.

Java: client socket does not read the second line and remains open after the terminator line

Having read tens of examples online, I am still stuck with the problem.
I am sending a message from my client in Java to a server in C++. After receiving the hand-shake message, the server sends back the following data:
"0000:1111:2222:3333:4444
END_CONNECT_DATA"
As soon as the last line (terminator) is read by the client, it should close the connection.
This is how I do it:
Socket socket = null;
String terminator = "END_CONNECT_DATA";
try
{
int serverPort = 7767;
String ip = "192.168.1.10";
String messageOut = "HAND-SHAKE MESSAGE";
socket = new Socket(ip, serverPort);
DataInputStream input = new DataInputStream( socket.getInputStream());
DataOutputStream output = new DataOutputStream( socket.getOutputStream());
//Send message
output.writeBytes(messageOut);
//Read Response
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuffer sb = new StringBuffer();
String s = "";
while((s = br.readLine()) != null)
{
System.out.println("CHECK !!!");
System.out.println(s);
sb.append(s);
if(s.contains(terminator))
{
System.out.println("CHECK TERMINATOR");
break;
}
}
socket.close();
String data = sb.toString();
System.out.println("FULL DATA:\n");
System.out.println(data);
}
catch (UnknownHostException e)
{
System.out.println("Sock:"+e.getMessage());
}
catch (EOFException e)
{
System.out.println("EOF:"+e.getMessage());
}
catch (IOException e)
{
System.out.println("IO:"+e.getMessage());
}
finally
{
if(socket!=null)
{
try
{
socket.close();
}
catch (IOException e)
{
}
}
}
}
What I get back from the server is only the first line. The cursor goes to the next line and continues blinking. The socket connection is not closed. Looks like the client is not reading the terminator (the second line of the message) at all.
Any ideas?
Thanks a lot!
As documented, the loop fails to read in the second line as it's not terminated with \r or \n. Therefore, returning only the result up till then, which is the first line as described.
You'll need to either add in a \r or \n right after the terminator or use BufferedReader.read() instead and check manually or adopt another strategy to read in the message
Clearly the peer is neither sending a line terminator after the last line nor closing the socket. Ergo using readLine() to read those messages is not correct. If you can adjust the peer, do so.

Java sockets inputstreams outputstreams

I have server:
StringBuilder sb = new StringBuilder();
try {
ServerSocket ses = new ServerSocket(7841);
Socket s=ses.accept();
Reader br = new InputStreamReader(s.getInputStream());
OutputStream os = s.getOutputStream();
os.write("string from system".getBytes());
os.flush();
char[] request = new char[6];
int count = br.read(request);
while (count!=-1) {
sb.append(new String(request, 0, count));
count = br.read(request);
System.out.println(count);
System.out.println(sb);
}
System.out.println("111"+count);
System.out.println(sb);
}
catch (IOException e) {e.printStackTrace();}
System.out.println(sb);
and client :
StringBuilder sb = new StringBuilder();
try {
Socket s = new Socket("127.0.0.1",7839);
Reader br = new InputStreamReader(s.getInputStream());
OutputStream os = s.getOutputStream();
os.write("string from project".getBytes());
os.flush();
char[] request = new char[6];
int count = br.read(request);
while (count!=-1) {
sb.append(new String(request, 0 , count));
// if (sb.toString().endsWith("</family>")) {
// break;
// }
count = br.read(request);
System.out.println(count);
System.out.println(sb);
}
System.out.println("111"+count);
System.out.println(sb);
}
catch (IOException e) {e.printStackTrace();}
System.out.println(sb);
The problem is I can not read the string till the end. For example, the server read only "string from projec".
Also the code System.out.println("111"+count); is not reached neither in client nor in server. What is wrong with the program? Thank you very much for any ideas.
Your program is fine, the only problem is your Server listening on port 7841, but your client try to connect on the port 7839, they must connect to the same port.
I think you need to figure out the protocol things like:
Server starts to listen, there is no client yet, the server is waiting on method accept();
When your client get connected, you must decide: who will first talk, who will response, what sign is beginning of the conversation and what sign is the end of the conversation, after this both the client and server can close this connection properly. Refer to this link https://docs.oracle.com/javase/tutorial/networking/sockets/clientServer.html
If you don't like the request-response mode, you can read and write simultaneously, then maybe the thread approach is better, use two threads to separate reading and writing process, so the read process will not block the write process.
Back to your question, the read process blocks program in while loop, so the line System.out.print(...) can not be reached.

Java socket timing out: Broken pipe

I'm writing a simple server in Java, and I'm able to retrieve incoming data from the client on the server side, but not on the client side due to a 2000ms timeout. Anyone know why this times out?
This is the server's code:
private static void listen() throws IOException {
while(true) {
Socket clientSocket = serverSocket.accept();
StringBuilder bufferedStringInput = new StringBuilder();
CharBuffer cbuf = CharBuffer.allocate(4096);
try {
InputStream is = clientSocket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF8"));
int noCharsLeft = 0;
while ((noCharsLeft = br.read(cbuf)) != -1) {
char[] arr = new char[noCharsLeft];
cbuf.rewind();
cbuf.get(arr);
bufferedStringInput.append(arr);
cbuf.clear();
}
System.out.println(bufferedStringInput.toString());
} catch (IOException e) {
System.out.println("Error received client data: " + e.getMessage());
}
String message = "Hello client";
try {
PrintWriter out = new PrintWriter(clientSocket.getOutputStream());
out.print(message);
} catch (IOException e) {
System.out.println("Error getting output stream from client: " + e.getMessage());
}
clientSocket.close();
}
}
You're reading the input until end of stream, which only happens when the peer closes the connection, then you're trying to write to it, so of course you get a broken pipe. Doesn't make sense. You should just read the input until you have one entire request, whatever that means in your protocol.
There are other problems lurking here:
If the client code uses readLine(), you're not sending a line terminator: use println(), not print(), and close the PrintWriter, not just the client socket.
cbuf.rewind()/get()/clear() should be cbuf.flip()/get()/compact().
But it would make more sense to read directly into a char[] cbuf = new char[8192]; array, then bufferedStringInput.append(cbuf, 0, noCharsLeft), and forget about the CharBuffer altogether. Too much data copying at present.
noCharsLeft is a poor name for that variable. It is a read count.

How to read java stream until certain byte is reached

My question is similar to this post. But I don't send packet length rather a 0 byte at end.
Most efficient way to read in a tcp stream in Java
So I'm wondering how would I code something that would.
At the moment I just use
this.socketIn = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
String line = this.socketIn.readLine();
If packet is getting sent while you are spamming the packet it's going to count the packet which hasn't arrived yet as a fully read Line, yet it's incomplete and messes up the whole protocol.
In my protocol each packet is ended with a 0 byte (0x00) to determine the end of a single packet if in case packets end up merged/stacked together.
So what I'm trying to do really is keep reading the socket stream until a 0x00 is reached to indicate the packet is fully crafted and ready for processing.. and of course some kind of security (a timeout is best I believe) to determine the packet is junk as it's not ended in a 0 byte in a specific time frame lets say 5 seconds.
How would I go about doing this?
P.S>
I'm not using NIO framework but just a regular thread per connection socket and I don't want to switch to NIO as it's very difficult to inject data with a completely different global thread that processes updates and sends specific updates to random users (not broadcast).
Here is what I tried so far.
String line = "";
int read;
long timeOut = System.currentTimeMillis();
while(true) {
read = this.socketIn.read();
if (read == -1 || read == 0 || (System.currentTimeMillis()-timeOut) > 5000)
break;
line += read
}
Here's a sketch using setSocketTimeout to deal with the "slow client / denial of service" scenario.
this.socket.setSoTimeout(5000);
BufferedReader br = new BufferedReader(
new InputStreamReader(this.socket.getInputStream()));
StringBuilder sb = new StringBuilder();
try {
int ch;
while ((ch = br.read()) != -1) {
if (ch == 0) {
String message = sb.toString();
// process message
sb.setLength(0);
} else {
sb.append((char) ch);
}
}
} catch (InterruptedIOException ex) {
System.err.println("timeout!");
...
} finally {
br.close();
}
I think it is also possible to implement a (brutal) socket timeout by creating a second thread that calls socket.close() on the socket object if it detects that the reading thread is not getting any data. But that's a heavyweight approach, given the simpler setSoTimeout() approach.
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
BufferedReader in = new BufferedReader(isr);
String line = "";
String response = "";
while ((line = in.readLine()) != null) {
System.out.println(line);
response = response + line + "\n";
if (in.ready() == false) {
break;
}
}
The trick is the ready function that belongs to the BufferedReader. You need to check if it's ready, if not just get out of the loop.
Should I use StringBuilder? or build my own as EJP wrote. which is faster?
String line = "";
int read;
//long timeOut = System.currentTimeMillis();
this.socket.setSoTimeout(5000);
while(this.socket.isConnected()) {
read = this.socketIn.read();
if (read == -1)
throw new IOException("Insufficient data / timeout)");
else if(read == 0)
break;
line += (char)(read & 0xFF);
}

Categories

Resources