[Erlang 0097] TCP半开的几个小测试
生活随笔
收集整理的這篇文章主要介紹了
[Erlang 0097] TCP半开的几个小测试
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
TCP半開的幾個(gè)測試,比較簡單都在Erlang Shell中完成.立此存照,備忘. gen_tcp提供了shutdown來實(shí)現(xiàn)這個(gè)功能,下面官方文檔中提到了{(lán)exit_on_close,false}參數(shù),如果要實(shí)現(xiàn)半開,無論是Read Write都要添加這個(gè)參數(shù). 官網(wǎng)文檔?Doc Ref shutdown(Socket, How) -> ok | {error, Reason} ???
Types:
Socket = socket()
How = read | write | read_write
Reason = posix()
Immediately close a socket in one or two directions.
How == write means closing the socket for writing, reading from it is still possible.
To be able to handle that the peer has done a shutdown on the write side, the {exit_on_close, false} option is useful.
%%ServerEshell V5.9 (abort with ^G) 1> {ok,S0}=gen_tcp:listen(5678,[]). {ok,#Port<0.506>} 2> {ok,S2}=gen_tcp:accept(S0). {ok,#Port<0.512>} 3> flush(). Shell got {tcp,#Port<0.512>,[104,105,44,121,111,117,32,104,97,118,101,32,110,101,119,32,109,115,103,32,0]} ok 4> gen_tcp:send(S2,"are you alive?"). ok 5> gen_tcp:shutdown(S2,write). %% 執(zhí)行完這一句 檢查Socket已經(jīng)是einval ok 6> inet:getstat(S2). {error,einval} 7>%%ClientEshell V5.9 (abort with ^G) 1> {ok,S1}= gen_tcp:connect("localhost",5678,[]). {ok,#Port<0.511>} 2> gen_tcp:send(S1,"hi,you have new msg \0"). ok 3> flush(). Shell got {tcp,#Port<0.511>,"are you alive?"} ok 4> gen_tcp:send(S1,"hi,you have new msg \0"). {error,closed} 5>
Types:
Socket = socket()
How = read | write | read_write
Reason = posix()
Immediately close a socket in one or two directions.
How == write means closing the socket for writing, reading from it is still possible.
To be able to handle that the peer has done a shutdown on the write side, the {exit_on_close, false} option is useful.
?
shutdown read
%% ServerEshell V5.9 (abort with ^G) 1> {ok,S0}=gen_tcp:listen(5678,[]). {ok,#Port<0.506>} 2> {ok,S2}=gen_tcp:accept(S0). {ok,#Port<0.512>} 3> flush(). Shell got {tcp,#Port<0.512>,[104,105,44,121,111,117,32,104,97,118,101,32,110,101,119,32,109,115,103,32,0]} ok 4> gen_tcp:shutdown(S2,read). ok 5> flush(). Shell got {tcp_closed,#Port<0.512>} ok 6> gen_tcp:send(S2,"are you alive?"). {error,closed} 7>%% ClientEshell V5.9 (abort with ^G) 1> {ok,S1}= gen_tcp:connect("localhost",5678,[]). {ok,#Port<0.511>} 2> gen_tcp:send(S1,"hi,you have new msg \0"). ok 3> gen_tcp:send(S1,"hi,you have new msg \0"). %% 發(fā)送這條消息導(dǎo)致server端 接受到tcp_closed的消息 ok 4> gen_tcp:send(S1,"hi,you have new msg \0"). %% 服務(wù)器端宕掉之后再發(fā)送消息 Client也會出現(xiàn){error,closed}錯誤 {error,closed}?
Client添加一下選項(xiàng)?{exit_on_close, false}?試試 %% ServerEshell V5.9 (abort with ^G) 1> {ok,S0}=gen_tcp:listen(5678,[]). {ok,#Port<0.506>} 2> {ok,S2}=gen_tcp:accept(S0). {ok,#Port<0.512>} 3> gen_tcp:send(S2,"are you alive?"). ok 4> flush(). Shell got {tcp,#Port<0.512>,[104,105,44,121,111,117,32,104,97,118,101,32,110,101,119,32,109,115,103,32,0]} ok 5> gen_tcp:shutdown(S2,read). ok 6> inet:getstat(S2). %%這時(shí)候檢查一下Socket的狀態(tài) {ok,[{recv_oct,21},{recv_cnt,1},{recv_max,21},{recv_avg,21},{recv_dvi,0},{send_oct,14},{send_cnt,1},{send_max,14},{send_avg,14},{send_pend,0}]} 7> gen_tcp:send(S2,"are you alive?===="). %% 由于server只關(guān)閉了read 所以發(fā)送消息還可以發(fā)送出去 ok 8> flush(). %% Client嘗試發(fā)送消息 Shell got {tcp_closed,#Port<0.512>} ok 9> inet:getstat(S2). {error,einval} 10>%% ClientEshell V5.9 (abort with ^G) 1> {ok,S1} = gen_tcp:connect("localhost",5678,[{exit_on_close, false} ]). {ok,#Port<0.511>} 2> gen_tcp:send(S1,"hi,you have new msg \0"). ok 3> flush(). Shell got {tcp,#Port<0.511>,"are you alive?"} ok 4> flush(). %% 服務(wù)器端只關(guān)閉了read 還可以發(fā)送消息 這條消息還可以收到 Shell got {tcp,#Port<0.511>,"are you alive?===="} ok 5> gen_tcp:send(S1,"hi,you have new msg \0"). %% Client向Server發(fā)送消息 會導(dǎo)致Server收到tcp_closed消息 ok 6> gen_tcp:send(S1,"hi,you have new msg \0"). {error,closed} 7>?
shutdown write
下面的測試關(guān)注點(diǎn)是shutdown write,過程和上面類似:%%ServerEshell V5.9 (abort with ^G) 1> {ok,S0}=gen_tcp:listen(5678,[]). {ok,#Port<0.506>} 2> {ok,S2}=gen_tcp:accept(S0). {ok,#Port<0.512>} 3> flush(). Shell got {tcp,#Port<0.512>,[104,105,44,121,111,117,32,104,97,118,101,32,110,101,119,32,109,115,103,32,0]} ok 4> gen_tcp:send(S2,"are you alive?"). ok 5> gen_tcp:shutdown(S2,write). %% 執(zhí)行完這一句 檢查Socket已經(jīng)是einval ok 6> inet:getstat(S2). {error,einval} 7>%%ClientEshell V5.9 (abort with ^G) 1> {ok,S1}= gen_tcp:connect("localhost",5678,[]). {ok,#Port<0.511>} 2> gen_tcp:send(S1,"hi,you have new msg \0"). ok 3> flush(). Shell got {tcp,#Port<0.511>,"are you alive?"} ok 4> gen_tcp:send(S1,"hi,you have new msg \0"). {error,closed} 5>
?
調(diào)整實(shí)驗(yàn) 添加exit_on_close選項(xiàng)? %%ServerEshell V5.9 (abort with ^G) 1> {ok,S0}=gen_tcp:listen(5678,[]). {ok,#Port<0.506>} 2> {ok,S2}=gen_tcp:accept(S0). {ok,#Port<0.512>} 3> flush(). Shell got {tcp,#Port<0.512>,[104,105,44,121,111,117,32,104,97,118,101,32,110,101,119,32,109,115,103,32,0]} ok 4> gen_tcp:send(S2,"are you alive?"). ok 5> gen_tcp:shutdown(S2,write). ok 6> inet:getstat(S2). %% 對比上面的測試結(jié)果 這里是正常的狀態(tài) {ok,[{recv_oct,21},{recv_cnt,1},{recv_max,21},{recv_avg,21},{recv_dvi,0},{send_oct,14},{send_cnt,1},{send_max,14},{send_avg,14},{send_pend,0}]} 7> flush(). %% 由于只關(guān)閉了寫,讀還是正常的,接受到的消息 Shell got {tcp,#Port<0.512>,[104,105,44,121,111,117,32,104,97,118,101,32,110,101,119,32,109,115,103,32,50,32,0]} ok 8> gen_tcp:send(S2,"are you alive?"). %% 關(guān)閉了寫 這里的調(diào)用就會報(bào)錯了 {error,closed} 9> inet:getstat(S2). {error,einval} 10>%% ClientEshell V5.9 (abort with ^G) 1> {ok,S1} = gen_tcp:connect("localhost",5678,[{exit_on_close, false} ]). {ok,#Port<0.511>} 2> gen_tcp:send(S1,"hi,you have new msg \0"). ok 3> flush(). Shell got {tcp,#Port<0.511>,"are you alive?"} ok 4> gen_tcp:send(S1,"hi,you have new msg 2 \0"). ok 5>?
gen_tcp shutdown
看看gen_tcp 的shutdown的邏輯: shutdown(S, How) when is_port(S) ->case inet_db:lookup_socket(S) of{ok, Mod} ->Mod:shutdown(S, How);Error ->Errorend.?
?inet_db:lookup_socket(S)?的結(jié)果是什么?在Shell里面測試一下: Eshell V5.9 (abort with ^G) 1> {ok,S0}=gen_tcp:listen(5678,[]). {ok,#Port<0.506>} 2> inet_db:lookup_socket(S0). {ok,inet_tcp} 3>?
|--> ?inet_tcp
%% %% Shutdown one end of a socket %% shutdown(Socket, How) ->prim_inet:shutdown(Socket, How).?
|-->?prim_inet.erl shutdown(S, read) when is_port(S) ->shutdown_2(S, 0); shutdown(S, write) when is_port(S) ->shutdown_1(S, 1); shutdown(S, read_write) when is_port(S) ->shutdown_1(S, 2).shutdown_1(S, How) ->case subscribe(S, [subs_empty_out_q]) of{ok,[{subs_empty_out_q,N}]} when N > 0 ->shutdown_pend_loop(S, N); %% wait for pending output to be sent_Other -> okend,shutdown_2(S, How).shutdown_2(S, How) ->case ctl_cmd(S, ?TCP_REQ_SHUTDOWN, [How]) of{ok, []} -> ok;{error,_}=Error -> Errorend.shutdown_pend_loop(S, N0) ->receive{empty_out_q,S} -> okafter ?INET_CLOSE_TIMEOUT ->case getstat(S, [send_pend]) of{ok,[{send_pend,N0}]} -> ok;{ok,[{send_pend,N}]} -> shutdown_pend_loop(S, N);_ -> okendend.?
上面處理考慮的相當(dāng)周全,如果shutdown的時(shí)候有消息還沒有發(fā)送完成,就會先完成暫存數(shù)據(jù)的發(fā)送,霸爺有一篇很詳細(xì)的分析: ?gen_tcp調(diào)用進(jìn)程收到{empty_out_q, Port}消息奇怪行為分析?
?最后小圖一張?Natalie Portman?:
?
總結(jié)
以上是生活随笔為你收集整理的[Erlang 0097] TCP半开的几个小测试的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Cloud(二) Cons
- 下一篇: 再也不学AJAX了!(二)使用AJAX