檢視原始碼 gen_tcp (kernel v10.2)
TCP/IP 通訊協定 Socket 的介面。
此模組提供透過 TCP/IP 通訊協定 Socket 進行通訊的函式。
以下程式碼片段是一個簡單的範例,說明客戶端如何連線至埠號 5678 的伺服器、傳輸二進位資料,然後關閉連線
client() ->
SomeHostInNet = "localhost", % to make it runnable on one machine
{ok, Sock} = gen_tcp:connect(SomeHostInNet, 5678,
[binary, {packet, 0}]),
ok = gen_tcp:send(Sock, "Some Data"),
ok = gen_tcp:close(Sock).
另一端,伺服器正在埠號 5678 上監聽,接受連線,並接收二進位資料
server() ->
{ok, LSock} = gen_tcp:listen(5678, [binary, {packet, 0},
{active, false}]),
{ok, Sock} = gen_tcp:accept(LSock),
{ok, Bin} = do_recv(Sock, []),
ok = gen_tcp:close(Sock),
ok = gen_tcp:close(LSock),
Bin.
do_recv(Sock, Bs) ->
case gen_tcp:recv(Sock, 0) of
{ok, B} ->
do_recv(Sock, [Bs, B]);
{error, closed} ->
{ok, list_to_binary(Bs)}
end.
如需更多範例,請參閱範例章節。
注意
建立 Socket 的函式可以採用一個可選的選項:
{inet_backend, Backend}
,如果指定,它必須是第一個選項。這會選擇平台 Socket API 的實作後端。這是一個臨時選項,在未來的版本中將會被忽略。
預設值是
Backend = inet
,它會選擇傳統的inet_drv.c
驅動程式。另一個選擇是Backend = socket
,它會選擇新的socket
模組及其 NIF 實作。當節點啟動時,可以使用應用程式
kernel
的組態變數inet_backend
來變更系統預設值。對於
gen_tcp
使用inet_backend = socket
,我們已盡可能地嘗試「相容」,但有時不可能。以下列出 inet 後端inet
(預設) 和socket
的行為不同的情況:
如果使用者呼叫
gen_tcp:send/2
並使用inet_backend = inet
,嘗試傳送比作業系統緩衝區空間更多的資料,則「剩餘資料」會由 inet 驅動程式緩衝 (並稍後在背景傳送)。對使用者而言,效果是此呼叫是非阻塞的。當
inet_backend = socket
時,並非如此,因為沒有緩衝。相反,使用者會一直掛起,直到所有資料都已傳送或達到send_timeout
超時時間。
shutdown/2
可能會隱藏錯誤此呼叫不涉及接收處理序狀態,並且直接在底層 Socket 上完成。例如在 Linux 上,已知有一個錯誤行為是它會跳過某些檢查,因此在監聽 Socket 上執行關閉會傳回
ok
,但邏輯結果應該是{error, enotconn}
。inet_drv.c
驅動程式會執行額外的檢查並模擬正確的錯誤,但使用Backend = socket
會引入額外的開銷來涉及接收處理序。nodelay 選項是一個 TCP 特定選項,不與
domain = local
相容。當使用
inet_backend = socket
時,嘗試使用domain = local
建立 Socket (透過監聽或連線) (例如使用選項 {ifaddr, {local,"/tmp/test"}}),將會失敗並傳回{error, enotsup}
。實際上這也不適用於
inet_backend = inet
,但在這種情況下,錯誤會被直接忽略,這是一個壞主意。我們選擇對於inet_backend = socket
不忽略此錯誤。對使用
inet_backend = socket
建立的 Socket 呼叫 gen_tcp:shutdown(Socket, write | read_write) 將會立即生效,這與使用inet_backend = inet
建立的 Socket 不同。請參閱非同步關閉寫入以取得更多資訊。
Windows 需要繫結 Socket (domain =
inet | inet6
)。目前,所有在 Windows 上使用
inet_backend = socket
建立的 Socket 都會被繫結。如果使用者未提供位址,則 gen_tcp 會嘗試自行「找出」位址。
範例
以下範例說明如何使用選項 {active,once}
和多重接受,方法是實作一個伺服器,其方式為使用多個工作處理序在單一監聽 Socket 上執行 accept。函式 start/2
會取得工作處理序的數量和要監聽連入連線的埠號。如果 LPort
指定為 0
,則會使用臨時埠號,這就是為什麼 start 函式會傳回已配置的實際埠號的原因
start(Num,LPort) ->
case gen_tcp:listen(LPort,[{active, false},{packet,2}]) of
{ok, ListenSock} ->
start_servers(Num,ListenSock),
{ok, Port} = inet:port(ListenSock),
Port;
{error,Reason} ->
{error,Reason}
end.
start_servers(0,_) ->
ok;
start_servers(Num,LS) ->
spawn(?MODULE,server,[LS]),
start_servers(Num-1,LS).
server(LS) ->
case gen_tcp:accept(LS) of
{ok,S} ->
loop(S),
server(LS);
Other ->
io:format("accept returned ~w - goodbye!~n",[Other]),
ok
end.
loop(S) ->
inet:setopts(S,[{active,once}]),
receive
{tcp,S,Data} ->
Answer = process(Data), % Not implemented in this example
gen_tcp:send(S,Answer),
loop(S);
{tcp_closed,S} ->
io:format("Socket ~w closed [~w]~n",[S,self()]),
ok
end.
簡單客戶端的範例
client(PortNo,Message) ->
{ok,Sock} = gen_tcp:connect("localhost",PortNo,[{active,false},
{packet,2}]),
gen_tcp:send(Sock,Message),
A = gen_tcp:recv(Sock,0),
gen_tcp:close(Sock),
A.
send
呼叫不接受逾時選項,因為傳送逾時是透過 Socket 選項 send_timeout
來處理。沒有接收器的傳送操作行為主要由底層 TCP 堆疊和網路基礎結構定義。若要撰寫程式碼來處理掛起的接收器,該接收器最終可能會導致傳送者掛起 send
,請執行以下操作。
考量一個處理序,它從客戶端處理序接收要轉寄到網路上伺服器的資料。該處理序透過 TCP/IP 連線到伺服器,並且不會收到它傳送的每則訊息的確認,而是必須依賴傳送逾時選項來偵測另一端沒有回應。連線時可以使用選項 send_timeout
...
{ok,Sock} = gen_tcp:connect(HostAddress, Port,
[{active,false},
{send_timeout, 5000},
{packet,2}]),
loop(Sock), % See below
...
在處理要求的迴圈中,現在可以偵測到傳送逾時
loop(Sock) ->
receive
{Client, send_data, Binary} ->
case gen_tcp:send(Sock,[Binary]) of
{error, timeout} ->
io:format("Send timeout, closing!~n",
[]),
handle_send_timeout(), % Not implemented here
Client ! {self(),{error_sending, timeout}},
%% Usually, it's a good idea to give up in case of a
%% send timeout, as you never know how much actually
%% reached the server, maybe only a packet header?!
gen_tcp:close(Sock);
{error, OtherSendError} ->
io:format("Some other error on socket (~p), closing",
[OtherSendError]),
Client ! {self(),{error_sending, OtherSendError}},
gen_tcp:close(Sock);
ok ->
Client ! {self(), data_sent},
loop(Sock)
end
end.
通常只需偵測接收逾時就足夠了,因為大多數通訊協定都包含來自伺服器的某種形式的確認,但如果通訊協定是嚴格單向的,則選項 send_timeout
會派上用場。
摘要
函式
在監聽 Socket 上接受連入的連線要求。
關閉 TCP Socket。
建立一個連線到指定位址的 Socket。
建立一個連線到指定位址的 Socket。
變更 Socket 的控制處理序 (擁有者)。
建立一個監聽 Socket。
從被動模式的 Socket 接收封包。
在 Socket 上傳送封包。
在一個或兩個方向關閉 Socket。
類型
-type connect_option() :: {fd, Fd :: non_neg_integer()} | inet:address_family() | {ifaddr, socket:sockaddr_in() | socket:sockaddr_in6() | inet:socket_address()} | {ip, inet:socket_address()} | {port, inet:port_number()} | {tcp_module, module()} | {netns, file:filename_all()} | {bind_to_device, binary()} | option().
-type listen_option() :: {fd, Fd :: non_neg_integer()} | inet:address_family() | {ifaddr, socket:sockaddr_in() | socket:sockaddr_in6() | inet:socket_address()} | {ip, inet:socket_address()} | {port, inet:port_number()} | {backlog, B :: non_neg_integer()} | {tcp_module, module()} | {netns, file:filename_all()} | {bind_to_device, binary()} | option().
-type option() :: {active, true | false | once | -32768..32767} | {buffer, non_neg_integer()} | {debug, boolean()} | {delay_send, boolean()} | {deliver, port | term} | {dontroute, boolean()} | {exit_on_close, boolean()} | {exclusiveaddruse, boolean()} | {header, non_neg_integer()} | {high_msgq_watermark, pos_integer()} | {high_watermark, non_neg_integer()} | {keepalive, boolean()} | {linger, {boolean(), non_neg_integer()}} | {low_msgq_watermark, pos_integer()} | {low_watermark, non_neg_integer()} | {mode, list | binary} | list | binary | {nodelay, boolean()} | {packet, 0 | 1 | 2 | 4 | raw | sunrm | asn1 | cdr | fcgi | line | tpkt | http | httph | http_bin | httph_bin} | {packet_size, non_neg_integer()} | {priority, non_neg_integer()} | {raw, Protocol :: non_neg_integer(), OptionNum :: non_neg_integer(), ValueBin :: binary()} | {recbuf, non_neg_integer()} | {reuseaddr, boolean()} | {reuseport, boolean()} | {reuseport_lb, boolean()} | {send_timeout, non_neg_integer() | infinity} | {send_timeout_close, boolean()} | {show_econnreset, boolean()} | {sndbuf, non_neg_integer()} | {tos, non_neg_integer()} | {tclass, non_neg_integer()} | {ttl, non_neg_integer()} | {recvtos, boolean()} | {recvtclass, boolean()} | {recvttl, boolean()} | {ipv6_v6only, boolean()}.
-type option_name() :: active | buffer | debug | delay_send | deliver | dontroute | exit_on_close | exclusiveaddruse | header | high_msgq_watermark | high_watermark | keepalive | linger | low_msgq_watermark | low_watermark | mode | nodelay | packet | packet_size | priority | {raw, Protocol :: non_neg_integer(), OptionNum :: non_neg_integer(), ValueSpec :: (ValueSize :: non_neg_integer()) | (ValueBin :: binary())} | recbuf | reuseaddr | reuseport | reuseport_lb | send_timeout | send_timeout_close | show_econnreset | sndbuf | tos | tclass | ttl | recvtos | recvtclass | recvttl | pktoptions | ipv6_v6only.
-type pktoptions_value() :: {pktoptions, inet:ancillary_data()}.
來自 Socket 選項 pktoptions
的值。
如果平台為 Socket 實作 IPv4 選項 IP_PKTOPTIONS
,或 IPv6 選項 IPV6_PKTOPTIONS
或 IPV6_2292PKTOPTIONS
,則當使用選項名稱 pktoptions
呼叫 inet:getopts/2
時,會傳回此值。
注意
此選項似乎非常特定於 Linux,並且其在未來 Linux 核心版本中的存在也令人擔憂,因為此選項是 RFC 2292 的一部分,該 RFC 早在 (2003) 就被 RFC 3542 取代,而 RFC 3542 明確地移除了從串流 Socket 取得封包資訊的可能性。做為比較:它存在於 FreeBSD 中,但現在已被移除,至少從 FreeBSD 10 開始已移除。
-type socket() :: inet:socket().
由 accept/1,2
和 connect/3,4
傳回。
函式
-spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when ListenSocket :: socket(), Socket :: socket(), Reason :: closed | system_limit | inet:posix().
-spec accept(ListenSocket, Timeout) -> {ok, Socket} | {error, Reason} when ListenSocket :: socket(), Timeout :: timeout(), Socket :: socket(), Reason :: closed | timeout | system_limit | inet:posix().
在監聽 Socket 上接受連入的連線要求。
Socket
必須是從 listen/2
傳回的 Socket。Timeout
指定逾時值 (以毫秒為單位)。預設值為 infinity
。
傳回
- 如果已建立連線,則傳回
{ok, Socket}
- 如果
ListenSocket
已關閉,則傳回{error, closed}
- 如果在
Timeout
內未建立連線,則傳回{error, timeout}
- 如果 Erlang 模擬器中的所有可用埠號都在使用中,則傳回
{error, system_limit}
- 如果其他地方出錯,則傳回 POSIX 錯誤值,請參閱關於可能值的
inet
若要將封包 (輸出) 傳送到傳回的 Socket
,請使用 send/2
。從對等點傳送的封包 (輸入) 會以訊息的形式傳遞給 Socket 擁有者 (建立 Socket 的處理序)。除非在建立監聽 Socket時,選項清單中指定了 {active, false}
。
請參閱關於主動模式 Socket 訊息和被動模式的 connect/4
。
注意
accept
呼叫不一定必須從 Socket 擁有者處理序發出。使用模擬器的 5.5.3 及更高版本,可以從不同的處理序發出多個同步 accept 呼叫,這允許使用接受器處理序池來處理連入連線。
-spec close(Socket) -> ok when Socket :: socket().
關閉 TCP Socket。
請注意,在大多數 TCP 的實作中,執行 close
並不保證已傳送的資料會送達接收方。雖然可以保證接收方在關閉連線之前會看到所有已傳送的資料,但傳送方並不會收到任何這方面的指示。
如果傳送方需要知道接收方已收到所有資料,通常有兩種方式可以實現:
- 使用
gen_tcp:shutdown(Sock, write)
來發出不再傳送資料的訊號,並等待另一方確認其讀取端已關閉,方法是關閉其寫入端,這會在本端顯示為 socket 關閉。 - 在 TCP 之上的協定中實作確認機制,讓連線的兩端都遵守,以指示所有資料都已被看到。socket 選項
{packet, N}
可能會很有用。
-spec connect(SockAddr, Opts) -> {ok, Socket} | {error, Reason} when SockAddr :: socket:sockaddr_in() | socket:sockaddr_in6(), Opts :: [inet:inet_backend() | connect_option()], Socket :: socket(), Reason :: inet:posix().
-spec connect(Address, Port, Opts) -> {ok, Socket} | {error, Reason} when Address :: inet:socket_address() | inet:hostname(), Port :: inet:port_number(), Opts :: [inet:inet_backend() | connect_option()], Socket :: socket(), Reason :: inet:posix(); (SockAddr, Opts, Timeout) -> {ok, Socket} | {error, Reason} when SockAddr :: socket:sockaddr_in() | socket:sockaddr_in6(), Opts :: [inet:inet_backend() | connect_option()], Timeout :: timeout(), Socket :: socket(), Reason :: timeout | inet:posix().
建立一個連線到指定位址的 Socket。
使用 Address
和 Port
參數
等同於 connect(Address, Port, Opts, infinity)
。
使用 SockAddr
參數(自 OTP 24.3 起)
連線到 SockAddr
指定的遠端監聽 socket,例如 socket:sockaddr_in6/0
允許指定連結本機 IPv6 位址的 scope_id
。
除了目標位址的格式之外,等同於 connect/4
。
-spec connect(Address, Port, Opts, Timeout) -> {ok, Socket} | {error, Reason} when Address :: inet:socket_address() | inet:hostname(), Port :: inet:port_number(), Opts :: [inet:inet_backend() | connect_option()], Timeout :: timeout(), Socket :: socket(), Reason :: timeout | inet:posix().
建立一個連線到指定位址的 Socket。
建立一個 socket 並將其連線到主機上 IP 位址為 Address
的 TCP 連接埠 Port
的伺服器,Address
也可以是主機名稱。
Opts
(連線選項)
{ip, Address}
- 如果本機有多個 IP 位址,此選項指定要使用哪一個。{ifaddr, Address}
- 與{ip, Address}
相同。但是,如果
Address
反而是一個socket:sockaddr_in/0
或socket:sockaddr_in6/0
,則此選項會優先於先前使用ip
和port
選項設定的任何值。如果這些選項 (ip
或/和port
) 出現在此選項之後,則可以使用它們來更新此選項的對應欄位 (對於ip
,是addr
欄位,對於port
,是port
欄位)。{fd, integer() >= 0}
- 如果某個 socket 在沒有使用gen_tcp
的情況下連線,請使用此選項來傳遞其檔案描述符。如果{ip, Address}
和/或{port, port_number()}
與此選項結合使用,則fd
會在連線之前繫結到指定的介面和連接埠。如果沒有指定這些選項,則假設fd
已適當繫結。inet
- 設定 socket 以用於 IPv4。inet6
- 設定 socket 以用於 IPv6。local
- 設定 Unix Domain Socket。請參閱inet:local_address/0
。{port, Port}
- 指定要使用的本機連接埠號碼。{tcp_module, module()}
- 覆寫要使用的回呼模組。預設值是 IPv4 的inet_tcp
和 IPv6 的inet6_tcp
。option/0
- 請參閱inet:setopts/2
。
Socket 資料
可以使用 send(Socket, Packet)
將封包傳送到對等端 (傳出)。從對等端傳送的封包 (傳入) 會以訊息形式傳遞給 socket 的擁有者;除非在 Options
清單中指定 {active, false}
,否則為建立 socket 的程序。
主動模式 socket 訊息
{tcp, Socket, Data}
- 來自 socket 的傳入資料。{tcp_passive, Socket}
- socket 處於{active, N}
模式 (詳細資訊請參閱inet:setopts/2
),且其訊息計數器達到0
,表示 socket 已轉換為被動 ({active, false}
) 模式。{tcp_closed, Socket}
- socket 已關閉。{tcp_error, Socket, Reason}
- 發生 socket 錯誤。
被動模式
如果在 socket 的選項清單中指定了 {active, false}
,則會透過呼叫 recv/2,3
擷取封包和錯誤 ( send/2
也可能會傳回錯誤)。
逾時
選用的 Timeout
參數以毫秒為單位指定連線逾時時間。預設值為 infinity
。
注意
請記住,如果底層作業系統
connect()
呼叫傳回逾時,即使指定了更大的Timeout
(例如infinity
),gen_tcp:connect
也會傳回逾時 (即{error, etimedout}
)。
注意
傳遞至
connect
的選項預設值會受到核心設定參數inet_default_connect_options
的影響。詳細資訊請參閱inet
。
-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: socket(), Pid :: pid(), Reason :: closed | not_owner | badarg | inet:posix().
變更 Socket 的控制處理序 (擁有者)。
為 Socket
指派新的控制程序 Pid
。控制程序是 socket 將訊息傳送到的程序。如果從目前的控制程序以外的任何其他程序呼叫此函式,則會傳回 {error, not_owner}
。
如果由 Pid
識別的程序不是現有的本機 pid/0
,則會傳回 {error, badarg}
。在某些情況下,當 Socket
在執行此函式期間關閉時,也可能會傳回 {error, badarg}
。
如果 socket 處於主動模式,則此函式會將呼叫者信箱中來自 socket 的任何訊息傳輸到新的控制程序。
如果在傳輸期間有任何其他程序與 socket 互動,則可能無法正常運作,並且訊息可能仍保留在呼叫者的信箱中。例如,在傳輸期間變更 socket 的主動模式可能會導致這種情況。
-spec listen(Port, Options) -> {ok, ListenSocket} | {error, Reason} when Port :: inet:port_number(), Options :: [inet:inet_backend() | listen_option()], ListenSocket :: socket(), Reason :: system_limit | inet:posix().
建立一個監聽 Socket。
建立一個 socket 並將其設定為在本地主機的連接埠 Port
上監聽。
如果 Port == 0
,底層作業系統會指派一個可用的 (暫時的) 連接埠號碼,請使用 inet:port/1
來擷取它。
可以使用下列選項
list
- 收到的Packet
會以位元組清單[
byte/0
]
的形式傳遞。binary
- 收到的Packet
會以binary/0
的形式傳遞。{backlog, B}
-B ::
non_neg_integer/0
。backlog 值定義了擱置連線佇列可以成長到的最大長度。預設值為5
。inet6
- 設定 socket 以用於 IPv6。inet
- 設定 socket 以用於 IPv4。{fd, Fd}
- 如果某個 socket 在沒有使用gen_tcp
的情況下建立,請使用此選項來傳遞其檔案描述符。{ip, Address}
- 如果主機有多個 IP 位址,則此選項指定要監聽哪個 IP 位址。{port, Port}
- 指定要使用的本機連接埠號碼。{ifaddr, Address}
- 與{ip, Address}
相同。但是,如果這反而是一個
socket:sockaddr_in/0
或socket:sockaddr_in6/0
,則此選項會優先於先前使用ip
和port
選項設定的任何值。如果這些選項 (ip
或/和port
) 出現在此選項之後,則可以使用它們來更新此選項的對應欄位 (對於ip
,是addr
欄位,對於port
,是port
欄位)。{tcp_module, module()}
- 覆寫要使用的回呼模組。預設值是 IPv4 的inet_tcp
和 IPv6 的inet6_tcp
。option/0
- 請參閱inet:setopts/2
。
當呼叫 accept/1,2
以接受傳入的連線請求時,應該使用傳回的 socket ListenSocket
。
注意
傳遞至
listen
的選項預設值會受到核心設定參數inet_default_listen_options
的影響。詳細資訊請參閱inet
。
-spec recv(Socket, Length) -> {ok, Packet} | {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), Packet :: string() | binary() | HttpPacket, Reason :: closed | inet:posix(), HttpPacket :: term().
-spec recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), Timeout :: timeout(), Packet :: string() | binary() | HttpPacket, Reason :: closed | timeout | inet:posix(), HttpPacket :: term().
從被動模式的 Socket 接收封包。
關閉的 socket 會以傳回值 {error, closed}
表示。如果 socket 不處於被動模式,則傳回值為 {error, einval}
。
只有在 socket 處於 raw
模式時,參數 Length
才有意義,且表示要讀取的位元組數。如果 Length
為 0
,則會傳回所有可用的位元組。如果 Length > 0
,則會傳回剛好 Length
個位元組,或傳回錯誤;除非 socket 從另一側關閉,否則在傳回 {error, closed}
之前的最後一次讀取可能會傳回少於 Length
個位元組的資料。
選用的 Timeout
參數以毫秒為單位指定逾時時間。預設值為 infinity
。
任何程序都可以從被動 socket 接收資料,即使該程序不是 socket 的控制程序也是如此。但是,在任何給定時間,只有一個程序可以在 socket 上呼叫此函式。不建議同時呼叫 recv
,因為其行為取決於 socket 的實作,並且可能會傳回 {error, ealready}
等錯誤。
-spec send(Socket, Packet) -> ok | {error, Reason} when Socket :: socket(), Packet :: iodata(), Reason :: closed | {timeout, RestData} | inet:posix(), RestData :: binary() | erlang:iovec().
在 Socket 上傳送封包。
沒有帶有逾時選項的 send/2
呼叫;如果需要逾時,請使用 socket 選項 send_timeout
。請參閱範例章節。
只有當 inet_backend = socket
時,才會傳回值 {error, {timeout, RestData}}
。
注意
非阻塞傳送。
如果使用者嘗試傳送的資料量超過作業系統傳送緩衝區的容量,剩餘的資料會儲存在(網路驅動程式)內部緩衝區中,稍後在背景傳送。該函數會立即返回 ok(不會通知呼叫者有些資料尚未傳送)。傳送剩餘資料時發生的任何問題可能會稍後返回。
當使用
inet_backend = socket
時,行為會有所不同。這裡沒有緩衝機制,呼叫者會「掛起」,直到所有資料都已傳送完畢,或傳送逾時(由send_timeout
選項指定)到期為止(即使在使用inet
後端且內部緩衝區已滿的情況下,該函數也可能「掛起」)。如果在使用
packet =/= raw
時發生這種情況,則已寫入部分封包。因此,此時絕對不能寫入新的封包,因為對等方無法區分這是否為目前封包中的資料。而是應該將封包設定為 raw,傳送剩餘的資料(作為原始資料),然後再次將封包設定為正確的封包類型。
-spec shutdown(Socket, How) -> ok | {error, Reason} when Socket :: socket(), How :: read | write | read_write, Reason :: inet:posix().
在一個或兩個方向關閉 Socket。
How == write
表示關閉 socket 以進行寫入,但仍然可以從中讀取資料。
如果 How == read
,或者在 Socket
連接埠中沒有緩衝的輸出資料,則會立即執行關閉,並且遇到的任何錯誤都會在 Reason
中返回。
如果 socket 連接埠中有緩衝的資料,則在將緩衝的資料寫入作業系統協定堆疊之前,不會在 socket 上執行關閉。如果發生任何錯誤,則會關閉 socket,並且下一次呼叫 recv/2
或 send/2
時會返回 {error, closed}
。
如果對等方執行其寫入端的關閉,則選項 {exit_on_close, false}
非常有用。這樣一來,在接收指示 socket 已關閉後,socket 仍會保持開啟以進行寫入。
注意
非同步關閉寫入(
How :: write | read_write
)。如果在網路驅動程式於背景傳送緩衝資料時嘗試關閉,則會延後關閉,直到傳送完所有緩衝的資料。該函數會立即返回
ok
,並且不會通知呼叫者(關閉已延後)。當使用
inet_backend = socket
時,行為會有所不同。使用How :: write | read_write
的關閉會立即執行。