Go to main content

man pages section 3: Extended Library Functions, Volume 1

Exit Print View

Updated: Wednesday, July 27, 2022
 
 

gen_tcp (3erl)

Name

gen_tcp - Interface to TCP/IP sockets.

Synopsis

Please see following description for synopsis

Description

gen_tcp(3)                 Erlang Module Definition                 gen_tcp(3)



NAME
       gen_tcp - Interface to TCP/IP sockets.

DESCRIPTION
       This module provides functions for communicating with sockets using the
       TCP/IP protocol.

       The following code fragment is a simple example of a client  connecting
       to  a  server at port 5678, transferring a binary, and closing the con-
       nection:

       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).

       At the other end, a server is listening on port 5678, accepts the  con-
       nection, and receives the binary:

       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.

       For more examples, see section Examples.

   Note:
       Functions  that create sockets can take an optional option; {inet_back-
       end, Backend} that, if specified, has to  be  the  first  option.  This
       selects the implementation backend towards the platform's socket API.

       This is a temporary option that will be ignored in a future release.

       The  default  is Backend = inet that selects the traditional inet_drv.c
       driver. The other choice is Backend  =  socket  that  selects  the  new
       socket module and its NIF implementation.

       The  system  default  can  be changed when the node is started with the
       application kernel's configuration variable inet_backend.

       For gen_tcp with inet_backend = socket we have tried to be as "compati-
       ble" as possible which has sometimes been impossible. Here is a list of
       cases when the behaviour of inet-backend inet (default) and socket  are
       different:

         * Non-blocking send

           If a user calling gen_tcp:send/2 with inet_backend = inet, tries to
           send more data than there is room for in the OS buffers, the  "rest
           data"  is  buffered by the inet driver (and later sent in the back-
           ground). The effect for the user is that the call is non-blocking.

           This is not the effect when inet_backend = socket, since  there  is
           no buffering. Instead the user hangs either until all data has been
           sent or the send_timeout timeout has been reached.

         * Remote close detected by background send.

           An background send will detect  a  'remote  close'  and  (the  inet
           driver will) mark the socket as 'closed'. No other action is taken.
           If the socket has active set to false (passive) at this  point  and
           no  one  is  reading,  this will not be noticed. But as soon as the
           socket is "activated" (active set to not false, send/2 is called or
           recv/2,3 is called), an error message will be sent to the caller or
           (socket) owner: {tcp_error, Socket, econnreset}. Any data in the OS
           receive buffers will be lost!

           This  behaviour  is  not replicated by the socket implementation. A
           send operation will detect a remote close  and  immediately  return
           this  to the caller, but do nothing else. A reader will therefor be
           able to extract any data from the OS buffers. If the socket is  set
           to  active  to  not  false,  the  data will be received as expected
           ({tcp, ...} and then a closed message ({tcp_closed,  ...}  will  be
           received (not an error).

         * The  option show_econnreset basically do not work as described when
           used with inet_backend = socket. The "issue" is that a remote close
           (as  described  above)  do allow a reader to extract what is in the
           read buffers before a close is "delivered".

         * The option nodelay is a TCP specific option that is not  compatible
           with domain = local.

           When  using  inet_backend  = socket, trying to create a socket (via
           listen or connect) with domain = local  (for  example  with  option
           {ifaddr, {local,"/tmp/test"}}) will fail with {error, enotsup}.

           This  does not actually work for inet_backend = inet either, but in
           that case the error is simply ignored, which is a bad idea. We have
           choosen to not ignore this error for inet_backend = socket.

         * Async shutdown write

           Calling  gen_tcp:shutdown(Socket,  write  | read_write) on a socket
           created with inet_backend =  socket  will  take  immediate  effect,
           unlike for a socket created with inet_backend = inet.

           See async shutdown write for more info.

DATA TYPES
       option() =
           {active, true | false | once | -32768..32767} |
           {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()} |
           {show_econnreset, boolean()} |
           {sndbuf, integer() >= 0} |
           {tos, integer() >= 0} |
           {tclass, integer() >= 0} |
           {ttl, integer() >= 0} |
           {recvtos, boolean()} |
           {recvtclass, boolean()} |
           {recvttl, boolean()} |
           {ipv6_v6only, boolean()}

       pktoptions_value() = {pktoptions, inet:ancillary_data()}

              If the platform implements the IPv4 option IP_PKTOPTIONS, or the
              IPv6  option  IPV6_PKTOPTIONS  or  IPV6_2292PKTOPTIONS  for  the
              socket  this  value  is returned from inet:getopts/2 when called
              with the option name pktoptions.

          Note:
              This option appears to be VERY Linux specific, and its existence
              in  future  Linux  kernel  versions  is  also worrying since the
              option is part of RFC 2292 which is since long (2003)  obsoleted
              by  RFC  3542  that  explicitly  removes this possibility to get
              packet information from a stream socket. For comparision: it has
              existed  in  FreeBSD  but is now removed, at least since FreeBSD
              10.


       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 |
           show_econnreset | sndbuf | tos | tclass | ttl | recvtos |
           recvtclass | recvttl | pktoptions | ipv6_v6only

       connect_option() =
           {ip, inet:socket_address()} |
           {fd, Fd :: integer() >= 0} |
           {ifaddr, inet:socket_address()} |
           inet:address_family() |
           {port, inet:port_number()} |
           {tcp_module, module()} |
           {netns, file:filename_all()} |
           {bind_to_device, binary()} |
           option()

       listen_option() =
           {ip, inet:socket_address()} |
           {fd, Fd :: integer() >= 0} |
           {ifaddr, inet:socket_address()} |
           inet:address_family() |
           {port, inet:port_number()} |
           {backlog, B :: integer() >= 0} |
           {tcp_module, module()} |
           {netns, file:filename_all()} |
           {bind_to_device, binary()} |
           option()

       socket()

              As returned by accept/1,2 and connect/3,4.

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

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

              Types:

                 ListenSocket = socket()
                   Returned by listen/2.
                 Timeout = timeout()
                 Socket = socket()
                 Reason = closed | timeout | system_limit | inet:posix()

              Accepts an incoming connection request on  a  listening  socket.
              Socket  must  be a socket returned from listen/2. Timeout speci-
              fies a time-out value in milliseconds. Defaults to infinity.

              Returns:

                * {ok, Socket} if a connection is established

                * {error, closed} if ListenSocket is closed

                * {error, timeout} if no connection is established within  the
                  specified time

                * {error,  system_limit}  if all available ports in the Erlang
                  emulator are in use

                * A POSIX error  value  if  something  else  goes  wrong,  see
                  inet(3) for possible error values

              Packets  can be sent to the returned socket Socket using send/2.
              Packets sent from the peer are  delivered  as  messages  (unless
              {active,  false} is specified in the option list for the listen-
              ing socket, in which  case  packets  are  retrieved  by  calling
              recv/2):

              {tcp, Socket, Data}

          Note:
              The accept call does not have to be issued from the socket owner
              process. Using version 5.5.3 and higher of the emulator,  multi-
              ple  simultaneous accept calls can be issued from different pro-
              cesses, which allows for a pool of acceptor  processes  handling
              incoming connections.


       close(Socket) -> ok

              Types:

                 Socket = socket()

              Closes a TCP socket.

              Note that in most implementations of TCP, doing a close does not
              guarantee that any data  sent  is  delivered  to  the  recipient
              before  the close is detected at the remote side. If you want to
              guarantee delivery of the data to the recipient  there  are  two
              common ways to achieve this.

                * Use  gen_tcp:shutdown(Sock,  write)  to  signal that no more
                  data is to be sent and wait for the read side of the  socket
                  to be closed.

                * Use  the socket option {packet, N} (or something similar) to
                  make it possible for the receiver to  close  the  connection
                  when it knowns it has received all the data.

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

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

              Types:

                 Address = inet:socket_address() | inet:hostname()
                 Port = inet:port_number()
                 Options = [inet:inet_backend() | connect_option()]
                 Timeout = timeout()
                 Socket = socket()
                 Reason = timeout | inet:posix()

              Connects  to  a  server  on  TCP  port  Port on the host with IP
              address Address. Argument Address can be a  hostname  or  an  IP
              address.

              The following options are available:

                {ip, Address}:
                  If  the host has many network interfaces, this option speci-
                  fies which one to use.

                {ifaddr, Address}:
                  Same as {ip, Address}. If the host has many  network  inter-
                  faces, this option specifies which one to use.

                {fd, integer() >= 0}:
                  If  a  socket  has  somehow  been  connected  without  using
                  gen_tcp, use this option to pass the file descriptor for it.
                  If  {ip,  Address}  and/or {port, port_number()} is combined
                  with this option, the fd is bound to the specified interface
                  and  port before connecting. If these options are not speci-
                  fied, it is assumed that the fd is already  bound  appropri-
                  ately.

                inet:
                  Sets up the socket for IPv4.

                inet6:
                  Sets up the socket for IPv6.

                local:
                  Sets up a Unix Domain Socket. See inet:local_address()

                {port, Port}:
                  Specifies which local port number to use.

                {tcp_module, module()}:
                  Overrides   which  callback  module  is  used.  Defaults  to
                  inet_tcp for IPv4 and inet6_tcp for IPv6.

                Opt:
                  See inet:setopts/2.

              Packets can be sent to the returned socket Socket using  send/2.
              Packets sent from the peer are delivered as messages:

              {tcp, Socket, Data}

              If  the  socket  is  in {active, N} mode (see inet:setopts/2 for
              details) and its message counter drops to 0, the following  mes-
              sage  is  delivered to indicate that the socket has transitioned
              to passive ({active, false}) mode:

              {tcp_passive, Socket}

              If the socket is closed, the following message is delivered:

              {tcp_closed, Socket}

              If an error occurs on  the  socket,  the  following  message  is
              delivered  (unless  {active,  false}  is specified in the option
              list for the socket, in which  case  packets  are  retrieved  by
              calling recv/2):

              {tcp_error, Socket, Reason}

              The optional Timeout parameter specifies a time-out in millisec-
              onds. Defaults to infinity.

          Note:
              Keep in mind that if the underlying OS connect() call returns  a
              timeout,  gen_tcp:connect  will  also  return  a  timeout  (i.e.
              {error, etimedout}), even if a larger Timeout was specified.


          Note:
              The default values for  options  specified  to  connect  can  be
              affected by the Kernel configuration parameter inet_default_con-
              nect_options. For details, see inet(3).


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

              Types:

                 Socket = socket()
                 Pid = pid()
                 Reason = closed | not_owner | badarg | inet:posix()

              Assigns a new controlling process Pid to Socket. The controlling
              process  is  the process that receives messages from the socket.
              If called by any other  process  than  the  current  controlling
              process,  {error, not_owner} is returned. If the process identi-
              fied by Pid is not an existing local  pid,  {error,  badarg}  is
              returned.  {error,  badarg}  may  also be returned in some cases
              when Socket is closed during the execution of this function.

              If the socket is set in active mode, this function will transfer
              any messages in the mailbox of the caller to the new controlling
              process. If any other process is  interacting  with  the  socket
              while  the transfer is happening, the transfer may not work cor-
              rectly and messages may remain  in  the  caller's  mailbox.  For
              instance changing the sockets active mode before the transfer is
              complete may cause this.

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

              Types:

                 Port = inet:port_number()
                 Options = [inet:inet_backend() | listen_option()]
                 ListenSocket = socket()
                 Reason = system_limit | inet:posix()

              Sets up a socket to listen on port Port on the local host.

              If Port == 0, the underlying OS assigns an available  port  num-
              ber, use inet:port/1 to retrieve it.

              The following options are available:

                list:
                  Received Packet is delivered as a list.

                binary:
                  Received Packet is delivered as a binary.

                {backlog, B}:
                  B  is an integer >= 0. The backlog value defines the maximum
                  length that the queue of pending connections  can  grow  to.
                  Defaults to 5.

                {ip, Address}:
                  If  the host has many network interfaces, this option speci-
                  fies which one to listen on.

                {port, Port}:
                  Specifies which local port number to use.

                {fd, Fd}:
                  If  a  socket  has  somehow  been  connected  without  using
                  gen_tcp, use this option to pass the file descriptor for it.

                {ifaddr, Address}:
                  Same  as  {ip, Address}. If the host has many network inter-
                  faces, this option specifies which one to use.

                inet6:
                  Sets up the socket for IPv6.

                inet:
                  Sets up the socket for IPv4.

                {tcp_module, module()}:
                  Overrides  which  callback  module  is  used.  Defaults   to
                  inet_tcp for IPv4 and inet6_tcp for IPv6.

                Opt:
                  See inet:setopts/2.

              The  returned  socket  ListenSocket  should  be used in calls to
              accept/1,2 to accept incoming connection requests.

          Note:
              The default values  for  options  specified  to  listen  can  be
              affected by the Kernel configuration parameter inet_default_lis-
              ten_options. For details, see inet(3).


       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 | timeout | inet:posix()
                 HttpPacket = term()
                   See the description of HttpPacket in erlang:decode_packet/3
                   in ERTS.

              Receives a packet from a socket in passive mode. A closed socket
              is indicated by return value {error, closed}.

              Argument Length is only meaningful when the  socket  is  in  raw
              mode  and  denotes  the number of bytes to read. If Length is 0,
              all available bytes are returned. If Length > 0, exactly  Length
              bytes  are  returned, or an error; possibly discarding less than
              Length bytes of data when the socket is closed  from  the  other
              side.

              The optional Timeout parameter specifies a time-out in millisec-
              onds. Defaults to infinity.

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

              Types:

                 Socket = socket()
                 Packet = iodata()
                 Reason = closed | {timeout, RestData} | inet:posix()
                 RestData = binary()

              Sends a packet on a socket.

              There is no send call with a time-out option, use socket  option
              send_timeout if time-outs are desired. See section Examples.

              The  return  value  {error,  {timeout,  RestData}}  can  only be
              returned when inet_backend = socket.

          Note:
              Non-blocking send.

              If the user tries to send more data than there is  room  for  in
              the  OS  send buffers, the 'rest data' is put into (inet driver)
              internal buffers and later sent in the background. The  function
              immediately returns ok (not informing the caller that not all of
              the data was actually sent). Any issue while sending  the  'rest
              data' is maybe returned later.

              When  using  inet_backend  = socket, the behaviour is different.
              There is no buffering done (like the inet-driver does),  instead
              the  caller  will  "hang" until all of the data has been sent or
              send timeout (as specified by the send_timeout  option)  expires
              (the  function  can  hang  even when using 'inet' backend if the
              internal buffers are full).

              If this happens when using packet =/= raw,  we  have  a  partial
              package  written.  A new package therefor must not be written at
              this point, as there is no way for the peer to distinguish  this
              from the data portion of the current package. Instead, set pack-
              age to raw, send the rest data (as raw data) and then set  pack-
              age to the wanted package type again.


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

              Types:

                 Socket = socket()
                 How = read | write | read_write
                 Reason = inet:posix()

              Closes a socket in one or two directions.

              How  == write means closing the socket for writing, reading from
              it is still possible.

              If How == read or there is no  outgoing  data  buffered  in  the
              Socket  port,  the socket is shut down immediately and any error
              encountered is returned in Reason.

              If there is data buffered in the socket  port,  the  attempt  to
              shutdown  the  socket is postponed until that data is written to
              the kernel socket send buffer. If any  errors  are  encountered,
              the socket is closed and {error, closed} is returned on the next
              recv/2 or send/2.

              Option {exit_on_close, false} is useful if the peer has  done  a
              shutdown on the write side.

          Note:
              Async shutdown write (write or read_write).

              If the shutdown attempt is made while the inet-driver is sending
              buffered data in the background, the shutdown is postponed until
              all  buffered  data  has  been  sent.  The  function immediately
              returns ok and the caller is not informed (that the shutdown has
              not yet been performed).

              When  using inet_backend = socket, the behaviour is different. A
              shutdown with How == write | read_write, the operation will take
              immediate  effect (unlike the inet-driver, which basically saves
              the operation for later).


EXAMPLES
       The following example illustrates use of option {active,once} and  mul-
       tiple  accepts by implementing a server as a number of worker processes
       doing accept on a single listening socket. Function start/2  takes  the
       number  of  worker processes and the port number on which to listen for
       incoming connections. If LPort is specified as  0,  an  ephemeral  port
       number is used, which is why the start function returns the actual port
       number allocated:

       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.

       Example of a simple client:

       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.

       The send call does not accept a time-out option  because  time-outs  on
       send  is  handled through socket option send_timeout. The behavior of a
       send operation with no receiver is mainly defined by the underlying TCP
       stack  and  the  network  infrastructure.  To write code that handles a
       hanging receiver that can eventually cause the sender to hang on a send
       do like the following.

       Consider  a process that receives data from a client process to be for-
       warded to a server on the network. The  process  is  connected  to  the
       server through TCP/IP and does not get any acknowledge for each message
       it sends, but has to rely on the send time-out option  to  detect  that
       the  other  end  is  unresponsive. Option send_timeout can be used when
       connecting:

       {ok,Sock} = gen_tcp:connect(HostAddress, Port,
                                   [{active,false},
                                    {send_timeout, 5000},
                                    {packet,2}]),
                       loop(Sock), % See below

       In the loop where requests are  handled,  send  time-outs  can  now  be
       detected:

       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.

       Usually  it  suffices to detect time-outs on receive, as most protocols
       include some sort of acknowledgment from the server, but if the  proto-
       col is strictly one way, option send_timeout comes in handy.



Ericsson AB                       kernel 8.2                        gen_tcp(3)