使用 gen_tcp 的 Erlang 客户端-服务器示例未收到任何信息

马可塔

我试图在客户端接收数据,但什么也没收到。

发送消息的服务器代码

client(Socket, Server) ->
  gen_tcp:send(Socket,"Please enter your name"),
  io:format("Sent confirmation"),
  {ok, N} = gen_tcp:recv(Socket,0),
  case string:tokens(N,"\r\n") of
    [Name] ->
      Client = #client{socket=Socket, name=Name, pid=self()},
      Server ! {'new client', Client},
      client_loop(Client, Server)
  end.

应该接收并打印出来的客户端

client(Port)->
   {ok, Sock} = gen_tcp:connect("localhost",Port,[{active,false},{packet,2}]),
   A = gen_tcp:recv(Sock,0),
   A.
7stud

我认为您的客户有问题,因为它指定:

{packet, 2}

但服务器指定(在未显示的代码中):

{packet, 0}

Programming Erlang (2nd)第。269 它说:

请注意,客户端和服务器使用的数据包参数必须一致如果服务器是用{packet,2}和客户端打开的{packet,4},那么什么都不会工作。

以下客户端可以成功接收来自服务器的文本:

%%=== Server:  {active,false}, {packet,0} ====

client(Port) ->
    {ok, Socket} = gen_tcp:connect(
        localhost, 
        Port, 
        [{active,false},{packet,0}]
     ),

    {ok, Chunk} = gen_tcp:recv(Socket, 0),
    io:format("Client received: ~s", [Chunk]),
    timer:sleep(1000),

    Name = "Marko",
    io:format("Client sending: ~s~n", [Name]),
    gen_tcp:send(Socket, Name),

    loop(Socket).

loop(Socket) ->
    {ok, Chunk} = gen_tcp:recv(Socket, 0),
    io:format("Client received: ~s~n", [Chunk]),
    loop(Socket).

但是,我认为聊天服务器和我的客户都有严重的问题当您通过 TCP(或 UDP)连接发送消息时,您必须假设该消息将被拆分为不确定数量的块——每个块具有任意长度。{packet,0}指定时,我认为recv(Socket, 0)只会从套接字读取一个块,然后返回。该块可能是整个消息,也可能只是消息的一部分。为了保证您已经从套接字读取了整个消息,我认为您必须遍历 recv():

get_msg(Socket, Chunks) ->
    Chunk = gen_tcp:recv(Socket, 0),
    get_msg(Socket, [Chunk|Chunks]).

那么问题就变成了:你怎么知道你什么时候读了整条消息,这样你就可以结束循环了?{packet,0}告诉 Erlang 不要在消息前添加长度头,那么你怎么知道消息的结尾在哪里呢?是否有更多的块来了,或者 recv() 是否已经读取了最后一个块?我认为消息结束的标记是另一方关闭套接字的时间:

get_msg(Socket, Chunks) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Chunk} ->
            get_msg(Socket, [Chunk|Chunks]);
        {error, closed} ->
            lists:reverse(Chunks);
        {error, Other} ->
            Other
    end.

但这引发了另一个问题:如果聊天服务器在 recv() 上循环等待来自客户端的消息,并且在客户端向服务器发送消息后,客户端在 recv() 上循环等待来自服务器的消息,并且双方都需要另一方关闭套接字以摆脱他们的 recv() 循环,那么您将陷入僵局,因为双方都没有关闭他们的套接字。因此,客户端将不得不关闭套接字,以便聊天服务器跳出其 recv() 循环并处理消息。但是,由于客户端关闭了套接字,因此服务器无法将任何内容发送回客户端。结果,我不知道你是否可以在{packet,0}指定时进行双向通信

这里是我约的结论{packet, N},并{active, true|false}从阅读文档和周围搜索:


发送()

当您调用 时send(),实际上没有数据*传输到目的地。相反,send()阻塞直到目标调用recv(),然后才将数据传输到目标。

* 在“Programming Erlang (2nd)”中,第 10 页。176 它说,send()由于操作系统缓冲数据的方式,当您调用时,少量数据将被推送到目标,然后send()将阻塞,直到recv()将数据拉到目标。


默认选项

您可以通过为其选项指定一个空列表来获取套接字的默认值,然后执行以下操作:

Defaults = inet:getopts(Socket, [mode, active, packet]),
io:format("Default options: ~w~n", [Defaults]).

--output:--
Default options: {ok,[{mode,list},{active,true},{packet,0}]}

您可以使用 inet:getopts() 来显示gen_tcp:accept(Socket)返回具有与 Socket 相同选项的套接字。


                    {active, true}   {active,false}
                   +--------------+----------------+
{packet, 1|2|4}:   |    receive   |     recv()     |
                   |    no loop   |     no loop    |
                   +--------------+----------------+
{packet, 0|raw}:   |    receive   |     recv()     |
  (equivalent)     |    loop      |     loop       |
                   +--------------+----------------+     

{主动,假}

邮件不会进入邮箱。此选项用于防止客户端用消息淹没服务器的邮箱。不要尝试使用接收块从邮箱中提取“tcp”消息——不会有任何消息。当一个进程想要读取消息时,该进程需要通过调用直接从套接字读取消息recv()

{数据包,1|2|4}

数据包元组指定每一方希望​​消息符合协议{packet, 2}指定每条消息前面有两个字节,其中包含消息的长度。这样,消息的接收者就知道要从字节流中读取多长时间才能到达消息的末尾。当您通过 TCP 连接发送消息时,您不知道该消息将被分成多少个块。如果接收者在一个块后停止阅读,它可能没有阅读整条消息。因此,接收者需要一个指示器来告诉它何时已阅读整个消息。

使用{packet, 2},接收器将读取两个字节以获取消息的长度,例如 100,然后接收器将等待,直到它从流式传输到接收器的随机大小的字节块中读取 100 个字节。

请注意,当您调用 时send(),erlang 会自动计算消息中的字节数并将长度插入N字节中,如 指定的那样{packet, N},并附加消息。同样,当您调用recv()erlang 时,会自动N从流中读取字节(由 指定{packet, N})以获取消息的长度,然后recv()阻塞直到它从套接字读取 length 个字节,然后recv()返回整个消息。

{数据包,0 | 原始}(等效):

{packet, 0}被指定,recv()将读取其长度参数指定的字节数。如果 Length 为 0,那么我认为recv()将从流中读取一个块,这将是任意数量的字节。其结果是,组合{packet, 0}recv(Socket, 0)要求您创建一个循环读取消息的所有块,并指示对recv()停止阅读,因为它已经达到了消息的末尾,当对方关闭套接字将是:

get_msg(Socket, Chunks) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Chunk} -> 
            get_msg(Socket, [Chunk|Chunks]);
        {error, closed} ->
            lists:reverse(Chunks);
        {error, Other} ->
            Other
    end.

请注意,发送方不能简单地调用gen_tcp:close(Socket)以表示它已完成发送数据(请参阅文档中gen_tcp:close/1的描述)。相反,发送方必须通过调用gen_tcp:shutdown/2来表示已完成发送数据

我认为聊天服务器有问题,因为它指定{packet, 0}recv(Socket, 0)

client_handler(Sock, Server) ->
    gen_tcp:send(Sock, "Please respond with a sensible name.\r\n"),
    {ok,N} = gen_tcp:recv(Sock,0), %% <**** HERE ****
    case string:tokens(N,"\r\n") of

但它不使用循环recv()


{主动,真实}

通过 TCP(或 UDP)连接发送的消息会自动从套接字中读取并放置在控制进程的邮箱中。控制进程是调用accept() 的进程或调用connect() 的进程。不是调用 recv() 直接从套接字读取消息,而是使用接收块从邮箱中提取消息

get_msg(Socket)
    receive
        {tcp, Socket, Chunk} ->  %Socket is already bound!
        ...
    end

{数据包,1|2|4}

Erlang 会自动为您从套接字读取消息的所有块,并在邮箱中放置一条完整的消息(去掉长度标头):

get_msg(Socket) ->
    receive
        {tcp, Socket, CompleteMsg} ->
            CompleteMsg,
        {tcp_closed, Socket} ->
            io:format("Server closed socket.~n")
    end.

{数据包,0 | 原始}(等效):

消息不会有长度头,所以当 Erlang 从套接字读取时,Erlang 无法知道消息的结尾何时到达。结果,Erlang 将从套接字读取的每个块放入邮箱。您需要一个循环来从邮箱中提取所有块,另一方必须关闭套接字以表示没有更多块即将到来:

get_msg(ClientSocket, Chunks) ->
    receive
        {tcp, ClientSocket, Chunk} ->
            get_msg(ClientSocket, [Chunk|Chunks]);
        {tcp_closed, ClientSocket} ->
            lists:reverse(Chunks)
    end.


recv() 文件提到一些有关recv()的长度参数是适用于插座原始模式。但是因为我不知道 Socket 何时处于原始模式,所以我不相信 Length 参数。但请看这里:Erlang gen_tcp:recv(Socket, Length) semantics好的,现在我到了某个地方:来自 erlang inet 文档

{packet, PacketType}(TCP/IP sockets)
Defines the type of packets to use for a socket. Possible values:

raw | 0
    No packaging is done.

1 | 2 | 4
    Packets consist of a header specifying the number of bytes in the packet, followed by that  
    number of bytes. The header length can be one, two, or four bytes, and containing an  
    unsigned integer in big-endian byte order. Each send operation generates the header, and the  
    header is stripped off on each receive operation.

    The 4-byte header is limited to 2Gb [message length].

正如Erlang gen_tcp:recv(Socket, Length) 语义中的示例所确认的那样,当{packet,0}指定时,arecv()可以指定从 TCP 流读取的长度。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

监督gen_tcp客户端/服务器

来自分类Dev

使用Erlang的gen_tcp模块在Elixir上使用TCP侦听器

来自分类Dev

使用g ++的TCP服务器客户端

来自分类Dev

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

来自分类Dev

使用Ajax上传文件-服务器端未收到任何内容

来自分类Dev

Delphi-使用Indy检查客户端状态的简单TCP客户端/服务器

来自分类Dev

使用C#的TCP / IP服务器/客户端多连接并接收客户端的公共IP地址

来自分类Dev

Delphi-使用Indy检查客户端状态的简单TCP客户端/服务器

来自分类Dev

为什么我没有从我的 TCP 服务器和客户端收到任何消息?

来自分类Dev

TCP 服务器未收到来自 TCP 客户端的消息 (Java)

来自分类Dev

C# TCP 客户端发送消息,但服务器未收到消息

来自分类Dev

在服务器端客户端-服务器TCP上未收到单独的值

来自分类Dev

Java客户端和Erlang服务器?

来自分类Dev

在erlang中中断gen_tcp:recv

来自分类Dev

使用ns3的基于TCP的客户端-服务器系统

来自分类Dev

在C ++中使用TCP套接字进行远程客户端和服务器通信

来自分类Dev

TCP套接字服务器/客户端连接应使用哪个IP /端口?

来自分类Dev

我应该为基于tcp的服务器-客户端连接使用相同的端口吗

来自分类Dev

TCP/IP 客户端和服务器基本使用和参考

来自分类Dev

初始连接后,TCP服务器未收到任何信息。Python

来自分类Dev

C ++服务器使用TCP向Android客户端发送消息

来自分类Dev

使用套接字在客户端阻止的客户端-服务器通信

来自分类Dev

让TCP客户端搜索TCP服务器

来自分类Dev

让TCP客户端搜索TCP服务器

来自分类Dev

未收到Indy TCP客户端数据

来自分类Dev

TCP客户端未收到所有消息

来自分类Dev

TCP客户端-服务器程序的问题

来自分类Dev

XmlSerializer和NetworkStream与Tcp服务器-客户端

来自分类Dev

TCP服务器和客户端Java

Related 相关文章

  1. 1

    监督gen_tcp客户端/服务器

  2. 2

    使用Erlang的gen_tcp模块在Elixir上使用TCP侦听器

  3. 3

    使用g ++的TCP服务器客户端

  4. 4

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

  5. 5

    使用Ajax上传文件-服务器端未收到任何内容

  6. 6

    Delphi-使用Indy检查客户端状态的简单TCP客户端/服务器

  7. 7

    使用C#的TCP / IP服务器/客户端多连接并接收客户端的公共IP地址

  8. 8

    Delphi-使用Indy检查客户端状态的简单TCP客户端/服务器

  9. 9

    为什么我没有从我的 TCP 服务器和客户端收到任何消息?

  10. 10

    TCP 服务器未收到来自 TCP 客户端的消息 (Java)

  11. 11

    C# TCP 客户端发送消息,但服务器未收到消息

  12. 12

    在服务器端客户端-服务器TCP上未收到单独的值

  13. 13

    Java客户端和Erlang服务器?

  14. 14

    在erlang中中断gen_tcp:recv

  15. 15

    使用ns3的基于TCP的客户端-服务器系统

  16. 16

    在C ++中使用TCP套接字进行远程客户端和服务器通信

  17. 17

    TCP套接字服务器/客户端连接应使用哪个IP /端口?

  18. 18

    我应该为基于tcp的服务器-客户端连接使用相同的端口吗

  19. 19

    TCP/IP 客户端和服务器基本使用和参考

  20. 20

    初始连接后,TCP服务器未收到任何信息。Python

  21. 21

    C ++服务器使用TCP向Android客户端发送消息

  22. 22

    使用套接字在客户端阻止的客户端-服务器通信

  23. 23

    让TCP客户端搜索TCP服务器

  24. 24

    让TCP客户端搜索TCP服务器

  25. 25

    未收到Indy TCP客户端数据

  26. 26

    TCP客户端未收到所有消息

  27. 27

    TCP客户端-服务器程序的问题

  28. 28

    XmlSerializer和NetworkStream与Tcp服务器-客户端

  29. 29

    TCP服务器和客户端Java

热门标签

归档