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);
}
Related
I was trying this code. it works fine but if I remove \n in String str it doesn't work I mean It was able to compile without \n but it didn't give me output.
public class Test {
// Use try-with-resources to close a socket.
public static void main(String args[]) throws Exception {
int c;
// Create a socket connected to internic.net, port 43. Manage this
// socket with a try-with-resources block.
try (Socket s = new Socket("whois.internic.net", 43)) {
// Obtain input and output streams.
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
// Construct a request string.
String str = (args.length == 0 ? "MHProfessional.com" : args[0]) + "\n"; // <- herer
// Convert to bytes.
byte buf[] = str.getBytes();
// Send request.
out.write(buf);
// Read and display response.
while ((c = in.read()) != -1) {
System.out.print((char) c);
}
}
// The socket is now closed.
}
}
The server you're talking to reads data up until an end-of-line (\n) character -- that's just the way it works, but it's far from unusual. It's possible that other end-of-line sequences will be accepted as well.
The server has to have some way to know the client has finished sending data. It will probably know if the client closes its connection, but by then it's too late to respond to the client.
I'm trying to read the data back from a server program that I didn't write. The server program doesn't send any kind of end of transmission character and it doesn't close the socket once it sends a response. There is a button I can press on the server to close the connection manually and if I leave a little bit of a timeout on the (android)client side so I have time to press it, I do get the data from the server into my client app. Otherwise it just eventually times out and I get no response. Since I can't control the protocol on the server side how can I close the connection and get the response from the server? Thanks for any help.
cSocket.setSoTimeout(timeOut);
cOut = new PrintWriter(cSocket.getOutputStream(), true);
cOut.println(msgIn);
cIn = new BufferedReader(new InputStreamReader(
cSocket.getInputStream()));
int intTest;
while ((intTest = cSocket.getInputStream().read()) != -1) {
System.out.println(cIn.readLine());
Message from the server looks like this:
char(60)...char(62)char(60)...char(62)char(60)...char(62)char(60)...char(62)
... is random data inside the chars doesn't duplicate.
*Final edit
Got it working like this:
int c;
int intCount = 0;
StringBuilder response= new StringBuilder();
while ((c = cIn.read()) != -1) {
response.append( (char)c ) ;
if (c == 62) {
intCount = intCount + 1;
}
if (intCount >=4) {
cSocket.close();
String result = response.toString();
System.out.println(result);
break;
}
}
while ((intTest = cSocket.getInputStream().read()) != -1) {
System.out.println(cIn.readLine());
This doesn't make any sense. Just because you read a byte doesn't mean you can read another whole line, and you're throwing away the byte you did read. It should be:
while ((intTest = cSocket.getInputStream().read()) != -1) {
System.out.println(intTest);
And intTest is a poor name for an input byte. There's no 'test' about it. I would call it i, or b.
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.
Using ServerSocket and BufferedReader.readLine() the input is only read once the line is terminated on the client side, is there anyway to read the input, and send an output before the line is terminated?
ServerSocket SRVSOCK = new ServerSocket(35553);
while(true){
Socket SOCK = SRVSOCK.accept();
InputStreamReader IR = new InputStreamReader(SOCK.getInputStream());
BufferedReader BR = new BufferedReader(IR);
String MESSAGE = BR.readLine();
PrintStream PS = new PrintStream(SOCK.getOutputStream());
System.out.println(MESSAGE);
PS.print("Returned Successfully");
}
You could go with bufferedReader.read() which will return the values one character at a time
StringBuffer response = "";
while ((c = bufferedReader.read()) != -1)
{
//Since c is an integer, cast it to a char. If it isn't -1, it will be in the correct range of char.
response.append( (char)c ) ;
}
If you go with this method, it's up to you to figure out when you can start parsing the result and send a reply.
Have a look at this answer.
You can read input into a buffer whose size is under your control.
(Apologies if linking to self answers is considered bad. Felt like reusing it. We are devs after all.)
I am having a field day trying to figure out why my Java code is not working.
I am supposed to receive the following messages from a C program via sockets.
1~message~i love you\r\n
2~message~do you love me?\r\n
3~message~when are we going to meet again?\r\n
4~message~How about now?\r\n
5~message~Oh! I'm pregnant!\r\n
Instead, I am receiving the following messages instead.
1~message~i love you\r\n
2~message~do you love me?\r\n
2~message~do you love me?\r\n
5~message~Oh! I'm pregnant!\r\n
I ran a port redirector on my laptop and found out that the C program is transmitting the messages correctly. It's my Java program that is not receiving them properly.
I am using Java NIO Channels to receive the messages.
My code as follows:
StringBuffer stringBuffer = new StringBuffer();
int pos = 0; // position of Buffer
// initialize server and client sockets
ServerSocketChannel serverChannel = null;
SocketChannel clientChannel = null;
// initialize ByteBuffer
ByteBuffer inBuffer = ByteBuffer.allocateDirect(65536);
inBuffer.order(ByteOrder.LITTLE_ENDIAN);
try {
serverChannel = ServerSocketChannel.open();
SocketAddress port = new InetSocketAddress(8080);
serverChannel.socket().bind(port);
while (true) {
clientChannel = serverChannel.accept();
while ((bytesRead = clientChannel.read(inBuffer)) != -1) {
inBuffer.flip();
while (inBuffer.get(pos) != '\r') {
stringBuffer.append((char) inBuffer.get(pos));
pos++;
} // end while loop checking for bytesRead
//increment over \r and \n
inBuffer.get();
bytesRead--;
inBuffer.get();
bytesRead--;
pos = pos + 2;
System.out.println(stringBuffer);
stringBuffer.setLength(0);
pos = 0;
} // close while reading bytesRead loop
} // close while(true) loop
} catch (IOException ex) {
}
I suggest you try plain IO which is simpler for this use case.
ServerSocket serverSocket = new ServerSocket(8080);
while(!serverSocket.isClosed()) {
Socket socket = serverSocket.accept();
System.out.println("Accepted socket "+socket);
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
StringBuilder sb = new StringBuilder();
int b;
while((b = bis.read())>=0) {
if (b == '\r')
b = bis.read();
if (b == '\n') {
System.out.println(sb);
sb.setLength(0);
} else {
sb.append((char) b);
}
}
System.out.println("Closing socket.");
socket.close();
}
Do you have to use NIO for this? It's generally a lot harder to get right (at least for me). My guess is that the problem is that you're always reading, but you're calling flip exactly once per read... whereas I'd expect to see two flips, or possibly a flip before the read and then a clear at the end of the processing.
Note that you're also completely ignoring bytesRead - why bother decrementing a variable which you're then reassigning without reading?
Furthermore, you're assuming you get exactly one \r from each read call. What if you receive two lines in one call, or one imcomplete line?
I'm not qualified to give the correct NIO code, but I'd recommend that you try to get it working with a plain InputStream first and then move onto NIO.
SocketChannel.read puts data into the buffer at the buffers current position. You're iterating the buffer from position 0 every time.
If you get two lines in one read you're only handling up to the first \r and then continuing by reading again. You may miss the final lines this way.