gen_tcp详解

该模块 提供一组基于tcp/ip协议 socket 网络通信方法。

gen_tcp:connect/3 gen_tcp:connect/4####

连接一个 TCP 端口

用法:
connect(Address, Port, Options, Timeout) -> {ok, Socket} | {error, Reason}
用给出的端口 Port 和 IP 地址 Address 连接到一个服务器上的 TCP 端口上。参数 Address 即可以是一个主机名,也可以是一个 IP 地址。

参数 Timeout 指定一个以毫秒为单位的超时值,默认值是 infinity。

{Rand, _RandSeed} = random:uniform_s(9999, erlang:now()),
Port = 40000 + Rand,
gen_tcp:connect("localhost", Port, [{active, false}, {packet, 0}], 5000).

Options选项:

gen_tcp:listen/2####

开启一个监听某个端口的套接字

用法:

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

在本地开启一个监听某个端口的套接字(socket)。开启成功的话,会返回一个套接字标识符 Socket,其一般会传递给 get_tcp:accept/1 或 get_tcp:accept/2 调用。

如果参数 Port 为 0,那么底层操作系统将赋值一个可用的端口号,可以使用 inet:port/1 来获取一个 socket 监听的端口。

参数 Options 的一些常用选项:

{active, true}:套接字设置为主动模式。所有套接字接收到的消息都作为 Erlang 消息转发到拥有这个套接字进程上。当开启一个套接字时,默认是主动模式。
{active, false}:设置套接字为被动模式。套接字收到的消息被缓存起来,进程必须通过调用函数 gen_tcp:recv/2 或 gen_tcp:recv/3 来读取这些消息。
{active, once}:将设置套接字为主动模式,但是一旦收到第一条消息,就将其设置为被动模式,并使用 gen_tcp:recv/2 或 gen_tcp:recv/3 函数来读取后续消息。
{keepalive, true}:当没有转移数据时,确保所连接的套接字发送保持活跃(keepalive)的消息。因为关闭套接字消息可能会丢失,如果没有接收到保持活跃消息的响应,那么该选项可确保这个套接字能被关闭。默认情况下,该标签是关闭的。
{nodelay, true}:数据包直接发送到套接字,不过它多么小。在默认情况下,此选项处于关闭状态,并且与之相反,数据被聚集而以更大的数据块进行发送。
{packet_size, Size}:设置数据包允许的最大长度。如果数据包比 Size 还大,那么将认为这个数据包无效。
{packet, 0}:表示 Erlang 系统会把 TCP 数据原封不动地直接传送给应用程序
{reuseaddr, true}:允许本地重复使用端口号
{nodelay, true}:意味着很少的数据也会被马上被发送出去
{delay_send, true}:数据不是立即发送,而是存到发送队列里,等 socket 可写的时候再发送
{backlog, 1024}:缓冲区的长度
{exit_on_close, false}:设置为 flase,那么 socket 被关闭之后还能将缓冲区中的数据发送出去
{send_timeout, 15000}:设置一个时间去等待操作系统发送数据,如果底层在这个时间段后还没发出数据,那么就会返回 {error,timeout}

{Rand, _RandSeed} = random:uniform_s(9999, erlang:now()),  
Port = 40000 + Rand,  
gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]).  

gen_tcp:accept/1 gen_tcp:accept/2
接受一个发送到监听套接字 ListenSocket 上的连接请求

用法:

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

接受一个发送到监听套接字 ListenSocket 上的连接请求。ListenSocket 必须是由函数 gen_tcp:listen/2 建立返回。

该函数会引起进程阻塞,直到有一个连接请求发送到监听的套接字。

如果连接已建立,则返回 {ok,Socket};或如果 ListenSocket 已经关闭,则返回{error,closed};或如果在指定的时间内连接没有建立,则返回{error,timeout};或如果 Erlang 虚拟机里可用的端口都被使用了,则返回 {error, system_limit};如果某些东西出错,也可能返回一个 POSIX 错误。一些有可能的错误请查看 inet 模块的相关说明。

使用 gen_tcp:send/2 向该函数返回的套接字 Socket 发送数据包。往端口发送的数据包会以下面格式的消息发送:
{tcp, Socket, Data}
如果在建立套接字 Socket 的时候选项列表中指定了 {active,false},这样就只能使用 gen_tcp:recv/2 或 gen_tcp:recv/3 来接收数据包了。

{Rand, _RandSeed} = random:uniform_s(9999, erlang:now()),
Port = 40000 + Rand,
case gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]) of
    {ok, ListenSocket} ->
        case gen_tcp:accept(ListenSocket) of
            {ok, Socket} ->
                Socket;
            {error, SocketAcceptFail} ->
                SocketAcceptFail
        end;
    _ ->
        socket_listen_fail
end.

gen_tcp:recv/3####

从一个被动模式的套接字接受一个数据包

用法:
recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason}
这个函数是从一个被动模式的套接字接受一个数据包。如果返回一个 {error, closed} 的返回值,那表明 Socket 已经关闭。

当 Socket 是 raw 模式下,参数 Length 才有意义的,并且 Length 表示接收字节的大小。如果 Length = 0,所有有效的字节数据都会被接收。如果 Length > 0,则只会接收 Length 长度的字节,或发生错误;当另一端 Socket 关闭时,接收的数据长度可能会小于 Length。

选项 Timeout 是一个以毫秒为单位的超时值,默认值是 infinity。

{Rand, _RandSeed} = random:uniform_s(9999, erlang:now()),
Port = 40000 + Rand,
case gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]) of
    {ok, ListenSocket} ->
        case gen_tcp:accept(ListenSocket) of
            {ok, Socket} ->
                gen_tcp:recv(Socket, 0, 5000);
            {error, SocketAcceptFail} ->
                SocketAcceptFail
        end;
    _ ->
        socket_listen_fail
end.

gen_tcp:send/2####

在一个套接字 Socket 发送一个数据包
用法:

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

在一个套接字 Socket 发送一个数据包。

{Rand, _RandSeed} = random:uniform_s(9999, erlang:now()),
Port = 40000 + Rand,
case gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]) of
    {ok, ListenSocket} ->
        case gen_tcp:accept(ListenSocket, 1500) of
            {ok, Socket} ->
                gen_tcp:send(Socket, "test!");
            {error, SocketAcceptFail} ->
                SocketAcceptFail
        end;
    _ ->
        socket_listen_fail
end.

gen_tcp:shutdown/2####

半关闭一个套接字

用法:
shutdown(Socket, How) -> ok | {error, Reason}
内部实现:

-spec shutdown(Socket, How) -> ok | {error, Reason} when
      Socket :: socket(),
      How :: read | write | read_write,
      Reason :: inet:posix().
 
shutdown(S, How) when is_port(S) ->
    case inet_db:lookup_socket(S) of
    {ok, Mod} ->
        Mod:shutdown(S, How);
    Error ->
        Error
    end.

以某种方式半关闭一个套接字。

如果参数 How 为 write 的形式,则套接字 socket 会关闭数据写入,读取仍可以正常执行。参数 How 为 read,则反之。

要实现套接字半打开, 那么套接字要设置 {exit_on_close, false} 这个参数。

{Rand, _RandSeed} = random:uniform_s(9999, erlang:now()),
Port = 40000 + Rand,
case gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]) of
    {ok, ListenSocket} ->
        case gen_tcp:accept(ListenSocket, 1500) of
            {ok, Socket} ->
                inet:setopts(Socket, [{exit_on_close, false}]),
                gen_tcp:shutdown(Socket, write);
            {error, SocketAcceptFail} ->
                SocketAcceptFail
        end;
    _ ->
        socket_listen_fail
end.

gen_tcp:controlling_process/2####

改变一个套接字的控制进程

用法:
gen_tcp:controlling_process(Socket, Pid) -> ok | {error, Reason}
为 Socket 分配一个新的控制进程 Pid。控制进程就是接收发自套接字消息数据的进程。如果被当前控制进程以外的其他任何进程调用,则会返回 {error, not_owner} 的错误。

{Rand, _RandSeed} = random:uniform_s(9999, erlang:now()),
Port = 40000 + Rand,
case gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}]) of
    {ok, Socket} ->
        gen_tcp:controlling_process(Socket, self());
    _ ->
        socket_listen_fail
end.

gen_tcp:close/1####

关闭一个 TCP 套接字
用法:

  gen_tcp:close(Socket) -> ok  

关闭一个 TCP 套接字。

你可能感兴趣的:(gen_tcp详解)