檢視原始碼 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_PKTOPTIONSIPV6_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,2connect/3,4 傳回。

函式

-spec accept(ListenSocket) -> {ok, Socket} | {error, Reason}
                when
                    ListenSocket :: socket(),
                    Socket :: socket(),
                    Reason :: closed | system_limit | inet:posix().

等同於 accept(ListenSocket, infinity)

連結到此函式

accept(ListenSocket, Timeout)

檢視原始碼
-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 並不保證已傳送的資料會送達接收方。雖然可以保證接收方在關閉連線之前會看到所有已傳送的資料,但傳送方並不會收到任何這方面的指示。

如果傳送方需要知道接收方已收到所有資料,通常有兩種方式可以實現:

  1. 使用 gen_tcp:shutdown(Sock, write) 來發出不再傳送資料的訊號,並等待另一方確認其讀取端已關閉,方法是關閉其寫入端,這會在本端顯示為 socket 關閉。
  2. 在 TCP 之上的協定中實作確認機制,讓連線的兩端都遵守,以指示所有資料都已被看到。socket 選項 {packet, N} 可能會很有用。
連結到此函式

connect(SockAddr, Opts)

檢視原始碼 (自 OTP 24.3 起)
-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().

等同於 connect(SockAddr, Opts, infinity)

-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。

使用 AddressPort 參數

等同於 connect(Address, Port, Opts, infinity)

使用 SockAddr 參數(自 OTP 24.3 起)

連線到 SockAddr 指定的遠端監聽 socket,例如 socket:sockaddr_in6/0 允許指定連結本機 IPv6 位址的 scope_id

也允許使用相同的 map/0 格式的 IPv4 位址

除了目標位址的格式之外,等同於 connect/4

連結到此函式

connect(Address, Port, Opts, Timeout)

檢視原始碼
-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/0socket:sockaddr_in6/0,則此選項會優先於先前使用 ipport 選項設定的任何值。如果這些選項 (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

連結到此函式

controlling_process(Socket, Pid)

檢視原始碼
-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/0socket:sockaddr_in6/0,則此選項會優先於先前使用 ipport 選項設定的任何值。如果這些選項 (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().

等同於 recv(Socket, Length, infinity)

連結到此函式

recv(Socket, Length, Timeout)

檢視原始碼
-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 才有意義,且表示要讀取的位元組數。如果 Length0,則會傳回所有可用的位元組。如果 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/2send/2 時會返回 {error, closed}

如果對等方執行其寫入端的關閉,則選項 {exit_on_close, false} 非常有用。這樣一來,在接收指示 socket 已關閉後,socket 仍會保持開啟以進行寫入。

注意

非同步關閉寫入(How :: write | read_write)。

如果在網路驅動程式於背景傳送緩衝資料時嘗試關閉,則會延後關閉,直到傳送完所有緩衝的資料。該函數會立即返回 ok,並且不會通知呼叫者(關閉已延後)。

當使用 inet_backend = socket 時,行為會有所不同。使用 How :: write | read_write 的關閉會立即執行。