Java SocketChannel read the messages of unknown length - java

SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int numRead = -1;
try {
numRead = channel.read(buffer);
System.out.println("numRead: " + numRead);
}
catch (IOException e) { e.printStackTrace();}
if (numRead == -1) {
this.dataMap.remove(channel);
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
System.out.println("Connection closed by client: " + remoteAddr);
channel.close();
key.cancel();
return;
}
System.out.println("Got: " + new String(buffer.array(), "windows-1251"));
From the socket reads 1024 bytes of data. In this case all messages are combined, and the last message does not come fully. How do I read the data into a buffer before the message separator '|' ? I want to receive each message individually.

Depends on the protocol, but if the protocol is based on a message separator character, your only option is to read as much as you can without blocking, then scan through the content you read to find the separator. You need to do this in a loop until the connection is closed (or until your side decides to close the connection).
Of course you can read multiple messages at once and even partial messages, so you have to make sure you handle those cases adequately.

There is no such thing as a message in TCP. It is a byte-stream API. If you want messages you must implement them yourself, via a separator, STX/ETX pair, length word prefix, self-describing protocol, etc.

Related

Socket Java to C++ - Messages are always 8192 characters long

I am implementing a TCP socket connection from a java to a C++ programm. Currently it's one way but should become two-way someday. My messages are pretty long (~100.000 characters). Somehow my application only sends 8192 characters/bytes at once. How can that be? Is there any tool that can help debugging? Both, client and server, run on a local windows machine. I am not familar with network programming, so any help is appreciated! Thanks alot in advance!
Here is my code so far:
JAVA:
make a connection:
try {
serverSocket = new ServerSocket(socketPort);
System.out.println("waiting for client ...");
while (true) {
clientSocket = serverSocket.accept();
System.out.println("client connected.");
if (clientSocket!=null) break;
}
}
catch (IOException e) {
System.out.println(e);
}
send stuff:
OutputStream out = clientSocket.getOutputStream();
BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream()));
//send the new data to client
PrintWriter pw = new PrintWriter(out, true);
String outString = "VERY LONG TEXT ENDING WITH SPECIAL LETTER LIKE $";
pw.println(outString);
C++:
make a connection
bool connectToHost(int PortNo, char* IPAddress)
{
//Start up Winsock…
WSADATA wsadata;
int error = WSAStartup(0x0202, &wsadata);
//Did something happen?
if (error)
return false;
//Did we get the right Winsock version?
if (wsadata.wVersion != 0x0202)
{
WSACleanup(); //Clean up Winsock
return false;
}
//Fill out the information needed to initialize a socket…
SOCKADDR_IN target; //Socket address information
target.sin_family = AF_INET; // address family Internet
target.sin_port = htons (PortNo); //Port to connect on
target.sin_addr.s_addr = inet_addr (IPAddress); //Target IP
mSocket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); //Create socket
if (mSocket == INVALID_SOCKET)
{
return false; //Couldn't create the socket
}
//Try connecting...
if (connect(mSocket, (SOCKADDR *)&target, sizeof(target)) == SOCKET_ERROR)
{
return false; //Couldn't connect
}
else
return true; //Success
}
receive stuff:
if (connectToHost(3141, "127.0.0.1"))
{
int iResult;
char recvbuf[DEFAULT_BUFLEN]; // DEFAULT_BUFLEN = 1000000
// Receive until the peer closes the connection
do {
iResult = recv(mSocket, recvbuf, DEFAULT_BUFLEN, 0); // DEFAULT_BUFLEN = 1000000
if ( iResult > 0 )
{
std::cout<<"recvbuf: "<< recvbuf[strlen(recvbuf)-1]<<""<< std::endl; //not the last character that I sent, but supposed to be
std::cout<<"recvbuf size: "<< iResult <<""<< std::endl; //fist are 8192 and then add up until sent size
}
else if ( iResult == 0 )
printf("Connection closed\n");
else
printf("recv failed with error: %d\n", WSAGetLastError());
} while( iResult > 0 );
} else
{
printf("connect failed\n");
}
TCP sockets work on a byte stream concept. The TCP socket ensures your data arrives without error and in order as a byte stream. The sender adds bytes to the TCP byte stream, and the socket takes care of sending them to the destination. The socket does not separate your logical messages; it is your responsibility to insert separators for any logical messages that are embedded in the byte stream. The TCP socket does not necessarily send a packet on the socket every time you write bytes to the socket. This is to increase the efficiency, as measured by the number of data bytes versus the total bytes sent (data + overhead). You can read about Nagle's algorithm for TCP.
When reading from a socket, you are again consuming the byte stream. The number of times you need to call receive may not match the number of times send was called. But you know the correct bytes will be delivered in order, and the number of these bytes will be the same as those sent.
The size 8192 is probably the buffer size that triggers sending a packet.
If you send only 1 byte, then flush the socket, you should see only the one byte on the receiving end. You can also disable Nagle's algorithm by setting TCP_NODELAY in the java socket options.
You can't receive more at a time than was in the socket receive buffer. Raise the socket receive buffer size from 8192, with setsockopt(), and do the same at the sending end for the socket send buffer.

In JAVA nio selector, when should I register the 'write operation'?

I am learning Java nio selector. In my understanding, I thought the steps using selector is to firstly I register the my interest operations and then I can check the ready set and finally I can do the operations corresponding my interest operations. I don't know why in this code the writing process can happen in the block of if (key.isReadable()){...} but not if (key.isWritable){...} and why writing operation is not registered?
Iterator keys = sel.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey)keys.next();
if (!key.isValid())
continue;
if (key.isAcceptable()) {
// increase the counter
connection++;
// remove accept request
keys.remove();
// ACCEPT: get the server channel
ServerSocketChannel ssc =
(ServerSocketChannel) key.channel();
// init a socket for a client
SocketChannel nsc = ssc.accept();
nsc.configureBlocking(false);
// register the socket for READ
nsc.register(sel, SelectionKey.OP_READ);
}
}
while (count < COUNT_MAX + NUM_CHILD - 1) {
sel.select();
// Get all pending events and iterate
Iterator keys = sel.selectedKeys().iterator();
while ( keys.hasNext() ) {
SelectionKey key = (SelectionKey)keys.next();
keys.remove();
if (!key.isValid())
continue;
if (key.isReadable()) {
// READ: get the channel
SocketChannel nsc = (SocketChannel) key.channel();
// clear buffer for reading
readBuffer.clear();
int nBytes = nsc.read(readBuffer);
if (nBytes == -1) {// Check if the client closed the socket
key.channel().close();
key.cancel();
continue;
}
// Read a message
DataInputStream ist = new DataInputStream (
new ByteArrayInputStream(readBuffer.array()));
String msg = ist.readUTF();
System.out.print(msg + "\n");
// Clear the write buffer
writeBuffer.clear();
// Write the counter value on the buffer
count++;
if (count < COUNT_MAX)
writeBuffer.putInt(count);
else
writeBuffer.putInt(-1);
// flip the buffer and write on the channel
writeBuffer.flip();
// Reply to a client
nsc.write(writeBuffer);
}
} // while (key)
You don't need to register interest in OP_WRITE because usually the channel is ready for writing. However a WritableChannel, if in non blocking mode, might not succeed in writing all content of the given ByteBuffer. See here in its java docs:
Some types of channels,
depending upon their state, may write only some of the bytes or
possibly none at all. A socket channel in non-blocking mode, for
example, cannot write any more bytes than are free in the socket's
output buffer.
In this case you need to register the interest for OP_WRITE on the selector to be notified when the channel is once again ready for writing, so you can finish writing your ByteBuffer.
See here a related SO question.

Getting end of Inputstream in java

following is my client side code , that retrieves text from server and prints.
Socket socket = new Socket(ip, port);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
String string = "Hello!\n";
byte buffer[] = string.getBytes();
out.write(buffer);
while ((character = in.read()) != -1){
System.out.print((char) character);
}
I am getting the the correct values from the server , but it is happening again and again , how can i find out the length of the value sent.
-1 denotes the end of the stream, and is received when the connection is closed. If you want to keep the connection open and send multiple messages, you need some sort of protocol (kind of like agreement between both ends) that tells where the message ends. The are many ways to do this, but in your example you're writing a line terminator (\n) to the end of the message, so you could check for that at the other end. Another way is to write the amount of bytes you're about to send before the actual message contents.

Datagram Channel Sockets not writing Java

Simply said, I'm able to receive data from the connected socket, until about 10 seconds after it starts. The solution to fix this is, to send data to the 'client'(an ARDrone) to keep the data stream going, otherwise it will stop sending data to the phone. However, my current code for some reason only writes data to the client (the ARDrone) on first connect and never after. I need it to keep sending data to the ARDrone even after the socket is communicating.
I've tried moving around the channel.register() calls, but nothing I've done seems to send data to the ARDrone, as I need.
Creating the connection:
channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new InetSocketAddress(video_port));
channel.connect(new InetSocketAddress(drone_addr, video_port));
selector = Selector.open();
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
Here is the current send/receive data function.
public void run() {
try {
ByteBuffer inbuf = ByteBuffer.allocate(BUFSIZE);
done = false;
while (!done) {
selector.select();
if (done) {
disconnect();
break;
}
Set readyKeys = selector.selectedKeys();
Iterator iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isWritable()) {
byte[] trigger_bytes = { 0x01, 0x00, 0x00, 0x00 };
ByteBuffer trigger_buf = ByteBuffer.allocate(trigger_bytes.length);
trigger_buf.put(trigger_bytes);
trigger_buf.flip();
channel.write(trigger_buf);
channel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
inbuf.clear();
int len = channel.read(inbuf);
if (len > 0) {
inbuf.flip();
final BufferedVideoImage vi = new BufferedVideoImage();;
vi.addImageStream(inbuf);
drone.videoFrameReceived(0, 0, vi.getWidth(), vi.getHeight(), vi.getJavaPixelData(), 0, vi.getWidth());
}
}
}
} catch (Exception e) {
drone.changeToErrorState(e);
}
}
I believe you corrupt your event interest on the first write with this line:
channel.register(selector, SelectionKey.OP_READ);
Per documentation, this resets it to OP_READ only.
Edit 0:
Per your comment - yes, I think you should remove that line entirely, and do not treat read and write cases as alternatives. In general a socket could be readable and writable at the same time. So what's happening now is that the write case prevents the read case since UDP socket is always writable (versus TCP that buffers outgoing data).
So my suggestion is to not include OP_WRITE into that event set at all, but handle writing in some other fashion, say on a timer, or after each read, or whatever makes sense to your app.
Hope this helps.

Reading Java sockets - either receiving duplicate lines or loss

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.

Categories

Resources