Java通过TCP Socket单独发送数据



我想在Java中使用tcp套接字分别发送多个数据包。这是我的代码。

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);
}

我现在把所有的数据放在一个包里。以下是接收到的数据包的终端输出:

Incoming Transmission => stackoverflowcoding

这就是我想要的:

Incoming Transmission => stack
Incoming Transmission => over
Incoming Transmission => flow
Incoming Transmission => coding 

如何分别接收4个数据包的数据?

为什么我想要这个?因为在我的客户端中有一个监听即将到来的tcp包的事件。此事件必须为数组的每个元素单独触发。

TCP在逻辑上表示连续流,类似于文件。把它想象成一个连续的字节序列,直到在某个时间点它关闭,流结束。但是在任何两个字节之间没有明确的边界。有时候你可能不得不等待获得更多的字节,但没有内在的方法来判断这是因为另一方停止发送还是你们之间存在某种网络问题。

当包被用作底层机制时,你不应该依赖于它们的分离,因为理论上它们可以被拆分和合并(实际上它们大多只是拆分而很少合并)。

通常的解决方案是在TCP之上使用某种协议来清楚地标记您感兴趣的不同块。这样的协议最简单的方式就是发送下一个数据块的字节数,然后再发送数据。

或者,你可以切换到UDP,它实际上是基于数据包的,并保证如果你收到一些东西,它将是来自另一边的单个数据包(尽管它不保证数据包的顺序甚至它们的传递)。

也许可以通过在你的代码中策略性地放置out.flush()来使这个工作,但是依赖它会使你的代码非常脆弱。

TCP是一个字节流,它没有消息边界的概念(例如,像UDP一样)。在发送和读取之间没有一对一的关系。发送的数据可以在网络认为有效传输所必需的任何数据包中进行连接和分割。TCP为您提供的唯一保证是:

  • 数据将被传送(除非连接被关闭或丢失)
  • 数据将以发送的顺序接收

因此,要完成您所要求的,您必须显式标记一个数据结束和下一个数据开始的位置。例如,通过在实际数据之前发送数据的长度,例如:

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);
}

这样,接收者可以首先读取一个整数来知道发送了多少个字符串,然后运行一个循环,其中每次迭代读取字符串长度的整数,然后读取该字符串的字节,例如:

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);
}

最新更新