概述

  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选项就派上用场了!

Erlang模块gen_tcp翻译的更多相关文章

  1. Erlang模块erl翻译

    命令:     erl 概述:     Erlang模拟器 描述:     erl程序启动一个Erlang运行时系统.准确的信息是依赖于系统的(举例,erl是否是脚本或程序,其它程序调用).     ...

  2. Erlang模块file翻译

    模块摘要     文件接口模块   描述     模块file提供了文件系统的接口.     在具有线程支持的操作系统上,可以让文件操作以其自己的线程执行,从而允许其他Erlang进程与文件操作并行地 ...

  3. Erlang模块ets翻译

    概要: 内置的存储 描述: 这个模块是Erlang内置存储BIFs的接口.这些提供了在Erlang运行时系统中存储大量数据的能力,并且能够对数据进行持续的访问时间.(在ordered_set的情况下, ...

  4. Erlang模块inet翻译

    模块 inet 模块概述 访问TCP / IP协议. 描述 此模块提供对TCP/IP协议的访问. 另请参阅<ERTS用户指南:Inet配置>,以获取有关如何配置用于IP通信的Erlang运 ...

  5. Erlang模块gen_server翻译

    gen_server 概要: 通用服务器行为描述: 行为模块实现服务器的客户端-服务器关系.一个通用的服务器进程使用这个模块将实现一组标准的接口功能,包括跟踪和错误报告功能.它也符合OTP进程监控树. ...

  6. Erlang模块gen_fsm翻译

    模块摘要     通用有限状态机行为.   描述     用于实现有限状态机的行为模块.使用该模块实现的通用有限状态机进程(gen_fsm)将具有一组标准的接口函数,并包括用于跟踪和错误报告的功能.它 ...

  7. Erlang模块supervisor翻译

    概要: 通用监督者行为   描述: 一个实现监督者的行为模块,一个监督被称为子进程的其它进程的进程.一个子进程可以是另一个监督者或工作者进程.工作者进程通常的实现使用gen_event,gen_fsm ...

  8. [Erlang25]Erlang in anger 翻译

    Erlang in anger     Erlang in anger 是写Learn some Erlang的帅小伙(照片真是帅死啦)写的,一共87页,可以随意下载(英文原版):http://www ...

  9. ns3 Tutorial 中的日志模块(翻译)

      转载地址:http://blog.sina.com.cn/s/blog_8ecca79b0101d7fe.html     1  日志模块的使用   在运行 first.cc 脚本时,我们已经简单 ...

随机推荐

  1. [TCP/IP]DNS解析

    DNS解析主机的IP地址 host -t A www.baidu.com

  2. Linux curl 常用示例

    本篇文章包含了curl的常用案例使用. 如果想了解curl选项的详细说明,请参考前一篇文章「Linux curl 命令详解」. 常见网页访问示例 基本用法 访问一个网页 curl https://ww ...

  3. macos Mojave 出现网络出错 Frame Check Sequence: Bad checksum

    问题描述:使用软电话外呼的时候出现Request Timeout . 端口监听之后通过 Wireshark发现错误:`Frame Check Sequence: Bad checksum`,查看wir ...

  4. UML类图(1.3)

    UML:Unified modeling Language 统一建模语言 UML类图:用来描述系统所包含的类以及类之间的关系. 画图工具:https://www.processon.com 类之间的6 ...

  5. Web 字体 font-family 再探秘

    之前写过一篇关于Web字体简介及使用技巧的文章: 你该知道的字体 font-family. 该篇文章基本没有太多移动端的字体选择及分析.并且过了这么久,如今的 Web 字体又有了一些新的东西,遂有此文 ...

  6. 导入spark2.3.3源码至intellij idea

    检查环境配置 maven环境 2.检查scala插件 没有的话可以到https://plugins.jetbrains.com/plugin/1347-scala/versions 下载与idea对应 ...

  7. css布局之居中

    CSS布局之居中 本文主要是介绍水平居中,垂直居中,还有水平垂直居中的方法 水平居中 1.行内元素水平居中 使用text-align:center;就可以实现行内元素的水平居中,但是记得要在父元素中设 ...

  8. 《Java 8 in Action》Chapter 2:通过行为参数化传递代码

    你将了解行为参数化,这是Java 8非常依赖的一种软件开发模式,也是引入 Lambda表达式的主要原因.行为参数化就是可以帮助你处理频繁变更的需求的一种软件开发模式.一言以蔽之,它意味 着拿出一个代码 ...

  9. .net core 在 Docker 开发下的巨坑

    一,Docker 的安装 Windows 安装  Docker 官方文档: https://docs.microsoft.com/zh-cn/virtualization/windowscontain ...

  10. 【解决】TLS/SSLError问题导致无法使用pip或conda安装软件包

    Copy these files from the ./Library/bin to ./DLLs/ :libcrypto-1_1-x64.*libssl-1_1-x64.* 解决 欢迎关注↓↓↓ 头 ...