Erlang gen_tcp没有收到任何东西 [英] Erlang gen_tcp not receiving anything

查看:170
本文介绍了Erlang gen_tcp没有收到任何东西的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



发送消息的服务器代码

 客户端(Socket,服务器) - > 
gen_tcp:send(Socket,请输入你的名字),
io:format(发送确认),
{ok,N} = gen_tcp:recv(Socket,0) ,
case string:
的名称(N,\\\\
)[名称] - >
Client = #client {socket = Socket,name = Name,pid = self()},
服务器! {'new client',Client},
client_loop(Client,Server)
end。

应收到和打印的客户端

 客户端(端口) - > 
{ok,Sock} = gen_tcp:connect(localhost,Port,[{active,false},{packet,2}]),
A = gen_tcp:recv(Sock,0)
A.


解决方案

我想你的客户是因为它指定:

  {packet,2} 

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

  {packet ,0} 

编程Erlang(2nd)在p。 269它说:


请注意,客户端和服务器使用的数据包的参数
必须符合。如果使用 {packet,2} 和客户端以 {packet,4} 打开服务器,那么没有


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

  %% ===服务器:{active,false},{packet,0} ==== 

客户端(端口) - >
{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(客户端发送:〜s〜n,[名称]),
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) 。

但是,我认为chatserver和我的客户端都有 严重问题 即可。当您通过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:
{ok,Chunk}的recv(Socket,0) - >
get_msg(Socket,[Chunk | Chunks]);
{error,closed} - >
列表:reverse(Chunks);
{error,Other} - >
其他
结束。

但这又引发了另一个问题:如果chatserver在循环访问recv()客户端,并且在客户端向服务器发送消息之后,客户端在等待来自服务器的消息的recv()上循环,并且双方需要另一方来关闭套接字以突破其recv()循环,那么你会得到死锁,因为两边都没有关闭它们的套接字。因此,客户端将不得不关闭套接字,以便chatserver突破其recv()循环并处理消息。但是,由于客户端关闭套接字,服务器无法将任何东西发送回客户端。因此,当指定 {packet,0} 时,我不知道是否可以进行双向通信。



下面是关于 {packet,N} {active,true | false} 文档和搜索:






send()



当您调用 send()时,实际上没有将数据*传输到目的地。相反, send()阻塞,直到目的地呼叫 recv(),只有这样才能将数据传输到目的地。



*在编程Erlang(2nd)中, 176它表示,当您调用 send()时,由于操作系统缓冲数据的方式,少量的数据将被推送到目的地,并且其中 send()将阻止,直到 recv()将数据拉到目的地。



< hr>

默认选项



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

  Defaults = inet:getopts(Socket,[mode,active,packet]),
io:format(默认选项:〜w〜n,[默认值])。

--output: -
默认选项:{ok,[{mode,list},{active,true},{packet,0}]}

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






  {active ,true} {active,false} 
+ -------------- + ---------------- +
{数据包,1 | 2 | 4}:|接收| recv()|
|无循环|无循环|
+ -------------- + ---------------- +
{packet,0 | raw}:|接收| recv()|
(当量)|循环|循环|
+ -------------- + ---------------- +




{active,false}


邮箱不要登陆邮箱。此选项用于防止客户端使用消息溢出服务器的邮箱。不要尝试使用接收块从邮箱提取tcp消息 - 不会有任何消息。当进程想要读取消息时,该进程需要通过调用 recv()直接从套接字读取消息。



{packet,1 | 2 | 4}



指定每个端口期望消息符合的协议 {packet,2} 指定每个消息前面都会有两个字节,其中包含消息的长度。这样一来,消息的接收者将知道从字节流读取到达消息结束的时间。当您通过TCP连接发送消息时,您不知道消息将被分割成多少块。如果接收器在一个块之后停止读取,它可能没有读取整个消息。因此,接收者需要一个指示器来告知整个消息何时被读取。



使用 {packet,2} ,接收器将读取两个字节以获得消息的长度,例如100,则接收器将等待,直到从流向接收器的随机大小的字节块读取100个字节。



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



{packet,0 |原始} (等效):



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

  get_msg(Socket,Chunks) - > 
case gen_tcp:
{ok,Chunk}的recv(Socket,0) - >
get_msg(Socket,[Chunk | Chunks]);
{error,closed} - >
列表:reverse(Chunks);
{error,Other} - >
其他
结束。

我认为chatserver是错误的,因为它指定了 {packet,0} / code>结合 recv(Socket,0)

  client_handler(Sock,Server) - > 
gen_tcp:send(Sock,请用敏感名称回复。\\\\
),
{ok,N} = gen_tcp:recv(Sock,0),%%< **** HERE ****
案例字符串:

$ b的令牌(N,\\\\

$ b

但它不使用 recv()的循环。







{active,true}


TCP(或UDP)连接将自动从套接字读取并放在控制过程的邮箱中​​。控制过程是称为accept()或称为connect()的进程的过程。您可以使用接收块从邮箱中提取消息,而不是调用recv()直接从套接字读取消息:

  get_msg(Socket)
接收
{tcp,Socket,Chunk} - > %Socket已经绑定了!
...
end

{packet,1 | 2 | 4}



Erlang会自动从套接字中读取所有的邮件块,并放置一条完整的邮件(长度标题被删除)在邮箱中:

  get_msg(Socket) - > 
接收
{tcp,Socket,CompleteMsg} - >
CompleteMsg,
{tcp_closed,Socket} - >
io:format(Server closed socket。〜n)
end。

{packet,0 |原始} (等效):



消息没有长度头,所以当Erlang从套接字读取时,Erlang无法知道何时结束的消息已经到了。因此,Erlang将从套接字读取的每个块放入邮箱。您需要一个循环来从邮箱中提取所有的块,另一端必须关闭套接字,以表示不再有块块。

 code> get_msg(ClientSocket,Chunks) - > 
接收
{tcp,ClientSocket,Chunk} - >
get_msg(ClientSocket,[Chunk | Chunks]);
{tcp_closed,ClientSocket} - >
列表:reverse(Chunks)
end。









recv() docs 提到一些关于 recv()的长度参数只适用于套接字在原始模式中。但是因为我不知道Socket是否处于raw模式,所以我不相信Length参数。但请看这里: Erlang gen_tcp:recv(Socket,Length)语义。好的,现在我到了某个地方:从erlang inet docs

  {packet,PacketType}(TCP / IP套接字)
定义要使用的数据包的类型用于插座。可能的值:

raw | 0
没有包装完成。

1 | 2 | 4
数据包由指定数据包中的字节数的头组成,后面是
个字节数。标题长度可以是一个,两个或四个字节,并且以big-endian字节顺序包含
无符号整数。每个发送操作都会生成头文件,并且
头文件在每个接收操作上被剥离。

4字节头部限制为2Gb [消息长度]。

作为 Erlang gen_tcp:recv(Socket,Length)语义确认,当指定 {packet,0} 时, recv()可以指定从TCP流读取的长度。


Im trting to receive data at client side, but nothing is received.

Server code that sends message

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 that should receive and print out

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

解决方案

I think your client is faulty because it specifies:

{packet, 2}

yet the server specifies (in code not shown) :

{packet, 0}

In Programming Erlang (2nd) on p. 269 it says:

Note that the arguments to packet used by the client and the server must agree. If the server was opened with {packet,2} and the client with {packet,4}, then nothing would work.

The following client can successfully receive text from the server:

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

However, I think that both the chatserver and my client have serious issues. When you send a message through a TCP (or UDP) connection, you have to assume that the message will get split into an indeterminate number of chunks--each with an arbitrary length. When {packet,0} is specified, I think recv(Socket, 0) will only read one chunk from the socket, then return. That chunk may be the entire message, or it might be only a piece of the message. To guarantee that you've read the entire message from the socket, I think you have to loop over the recv():

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

Then the question becomes: how do you know when you've read the entire message so that you can end the loop? {packet,0} tells Erlang not to prepend a length header to a message, so how do you know where the end of the message is? Are more chunks coming, or did the recv() already read the last chunk? I think the marker for the end of the message is when the other side closes the socket:

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.

But that raises another issue: if the chatserver is looping on a recv() waiting for a message from the client, and after the client sends a message to the server the client loops on a recv() waiting for a message from the server, and both sides need the other side to close the socket to break out of their recv() loops, then you will get deadlock because neither side is closing their socket. As a result, the client will have to close the socket in order for the chatserver to break out of its recv() loop and process the message. But, then the server can't send() anything back to the client because the client closed the socket. As a result, I don't know if you can do two way communication when {packet,0} is specified.

Here are my conclusions about {packet, N} and {active, true|false} from reading the docs and searching around:


send():

When you call send(), no data* is actually transferred to the destination. Instead, send() blocks until the destination calls recv(), and only then is data transferred to the destination.

* In "Programming Erlang (2nd)", on p. 176 it says that a small amount of data will be pushed to the destination when you call send() due to the way an OS buffers data, and thereafer send() will block until a recv() pulls data to the destination.


Default options:

You can get the defaults for a socket by specifying an empty list for its options, then doing:

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

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

You can use inet:getopts() to show that gen_tcp:accept(Socket) returns a socket with the same options as Socket.


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

{active, false}

Messages do not land in the mailbox. This option is used to prevent clients from flooding a server's mailbox with messages. Do not try to use a receive block to extract 'tcp' messages from the mailbox--there won't be any. When a process wants to read a message, the process needs to read the message directly from the socket by calling recv().

{packet, 1|2|4}:

The packet tuple specifies the protocol that each side expects messages to conform to. {packet, 2} specifies that each message will be preceded by two bytes, which will contain the length of the message. That way, a receiver of a message will know how long to keep reading from the stream of bytes to reach the end of the message. When you send a message over a TCP connection, you have no idea how many chunks the message will get split into. If the receiver stops reading after one chunk, it might not have read the whole message. Therefore, the receiver needs an indicator to tell it when the whole message has been read.

With {packet, 2}, a receiver will read two bytes to get the length of the message, say 100, then the receiver will wait until it has read 100 bytes from the randomly sized chunks of bytes that are streaming to the receiver.

Note that when you call send(), erlang automatically calculates the number of bytes in the message and inserts the length into N bytes, as specified by {packet, N}, and appends the message. Likewise, when you call recv() erlang automatically reads N bytes from the stream, as specified by {packet, N}, to get the length of the message, then recv() blocks until it reads length bytes from the socket, then recv() returns the whole message.

{packet, 0 | raw} (equivalent):

When {packet, 0} is specified, recv() will read the number of bytes specified by its Length argument. If Length is 0, then I think recv() will read one chunk from the stream, which will be an arbitrary number of bytes. As a result, the combination of {packet, 0} and recv(Socket, 0) requires that you create a loop to read all the chunks of a message, and the indicator for recv() to stop reading because it has reached the end of the message will be when the other side closes the socket:

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.

I think the chatserver is faulty because it specifies {packet, 0} in combination with 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

yet it does not use a loop for the recv().


{active, true}

Messages sent through a TCP (or UDP) connection are automatically read from the socket for you and placed in the controlling process's mailbox. The controlling process is the process that called accept() or the process that called connect(). Instead of calling recv() to read messages directly from the socket, you extract messages from the mailbox with a receive block:

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

{packet, 1|2|4}:

Erlang automatically reads all the chunks of a message from the socket for you and places a complete message (with the length header stripped off) in the mailbox:

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

{packet, 0 | raw} (equivalent):

Messages do not have a length header, so when Erlang reads from the socket, Erlang has no way of knowing when the end of the message has arrived. As a result, Erlang places each chunk it reads from the socket into the mailbox. You need a loop to extract all the chunks from the mailbox, and the other side has to close the socket to signal that no more chunks are coming:

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



The recv() docs mention something about recv()'s Length argument only being applicable to sockets in raw mode. But because I don't know when a Socket is in raw mode, I don't trust the Length argument. But see here: Erlang gen_tcp:recv(Socket, Length) semantics. Okay, now I'm getting somewhere: from the erlang inet docs:

{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].

As the examples at Erlang gen_tcp:recv(Socket, Length) semantics confirm, when {packet,0} is specified, a recv() can specify the Length to read from the TCP stream.

这篇关于Erlang gen_tcp没有收到任何东西的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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