我只是用gen_tcp进行测试。一个简单的echo服务器和一个客户端。
但是客户端启动和关闭,服务器接受两个连接,一个是好的,另一个是坏的。
我的演示脚本的任何问题,以及如何解释它?
1> c(echo).
{ok,echo}
2> echo:listen(1111).
ok {ok,{{192,168,2,184},51608}}
ok {error,enotconn}
演示服务器
> spawn(fun() -> {ok, P} = gen_tcp:connect("192.168.2.173", 1111, []), gen_tcp:send(P, "aa"), gen_tcp:close(P) end).
<0.64.0>
CATQ()
```
答案 0 :(得分:3)
但是客户端启动和关闭,服务器接受两个连接,一个是好的,另一个是坏的。
实际上,您的服务器只接受了一个连接:
loop/1
inet:peername/1
返回{ok,{{192,168,2,184},51608}}
,因为套接字仍处于打开状态gen_tcp:recv/2
返回客户端发送的<<"aa">>
gen_tcp:send/2
将数据从3发送到客户端loop/1
inet:peername/1
返回{error,enotconn}
,因为套接字已被客户端gen_tcp:recv/2
返回{error,closed}
所以实际上,你的echo服务器运行正常,但是@ zxq9在评论中提到了一些改进。
将接受的套接字的控制权移交给新生成的进程。
-module(echo).
-export([listen/1]).
-define(TCP_OPTIONS, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]).
listen(Port) ->
{ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
accept(LSocket).
accept(LSocket) ->
{ok, CSocket} = gen_tcp:accept(LSocket),
Ref = make_ref(),
To = spawn(fun() -> init(Ref, CSocket) end),
gen_tcp:controlling_process(CSocket, To),
To ! {handoff, Ref, CSocket},
accept(LSocket).
init(Ref, Socket) ->
receive
{handoff, Ref, Socket} ->
{ok, Peername} = inet:peername(Socket),
io:format("[S] peername ~p~n", [Peername]),
loop(Socket)
end.
loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
io:format("[S] got ~p~n", [Data]),
gen_tcp:send(Socket, Data),
loop(Socket);
{error, closed} ->
io:format("[S] closed~n", []);
E ->
io:format("[S] error ~p~n", [E])
end.
在客户端等待echo服务器在关闭套接字之前发回数据。
spawn(fun () ->
{ok, Socket} = gen_tcp:connect("127.0.0.1", 1111, [binary, {packet, 0}, {active, false}]),
{ok, Peername} = inet:peername(Socket),
io:format("[C] peername ~p~n", [Peername]),
gen_tcp:send(Socket, <<"aa">>),
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
io:format("[C] got ~p~n", [Data]),
gen_tcp:close(Socket);
{error, closed} ->
io:format("[C] closed~n", []);
E ->
io:format("[C] error ~p~n", [E])
end
end).
服务器应如下所示:
1> c(echo).
{ok,echo}
2> echo:listen(1111).
[S] peername {{127,0,0,1},57586}
[S] got <<"aa">>
[S] closed
客户应该看起来像这样:
1> % paste in the code from Improvement 2
<0.34.0>
[C] peername {{127,0,0,1},1111}
[C] got <<"aa">>
正如@ zxq9所提到的,这是不是 OTP样式代码,可能不应该用于教育以外的任何事情。
更好的方法可能是使用ranch或gen_listener_tcp之类的内容来监听和接受连接。这两个项目都有回显服务器的示例:tcp_echo (ranch)和echo_server.erl (gen_listener_tcp)。