概述

  TCP/IP套接字接口

描述

  gen_tcp模块提供了使用TCP / IP协议与套接字进行通信的功能。

  以下代码片段提供了一个客户端连接到端口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),

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.

  有关更多示例,请参阅示例部分。

数据类型

option() = {active, true | false | once}
         | {buffer, integer() >= 0}
         | {delay_send, boolean()}
         | {deliver, port | term}
         | {dontroute, boolean()}
         | {exit_on_close, boolean()}
         | {header, integer() >= 0}
         | {high_msgq_watermark, integer() >= 1}
         | {high_watermark, integer() >= 0}
         | {keepalive, boolean()}
         | {linger, {boolean(), integer() >= 0}}
         | {low_msgq_watermark, integer() >= 1}
         | {low_watermark, integer() >= 0}
         | {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, integer() >= 0}
         | {priority, integer() >= 0}
         | {raw,
            Protocol :: integer() >= 0,
            OptionNum :: integer() >= 0,
            ValueBin :: binary()}
         | {recbuf, integer() >= 0}
   | {reuseaddr, boolean()}
         | {send_timeout, integer() >= 0 | infinity}
         | {send_timeout_close, boolean()}
         | {sndbuf, integer() >= 0}
         | {tos, integer() >= 0}
         | {ipv6_v6only, boolean()}

option_name() = active
              | buffer
              | delay_send
              | deliver
              | dontroute
              | exit_on_close
              | header
              | high_msgq_watermark
              | high_watermark
              | keepalive
              | linger
              | low_msgq_watermark
              | low_watermark
              | mode
              | nodelay
              | packet
              | packet_size
              | priority
              | {raw,
                 Protocol :: integer() >= 0,
                 OptionNum :: integer() >= 0,
                 ValueSpec :: (ValueSize :: integer() >= 0)
                            | (ValueBin :: binary())}
              | recbuf
              | reuseaddr
              | send_timeout
              | send_timeout_close
              | sndbuf
              | tos
              | ipv6_v6only

connect_option() = {ip, inet:ip_address()}
                 | {fd, Fd :: integer() >= 0}
                 | {ifaddr, inet:ip_address()}
                 | inet:address_family()
                 | {port, inet:port_number()}
                 | {tcp_module, module()}
                 | option()

listen_option() = {ip, inet:ip_address()}
                | {fd, Fd :: integer() >= 0}
                | {ifaddr, inet:ip_address()}
                | inet:address_family()
                | {port, inet:port_number()}
                | {backlog, B :: integer() >= 0}
                | {tcp_module, module()}
                | option()

socket()

  由accept/ 1,2和connect/ 3,4返回。

导出

connect(Address, Port, Options) -> {ok, Socket} | {error, Reason}
connect(Address, Port, Options, Timeout) -> {ok, Socket} | {error, Reason}

  Types:

    Address = inet:ip_address() | inet:hostname()

    Port = inet:port_number()

    Options = [connect_option()]

    Timeout = timeout()

    Socket = socket()

    Reason = inet:posix()

  连接到IP地址为Address的主机上的TCP端口Port上的服务器。 Address参数可以是主机名或IP地址。

  {ip, ip_address()}

  如果主机有多个网络接口,则此选项指定要使用哪一个。

  {ifaddr, ip_address()}

  与{ip, ip_address()}相同。 如果主机有多个网络接口,则此选项指定要使用哪一个。

  {fd, integer() >= 0}

  如果某个套接字在不使用gen_tcp的情况下以某种方式连接,请使用此选项为其传递文件描述符。

  inet

  设置IPv4的套接字。

  inet6

  设置IPv6的套接字。

  {port, Port}

  指定要使用的本地端口号。

  {tcp_module, module()}

  覆盖使用哪个回调模块。 默认为IPv4的inet_tcp和IPv6的inet6_tcp。

  Opt

  参见 inet:setopts/2.

  可以使用send / 2将数据包发送到返回的套接字Socket。 从对等方发送的数据包将作为消息发送:

  {tcp, Socket, Data}

  如果套接字已关闭,则会传递以下消息:

  {tcp_closed, Socket}

  如果套接字上发生错误,则传递以下消息:

  {tcp_error, Socket, Reason}

  除非在套接字的选项列表中指定{active,false},在这种情况下,通过调用recv/ 2来检索数据包。

  可选的Timeout参数指定以毫秒为单位的超时。 默认值是无穷大。

注意:

  给予连接的选项的默认值可能受内核配置参数inet_default_connect_options的影响。 有关详细信息,请参阅inet(3)。

listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}

  Types:

    Port = inet:port_number()

    Options = [listen_option()]

    ListenSocket = socket()

    Reason = system_limit | inet:posix()

  设置套接字以侦听本地主机上的端口Port。

  如果Port== 0,则底层操作系统会分配一个可用端口号,请使用inet:port/1来检索它。

  可用的选项是:

  list

  接收到的数据包作为列表提供。

  binary

  接收到的数据包以二进制形式提供。

  {backlog, B}

  B是>= 0的整数。backlog值默认为5。backlog值定义待处理连接队列可能增长到的最大长度。

  {ip, ip_address()}

  如果主机有多个网络接口,则此选项指定要监听哪个接口。

  {port, Port}

  指定要使用的本地端口号。

  {fd, Fd}

  如果某个套接字在不使用gen_tcp的情况下以某种方式连接,请使用此选项为其传递文件描述符。

  {ifaddr, ip_address()}

  与{ip,ip_address()}相同。 如果主机有多个网络接口,则此选项指定要使用哪一个。

  inet

  设置IPv4的套接字。

  inet6

  设置IPv6的套接字。

  {tcp_module, module()}

  覆盖使用哪个回调模块。 默认为IPv4的inet_tcp和IPv6的inet6_tcp。

  Opt

  参见 inet:setopts/2

  返回的套接字ListenSocket只能用于accept/1,2的调用。

注意:

  监听选项的默认值可能受内核配置参数inet_default_listen_options的影响。有关详细信息,请参阅inet(3)。

accept(ListenSocket) -> {ok, Socket} | {error, Reason}
accept(ListenSocket, Timeout) -> {ok, Socket} | {error, Reason}

  Types:

    ListenSocket = socket()

    listen/2返回。

    Timeout = timeout()

    Socket = socket()

    Reason = closed | timeout | system_limit | inet:posix()

  在侦听套接字上接受传入的连接请求。套接字必须是从listen / 2返回的套接字。 超时以ms为单位指定超时值,默认为无穷大。

  如果连接已建立,则返回{ok,Socket};如果ListenSocket已关闭,则返回{error,closed};如果在指定的时间内未建立连接,则返回{error,timeout};如果所有可用端口都处于连接状态,则返回{error,system_limit} 。 如果出现其他问题,也可能返回一个POSIX错误值,请参阅inet(3)了解可能的错误值。

  可以使用send/2将数据包发送到返回的套接字Socket。从对等方发送的数据包将作为消息发送:

  {tcp, Socket, Data}

  除非在侦听套接字的选项列表中指定了{active,false},在这种情况下,通过调用recv/2来检索数据包。

注意:

  值得注意的是,接受调用不必从套接字所有者进程发出。 使用仿真器5.5.3及更高版本,可以从不同进程发出多个同时接受调用,这允许接收器进程池处理传入连接。

send(Socket, Packet) -> ok | {error, Reason}

  Types:

    Socket = socket()

    Packet = iodata()

    Reason = closed | inet:posix()

  在套接字上发送数据包。

  发送调用没有超时选项,如果需要超时,可以使用send_timeout套接字选项。请参阅示例部分。

recv(Socket, Length) -> {ok, Packet} | {error, Reason}
recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason}

  Types:

    Socket = socket()

    Length = integer() >= 0

    Timeout = timeout()

    Packet = string() | binary() | HttpPacket

    Reason = closed | inet:posix()

    HttpPacket = term()

  请参阅erlang中的HttpPacket说明:decode_packet/3。.

  该函数以被动模式从套接字接收数据包。关闭的套接字由返回值{error,closed}表示。

  Length参数仅在套接字处于原始模式时才有意义,并且表示要读取的字节数。 如果Length = 0,则返回所有可用的字节。 如果长度> 0,则返回确切的长度字节或错误; 当套接字从另一端关闭时可能丢弃少于Length数据的字节数据。

  可选的Timeout参数指定以毫秒为单位的超时。默认值是无穷大。

controlling_process(Socket, Pid) -> ok | {error, Reason}

  Types:

    Socket = socket()

    Pid = pid()

    Reason = closed | not_owner | inet:posix()

  为Socket分配一个新的控制进程Pid。 控制过程是从套接字接收消息的过程。 如果被当前控制进程以外的任何其他进程调用,则返回{error,not_owner}。

close(Socket) -> ok

  Types:

    Socket = socket()

  关闭TCP套接字。

shutdown(Socket, How) -> ok | {error, Reason}

  Types:

    Socket = socket()

    How = read | write | read_write

    Reason = inet:posix()

  立即关闭一个或两个方向的套接字。

  How==write意味着关闭写入套接字,从它读取仍然是可能的。

  为了能够处理对端在写入端执行关闭操作,{exit_on_close,false}选项很有用。

例子

  以下示例通过将服务器实现为在单个侦听套接字上进行接受的多个工作进程来说明{active,once}选项和多个接受的用法。 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_timeout处理的。没有接收器的发送操作的行为在很大程度上由底层TCP堆栈以及网络基础结构定义。 如果想编写处理挂起的接收器的代码,最终可能会导致发送者挂起发送调用,则可以编写如下代码。

考虑一个从客户端进程接收数据的进程,该进程将被转发到网络上的服务器。该进程已通过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选项就派上用场了!

最新文章

  1. .NET平台开源项目速览(16)C#写PDF文件类库PDF File Writer介绍
  2. eclipse中启动调试maven构建的javaweb项目
  3. 企业IT管理员IE11升级指南【3】—— IE11 新的GPO设置
  4. 一起学习jQuery2.0.3源码—1.开篇
  5. C++的模板特化 和 STL中iterator_traits模板的偏特化
  6. 19 图形用户界面编程 - 《Python 核心编程》
  7. 【每日scrum】NO.6
  8. DevExpress Report 其他常用设计技巧
  9. Hadoop - 操作练习之单机配置 - Hadoop2.8.0/Ubuntu16.04
  10. shell编程之环境变量配置文件(4)
  11. Python3+unitest自动化测试初探(下篇)
  12. SVG制作可爱小页面
  13. window.open 和showModalDialog的返回值
  14. MQ的订阅模式
  15. 我的Java自学之路
  16. NIO学习笔记五:Buffer 的使用
  17. free命令的具体含义
  18. Beta阶段第1周/共2周 Scrum立会报告+燃尽图 05
  19. Codeforces 417D Cunning Gena(状态压缩dp)
  20. JKS与P12证书互转

热门文章

  1. cogs 1317. 数列操作C 区间修改 区间查询
  2. Docker:跨主机通信
  3. 【原创】JAVA进程突然消失的原因?
  4. JAVA基础知识(九)Java 异常
  5. 关于Linux的简单介绍
  6. 安装VMware14虚拟机,centos7版本的linux 软件地址
  7. 编译Assimp傻瓜教程
  8. Go_笔试题记录-指针与值类型实现接口的区别
  9. restapi(5)- rest-mongo 应用实例:分布式图片管理系统之一,rest 服务
  10. C#中appium自动化执行移动命令mobile:shell用法