gpt4 book ai didi

java - Erlang 服务器,Java 客户端 - TCP 消息被拆分?

转载 作者:可可西里 更新时间:2023-11-01 02:35:07 25 4
gpt4 key购买 nike

正如标题所说,我有一个用 Erlang 编写的服务器,一个用 Java 编写的客户端,它们通过 TCP 进行通信。我面临的问题是 gen_tcp:recv 显然不知道何时收到来自客户端的“完整”消息,因此将其“拆分”为多条消息。

这是我正在做的一个例子(不完整的代码,试图只保留相关部分):

代码

二郎服务器

-module(server).
-export([start/1]).

-define(TCP_OPTIONS, [list, {packet, 0}, {active, false}, {reuseaddr, true}].

start(Port) ->
{ok, ListenSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
accept(ListenSocket).

accept(ListenSocket) ->
{ok, Socket} = gen_tcp:accept(ListenSocket),
spawn(fun() -> loop(Socket) end),
accept(ListenSocket).

loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
io:format("Recieved: ~s~n", [Data]),
loop(Socket);
{error, closed} ->
ok
end.

Java 客户端

public class Client {
public static void main(String[] args) {
Socket connection = new Socket("localhost", Port);
DataOutputStream output = new DataOutputStream(connection.getOutputStream());
Scanner sc = new Scanner(System.in);

while(true) {
output.writeBytes(sc.nextLine());
}
}
}

结果

客户端

Hello!

服务器

Received: H
Received: el
Received: lo!

我一直在搜索,如果我理解正确的话,TCP 不知道消息的大小,你需要手动设置某种分隔符。

但我没有明白的是,如果我改为用 Erlang 编写客户端,消息似乎永远不会分开,如下所示:

Erlang 客户端

-module(client).
-export([start/1]).

start(Port) ->
{ok, Socket} = gen_tcp:connect({127,0,0,1}, Port, []),
loop(Socket).

loop(Socket) ->
gen_tcp:send(Socket, io:get_line("> ")),
loop(Socket).

结果

客户端

Hello!

服务器

Received: Hello!

这让我想知道它是否可以在 Java 端修复?我在服务器端尝试了几种不同的输出流、写入方法和套接字设置的组合,但没有解决问题。

另外,网络上有大量 Erlang(聊天)服务器示例,它们不做任何分隔符的事情,尽管这些通常在两端都是用 Erlang 编写的。然而,他们似乎假设消息的接收与发送一样。这只是不好的做法,还是当客户端和服务器都用 Erlang 编写时,是否存在一些关于消息长度的隐藏信息?

如果需要进行定界符检查,我很惊讶找不到关于该主题的太多信息。如何以实用的方式完成?

提前致谢!

最佳答案

This makes me wonder if it is something that can be fixed on the Java side?

不,绝对不是。不管您为什么没有碰巧看到 Erlang 客户端的问题,如果您没有在协议(protocol)中加入任何类型的“消息边界”指示,您将无法可靠地检测到整个消息。我强烈怀疑如果您使用 Erlang 客户端发送非常的消息,您仍然会看到拆分的消息。

你应该:

  • 使用某种“消息结束”序列,例如如果不会出现在您的消息中,则为 0 字节。
  • 在每条消息前加上消息的长度。

除此之外,您目前还不能清楚地区分字节和文本。例如,您的 Java 客户端当前静默忽略每个 char 的前 8 位。与其使用 DataOutputStream,我建议只使用 OutputStream,然后对每条消息:

  • 使用特定编码将其编码为字节数组,例如

    byte[] encodedText = text.getBytes(StandardCharsets.UTF_8);
  • 将长度前缀写入流(可能是 7 位编码整数,或者可能只是固定宽度,例如 4 字节)。 (实际上,坚持使用 DataOutputStream 会使更简单一些。)

  • 写入数据

在服务器端,您应该通过读取长度来“读取消息”,然后读取指定的字节数。

您无法回避 TCP 是基于流 的协议(protocol)这一事实。如果你想要一个基于消息的协议(protocol),你真的必须自己把它放在首位。 (当然,我确信有有用的库可以做到这一点——但你不应该把它留给 TCP 并寄希望于。)

关于java - Erlang 服务器,Java 客户端 - TCP 消息被拆分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23724112/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com