Erlang服务器,Java客户端 - TCP消息分裂? [英] Erlang server, Java client - TCP messages get split?

查看:194
本文介绍了Erlang服务器,Java客户端 - TCP消息分裂?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

正如标题所示,我有一个用Erlang编写的服务器,一个用Java编写的客户端,他们通过TCP进行通信。我遇到的问题是gen_tcp:recv显然不知道何时收到客户端的完整消息,因此在多个消息中分割。



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



代码



Erlang服务器



  -module(服务器)。 
-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 ) -
case gen_tcp:
{ok,Data}的recv(Socket,0) - >
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());
扫描仪sc = new扫描仪(System.in);

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



结果



客户端



 您好! 



服务器



 收到:H 
收到:el
收到:lo!

我一直在搜索,如果我理解正确,TCP不了解邮件的大小,你需要手动设置某种分隔符。



我不知道的是,如果我写客户端,消息似乎没有分裂Erlang代替如下:



Erlang客户端



  -module客户)。 
-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)。



结果



客户端



 您好! 



服务器



 收到:你好! 

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



另外,还有Erlang(聊天) )服务器示例在网络上,他们不做任何分隔符的东西,虽然这些都经常写在Erlang的两端。然而,他们似乎假设收到的消息就像发送消息一样。这是不好的做法,还是有一些关于消息长度的隐藏信息,当客户端和服务器都写入Erlang?



如果需要定界符检查,我很惊讶我找不到关于这个问题的很多信息。如何以实际的方式进行?



提前感谢!

解决方案

您需要在服务器和客户端之间定义一个协议,将TCP流分解成消息。 TCP流被分组,但不能保证这些匹配您的呼叫发送/写入或接收/读取。



一个简单而强大的解决方案是将所有消息长度。 Erlang可以透明地使用 {packet,1 | 2 | 4} 选项,其中前缀以1,2或4个字节编码。您将必须在Java端执行编码。如果您选择2或4个字节,请注意,长度应以big-endian格式编码,与 DataOutputStream.outputShort(int)使用的字节顺序相同。和 DataOutputStream.outputInt(int) java方法。



但是,从你的实现看来,你有一个隐式协议:您希望服务器分别处理每一行。



幸运的是,Erlang也透明地处理。你只需要通过 {packet,line} 选项。然而,您可能需要调整接收缓冲区,因为这个缓冲区将被截断的行长度。这可以通过 {recbuf,N} 选项来完成。



所以只需重新定义你的选项就可以做你想要的。

  -define(MAX_LINE_SIZE,512)。 
-define(TCP_OPTIONS,[list,{packet,line},{active,false},{reuseaddr,true},{recbuf,?MAX_LINE_SIZE}]
/ pre>

As the title says, I have a server written in Erlang, a client written in Java and they are communicating through TCP. The problem that I am facing is the fact that gen_tcp:recv apparently has no knowledge of when a "complete" message from the client has been received, and is therefore "splitting" it up in multiple messages.

This is an example of what I'm doing (Incomplete code, trying to keep it to only the relevant parts):

Code

Erlang server

-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 client

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

Result

Client

Hello!

Server

Received: H
Received: el
Received: lo!

I have been searching around and if I understand it correctly, TCP has no knowledge of the size of messages, and you need to manually set some kind of delimiter.

What I don't get though, is that the messages never seem to split up if I write the client in Erlang instead, like this:

Erlang client

-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).

Result

Client

Hello!

Server

Received: Hello!

This makes me wonder if it is something that can be fixed on the Java side? I have tried several combinations of different output streams, write methods and socket settings on the server side, but nothing solves the problem.

Also, there are loads of Erlang (chat) server examples around the net where they don't do any delimiter things, although those are often written in Erlang on both ends. Nevertheless, they seem to assume that the messages are received just like they are sent. Is that just bad practice, or is there some hidden information about message length when both the client and server are written in Erlang?

If delimiter checks are necessary, I am surprised I can't find much information on the subject. How can it be done in a practical way?

Thanks in advance!

解决方案

You need to define a protocol between your server and your client to split the TCP stream into messages. TCP stream is divided in packets, but there is no guarantee that these match your calls to send/write or recv/read.

A simple and robust solution is to prefix all messages with a length. Erlang can do this transparently with {packet, 1|2|4} option, where the prefix is encoded on 1, 2 or 4 bytes. You will have to perform the encoding on the Java side. If you opt for 2 or 4 bytes, please be aware that the length should be encoded in big-endian format, the same byte-order used by DataOutputStream.outputShort(int) and DataOutputStream.outputInt(int) java methods.

However, it seems from your implementations that you do have an implicit protocol: you want the server to process each line separately.

This is fortunately also handled transparently by Erlang. You simply need to pass {packet, line} option. You might need to adjust the receive buffer, however, as lines longer that this buffer will be truncated. This can be done with {recbuf, N} option.

So just redefining your options should do what you want.

-define(MAX_LINE_SIZE, 512).
-define(TCP_OPTIONS, [list, {packet, line}, {active, false}, {reuseaddr, true}, {recbuf, ?MAX_LINE_SIZE}].

这篇关于Erlang服务器,Java客户端 - TCP消息分裂?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆