I want to send multiple packets separately with using tcp socket in Java. Here's my code.
try {
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
String[] array = new String[4];
array[0] = "stack";
array[1] = "over";
array[2] = "flow";
array[3] = "coding";
for (int i = 0; i < array.length; i ) {
out.write(array[i].getBytes()); //send packet
}
} catch (IOException e) {
throw new RuntimeException(e);
}
I take all data in one packet right now. Here is the received packet's terminal output:
Incoming Transmission => stackoverflowcoding
That's what i want:
Incoming Transmission => stack
Incoming Transmission => over
Incoming Transmission => flow
Incoming Transmission => coding
How can i receive data as 4 packets separately?
Why do i want this ? Because in my client there's an event that listens the coming tcp packages. This event must be triggered for each elements of array separately.
CodePudding user response:
TCP logically represents a continuous stream, similar to a file. Think of it as one continuous sequence of bytes until at some point in time it's closed and the stream ends. But there's no definite clear boundary between any two of those bytes. Sometimes you might have to wait to get more bytes, but there's no intrinsic way to tell if that's because the other side stopped sending or if there's some kind of network issue between the two of you.
While packets are used as an underlying mechanism you shouldn't rely on their separation, because they could in theory be split and merged along the way (realistically they are mostly just split and rarely merged).
The usual solution is to use some kind of protocol on top of TCP to clearly mark the different chunks you're intrested in. The simplest such protocol would simply start by sending the amount of bytes the next chunk will be long, followed with whatever the data is.
Alternatively, you could switch to UDP, which is actually packet-based and guarantees that if you receive something it'll be a single packet from the other side (though it doesn't guarantee order of packets or even their delivery).
You might be able to make this work by strategically placing out.flush()
in your code, but depending on that will make your code very fragile.
CodePudding user response:
TCP is a stream of bytes, it has no concept of messages. You have to explicitly mark where one data ends and the next begins. For example, by sending the data's length before the actual data, eg:
try {
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
String[] array = new String[4];
array[0] = "stack";
array[1] = "over";
array[2] = "flow";
array[3] = "coding";
out.writeInt(array.length);
for (int i = 0; i < array.length; i ) {
byte[] bytes = array[i].getBytes(StandardCharsets.UTF_8);
out.writeInt(bytes.length);
out.write(bytes, 0, bytes.length);
}
out.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
This way, the receiver can first read an integer to know how many strings are being sent, then run a loop, where each iteration reads an integer for the string length then reads the bytes for that string, eg:
try {
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
int count = in.readInt();
for (int i = 0; i < count; i ) {
int length = in.readInt();
byte[] bytes = new byte[length];
in.readFully(bytes);
String s = new String(bytes, StandardCharsets.UTF_8);
System.out.println("Incoming Transmission => " s);
}
} catch (IOException e) {
throw new RuntimeException(e);
}