欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

TCP网络编程那些事

程序员文章站 2022-05-06 16:52:12
...

网络编程socket之bind函数

摘要 :在套接口中,一个套接字只是用户程序与内核交互信息的枢纽,它自身没有太多的信息,也没有网络协议地址和 端口号等信息,在进行网络通信的时候,必须把一个套接字与一个地址相关联,这个过程就是地址绑定的过程。许多时候内核会我们自动绑定一个地址,然而有时用 户可能需要自己来完成这个绑定的过程,以满足实际应用的需要,最典型的情况是一个服务器进程需要绑定一个众所周知的地址或端口以等待客户来连接。这个事由 bind的函数完成。

从bind函数功能我们很容易推测出这个函数的需要的参数与相应的返回值,如果此时大家已经对socket接口有点熟悉了:
#include<sys/socket.h>
int bind( int sockfd, struct sockaddr* addr, socklen_t addrlen)
返回:0 ──成功, - 1 ──失败

参数sockfd
指定地址与哪个套接字绑定,这是一个由之前的socket函数调用返回的套接字。调用bind的函数之后,该套接字与一个相应的地址关联,发送到这个地址的数据可以通过这个套接字来读取与使用。
参数addr
指定地址。这是一个地址结构,并且是一个已经经过填写的有效的地址结构。调用bind之后这个地址与参数sockfd指定的套接字关联,从而实现上面所说的效果。
参数addrlen
正如大多数socket接口一样,内核不关心地址结构,当它复制或传递地址给驱动的时候,它依据这个值来确定需要复制多少数据。这已经成为socket接口中最常见的参数之一了。

bind函数并不是总是需要调用的,只有用户进程想与一个具体的地址或端口相关联的时候才需要调用这个函数。如果用户进程没有这个需要,那么程序可 以依赖内核的自动的选址机制来完成自动地址选择,而不需要调用bind的函数,同时也避免不必要的复杂度。在一般情况下,对于服务器进程问题需要调用 bind函数,对于客户进程则不需要调用bind函数。

bind函数

int bind(
SOCKET s,
const struct sockaddr FAR *name,
int namelen
);

当创建了一个Socket以后,套接字数据结构中有一个默认的IP地址和默认的端口号。一个服务程序必须调用bind函数来给其绑定一个IP地址和 一个特定的端口号。客户程序一般不必调用bind函数来为其Socket绑定IP地址和断口号。该函数的第一个参数指定待绑定的Socket描述符;第二 个参数指定一个sockaddr结构,该结构是这样定义的:
struct sockaddr {
u_short sa_family;
char sa_data[14];
};

其中sin_family置AF_INET;sin_port指明端口号;sin_addr结构体中只有一个唯一的字段s_addr,表示IP地 址,该字段是一个整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsigned long型的整数值后再置给s_addr。有的服务器是多宿主机,至少有两个网卡,那么运行在这样的服务器上的服务程序在为其Socket绑定IP地址时 可以把htonl(INADDR_ANY)置给s_addr,这样做的好处是不论哪个网段上的客户程序都能与该服务程序通信;如果只给运行在多宿主机上的 服务程序的Socket绑定一个固定的IP地址,那么就只有与该IP地址处于同一个网段上的客户程序才能与该服务程序通信。我们用0来填充 sin_zero数组,目的是让sockaddr_in结构的大小与sockaddr结构的大小一致。下面是一个bind函数调用的例子:
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(ListenSocket,(struct sockaddr *)&saddr,sizeof(saddr));

网络编程socket之connect函数
网络编程socket api存在一批核心接口,而这一批核心接口就是几个看似简单的函数,尽管实际上这些函数没有一个是简单。connect函数就是这些核心接口的一个函数,它完成主动连接的过程。
connect函数的功能是完成一个有连接协议的连接过程,对于TCP来说就是那个三路握手过程,它的函数原型:

#include<sys/socket.h>
#include<sys/types.h>
int connect(int sockfd, const struct sockaddr* server_addr, socklen_t addrlen)
返回:0──成功, -1──失败。
换一种写法:
int connect(
    SOCKET s,     // 没绑定套接口描述字
    const struct sockaddr FAR *name,   // 目标地址指针,目标地址中必须包含IP和端口信息。
    int namelen   // name的长度
    );

为了理解connect函数,我们需要对connect函数的功能进行介绍。connect函数的功能可以用一句话来概括,就是完成面向连接的协议的连接过程,它是主要连接的。面向连接的协议,在建立连接的时候总会有一方先发送数据,那么谁调用了connect谁就是先发送数据的一方。如此理解connect三个参数是容易了,我必需指定数据发送的地址,同时也必需指定数据从哪里发送,这正好是connect的前两个参数,而第三个参数是为第二个参数服务的。

-参数sockfd
指定数据发送的套接字,解决从哪里发送的问题。内核需要维护大量IO通道,所以用户必需通过这个参数告诉内核从哪个IO通道,此处就是从哪个socket接口中发送数据。sockfd是先前socket返回的值。
-参数server_addr
指定数据发送的目的地,也就是服务器端的地址。这里服务器是针对connect说的,因为connect是主动连接的一方调用的,所以相应的要存在一个被连接的一方,被动连接的一方需要调用listen以接受connect的连接请求,如此被动连接的一方就是服务器了。
-参数addrlen
指定server_addr结构体的长度。我们知道系统中存在大量的地址结构,但socket接口只是通过一个统一的结构来指定参数类型,所以需要指定一个长度,以使内核在进行参数复制的时候有个有个界限。
说明:

connect函数将使用参数sockfd中的套接字连接到参数serv_addr中指定的服务器。参数addrlen为serv_addr指向的内存空间大小。

如果参数sockfd的类型为SOCK_DGRAM,serv_addr参数为数据报发往的地址,且将只接收该地址的数据报。如果sockfd的类型为SOCK_STREAM或SOCK_SEQPACKET,调用该函数将连接serv_addr中的服务器地址。

  1. 服务器进程中系统调用的顺序
    socket()————bind()————listen()————accept()
    在面向连接的协议的程序中,服务器执行以下函数:
    l 调用socket()函数创建一个套接字。
    l 调用bind()函数把自己绑定在一个地址上。
    l 调用listen()函数侦听连接。
    l 调用accept()函数接受所有引入的请求。
    l 调用recv()函数获取引入的信息然后调用send()回答。

    TCP三次握手协议:
    (1)客户端先用connect()向服务器发出一个要求连接的信号SYN1。
    (2)服务器进程接收到这个信号后,发回应答信号ack1,同时这也是一个要求回答的信号SYN2。
    (3)客户端收到应答信号ack1和SYN2后,再次应答ack2。
    (4)服务器收到应答信号ack2,一次连接才算建立完成。

网络编程socket之connect函数
非阻塞式I/O。 通过使用F_SETFL命令设置O_NONBLOCK文件状态标志,我们可以把一个套接字设置为非阻塞型。
信号驱动式I/O。 通过使用F_SETFL命令设置O_ASYNC文件状态标志,我们可以把一个套接字设置成O_ASYNC,一旦其状态发生变化,内核就产生一个SIGIO信号。
F_SETOWN命令允许我们指定用于接收SIGIO和SIGURG信号的套接字属主(进程ID或进程组ID)。其中SIGIO信号是套接字被设置为信号驱动式I/O型产生的,SIGURG信号是在新的带外数据到达套接字时产生的。F_GETOWN命令返回套接字的当前属主。

fcntl函数提供了与网络编程相关的如下特性:
非阻塞式I/O。 通过使用F_SETFL命令设置O_NONBLOCK文件状态标志,我们可以把一个套接字设置为非阻塞型。
信号驱动式I/O。 通过使用F_SETFL命令设置O_ASYNC文件状态标志,我们可以把一个套接字设置成O_ASYNC,一旦其状态发生变化,内核就产生一个SIGIO信号。
F_SETOWN命令允许我们指定用于接收SIGIO和SIGURG信号的套接字属主(进程ID或进程组ID)。其中SIGIO信号是套接字被设置为信号驱动式I/O型产生的,SIGURG信号是在新的带外数据到达套接字时产生的。F_GETOWN命令返回套接字的当前属主。

fcntl()函数有如下特性:
非阻塞I/O: 可将cmd 设为F_SETFL,将lock设为O_NONBLOCK。
信号驱动I/O:可将cmd设为F_SETFL,将lock设为O_ASYNC。
ai_family参数指定调用者期待返回的套接口地址结构的类型。它的值包括三种:AF_INET,AF_INET6和AF_UNSPEC。如果指定AF_INET,那么函数九不能返回任何IPV6相关的地址信息;如果仅指定了AF_INET6,则就不能返回任何IPV4地址信息。AF_UNSPEC则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。如果某个主机既有AAAA记录(IPV6)地址,同时又有A记录(IPV4)地址,那么AAAA记录将作为sockaddr_in6结构返回,而A记录则作为sockaddr_in结构返回

Given node and service, which identify an Internet host and a
service, getaddrinfo() returns one or more addrinfo structures, each
of which contains an Internet address that can be specified in a call
to bind(2) or connect(2). The getaddrinfo() function combines the
functionality provided by the gethostbyname(3) and getservbyname(3)
functions into a single interface, but unlike the latter functions,
getaddrinfo() is reentrant and allows programs to eliminate
IPv4-versus-IPv6 dependencies.

   The addrinfo structure used by getaddrinfo() contains the following
   fields:

       struct addrinfo {
           int              ai_flags;
           int              ai_family;
           int              ai_socktype;
           int              ai_protocol;
           socklen_t        ai_addrlen;
           struct sockaddr *ai_addr;
           char            *ai_canonname;
           struct addrinfo *ai_next;
       };

   The hints argument points to an addrinfo structure that specifies
   criteria for selecting the socket address structures returned in the
   list pointed to by res.  If hints is not NULL it points to an
   addrinfo structure whose ai_family, ai_socktype, and ai_protocol
   specify criteria that limit the set of socket addresses returned by
   getaddrinfo(), as follows:

   ai_family   This field specifies the desired address family for the
               returned addresses.  Valid values for this field include
               AF_INET and AF_INET6.  The value AF_UNSPEC indicates that
               getaddrinfo() should return socket addresses for any
               address family (either IPv4 or IPv6, for example) that
               can be used with node and service.

   ai_socktype This field specifies the preferred socket type, for exam‐
               ple SOCK_STREAM or SOCK_DGRAM.  Specifying 0 in this
               field indicates that socket addresses of any type can be
               returned by getaddrinfo().

   ai_protocol This field specifies the protocol for the returned socket
               addresses.  Specifying 0 in this field indicates that
               socket addresses with any protocol can be returned by
               getaddrinfo().

   ai_flags    This field specifies additional options, described below.
               Multiple flags are specified by bitwise OR-ing them
               together.

其中ai_flags、ai_family、ai_socktype说明如下:
参数 取值 值 说明
ai_family AF_INET 2 IPv4
AF_INET6 23 IPv6
AF_UNSPEC 0 协议无关
ai_protocol IPPROTO_IP 0 IP协议
IPPROTO_IPV4 4 IPv4
IPPROTO_IPV6 41 IPv6
IPPROTO_UDP 17 UDP
IPPROTO_TCP 6 TCP
ai_socktype SOCK_STREAM 1 流
SOCK_DGRAM 2 数据报
ai_flags AI_PASSIVE 1 被动的,用于bind,通常用于server socket
AI_CANONNAME 2
AI_NUMERICHOST 4 地址为数字串

   All the other fields in the structure pointed to by hints must con‐
   tain either 0 or a null pointer, as appropriate.

   Specifying hints as NULL is equivalent to setting ai_socktype and
   ai_protocol to 0; ai_family to AF_UNSPEC; and ai_flags to
   (AI_V4MAPPED | AI_ADDRCONFIG).  (POSIX specifies different defaults
   for ai_flags; see NOTES.)  node specifies either a numerical network
   address (for IPv4, numbers-and-dots notation as supported by
   inet_aton(3); for IPv6, hexadecimal string format as supported by
   inet_pton(3)), or a network hostname, whose network addresses are
   looked up and resolved.  If hints.ai_flags contains the AI_NUMERI‐
   CHOST flag, then node must be a numerical network address.  The
   AI_NUMERICHOST flag suppresses any potentially lengthy network host
   address lookups.

   If the AI_PASSIVE flag is specified in hints.ai_flags, and node is
   NULL, then the returned socket addresses will be suitable for
   bind(2)ing a socket that will accept(2) connections.  The returned
   socket address will contain the "wildcard address" (INADDR_ANY for
   IPv4 addresses, IN6ADDR_ANY_INIT for IPv6 address).  The wildcard
   address is used by applications (typically servers) that intend to
   accept connections on any of the host's network addresses.  If node
   is not NULL, then the AI_PASSIVE flag is ignored.

   If the AI_PASSIVE flag is not set in hints.ai_flags, then the
   returned socket addresses will be suitable for use with connect(2),
   sendto(2), or sendmsg(2).  If node is NULL, then the network address
   will be set to the loopback interface address (INADDR_LOOPBACK for
   IPv4 addresses, IN6ADDR_LOOPBACK_INIT for IPv6 address); this is used
   by applications that intend to communicate with peers running on the
   same host.

   service sets the port in each returned address structure.  If this
   argument is a service name (see services(5)), it is translated to the
   corresponding port number.  This argument can also be specified as a
   decimal number, which is simply converted to binary.  If service is
   NULL, then the port number of the returned socket addresses will be
   left uninitialized.  If AI_NUMERICSERV is specified in hints.ai_flags
   and service is not NULL, then service must point to a string contain‐
   ing a numeric port number.  This flag is used to inhibit the invoca‐
   tion of a name resolution service in cases where it is known not to
   be required.

   Either node or service, but not both, may be NULL.

   The getaddrinfo() function allocates and initializes a linked list of
   addrinfo structures, one for each network address that matches node
   and service, subject to any restrictions imposed by hints, and
   returns a pointer to the start of the list in res.  The items in the
   linked list are linked by the ai_next field.

   There are several reasons why the linked list may have more than one
   addrinfo structure, including: the network host is multihomed, acces‐
   sible over multiple protocols (e.g., both AF_INET and AF_INET6); or
   the same service is available from multiple socket types (one
   SOCK_STREAM address and another SOCK_DGRAM address, for example).
   Normally, the application should try using the addresses in the order
   in which they are returned.  The sorting function used within getad‐
   drinfo() is defined in RFC 3484; the order can be tweaked for a par‐
   ticular system by editing /etc/gai.conf (available since glibc 2.5).

   If hints.ai_flags includes the AI_CANONNAME flag, then the ai_canon‐
   name field of the first of the addrinfo structures in the returned
   list is set to point to the official name of the host.

   The remaining fields of each returned addrinfo structure are initial‐
   ized as follows:

   * The ai_family, ai_socktype, and ai_protocol fields return the
     socket creation parameters (i.e., these fields have the same mean‐
     ing as the corresponding arguments of socket(2)).  For example,
     ai_family might return AF_INET or AF_INET6; ai_socktype might
     return SOCK_DGRAM or SOCK_STREAM; and ai_protocol returns the pro‐
     tocol for the socket.

   * A pointer to the socket address is placed in the ai_addr field, and
     the length of the socket address, in bytes, is placed in the
     ai_addrlen field.

   If hints.ai_flags includes the AI_ADDRCONFIG flag, then IPv4
   addresses are returned in the list pointed to by res only if the
   local system has at least one IPv4 address configured, and IPv6
   addresses are returned only if the local system has at least one IPv6
   address configured.  The loopback address is not considered for this
   case as valid as a configured address.  This flag is useful on, for
   example, IPv4-only systems, to ensure that getaddrinfo() does not
   return IPv6 socket addresses that would always fail in connect(2) or
   bind(2).

   If hints.ai_flags specifies the AI_V4MAPPED flag, and hints.ai_family
   was specified as AF_INET6, and no matching IPv6 addresses could be
   found, then return IPv4-mapped IPv6 addresses in the list pointed to
   by res.  If both AI_V4MAPPED and AI_ALL are specified in
   hints.ai_flags, then return both IPv6 and IPv4-mapped IPv6 addresses
   in the list pointed to by res.  AI_ALL is ignored if AI_V4MAPPED is
   not also specified.

   The freeaddrinfo() function frees the memory that was allocated for
   the dynamically allocated linked list res.

Extensions to getaddrinfo() for Internationalized Domain Names
Starting with glibc 2.3.4, getaddrinfo() has been extended to selec‐
tively allow the incoming and outgoing hostnames to be transparently
converted to and from the Internationalized Domain Name (IDN) format
(see RFC 3490, Internationalizing Domain Names in Applications
(IDNA)). Four new flags are defined:

   AI_IDN If this flag is specified, then the node name given in node is
          converted to IDN format if necessary.  The source encoding is
          that of the current locale.

          If the input name contains non-ASCII characters, then the IDN
          encoding is used.  Those parts of the node name (delimited by
          dots) that contain non-ASCII characters are encoded using
          ASCII Compatible Encoding (ACE) before being passed to the
          name resolution functions.

   AI_CANONIDN
          After a successful name lookup, and if the AI_CANONNAME flag
          was specified, getaddrinfo() will return the canonical name of
          the node corresponding to the addrinfo structure value passed
          back.  The return value is an exact copy of the value returned
          by the name resolution function.

          If the name is encoded using ACE, then it will contain the
          xn-- prefix for one or more components of the name.  To con‐
          vert these components into a readable form the AI_CANONIDN
          flag can be passed in addition to AI_CANONNAME.  The resulting
          string is encoded using the current locale's encoding.

   AI_IDN_ALLOW_UNASSIGNED, AI_IDN_USE_STD3_ASCII_RULES
          Setting these flags will enable the IDNA_ALLOW_UNASSIGNED
          (allow unassigned Unicode code points) and
          IDNA_USE_STD3_ASCII_RULES (check output to make sure it is a
          STD3 conforming hostname) flags respectively to be used in the
          IDNA handling.

RETURN VALUE top

   getaddrinfo() returns 0 if it succeeds, or one of the following
   nonzero error codes:

   EAI_ADDRFAMILY
          The specified network host does not have any network addresses
          in the requested address family.

   EAI_AGAIN
          The name server returned a temporary failure indication.  Try
          again later.

   EAI_BADFLAGS
          hints.ai_flags contains invalid flags; or, hints.ai_flags
          included AI_CANONNAME and name was NULL.

   EAI_FAIL
          The name server returned a permanent failure indication.

   EAI_FAMILY
          The requested address family is not supported.

   EAI_MEMORY
          Out of memory.

   EAI_NODATA
          The specified network host exists, but does not have any
          network addresses defined.

   EAI_NONAME
          The node or service is not known; or both node and service are
          NULL; or AI_NUMERICSERV was specified in hints.ai_flags and
          service was not a numeric port-number string.

   EAI_SERVICE
          The requested service is not available for the requested
          socket type.  It may be available through another socket type.
          For example, this error could occur if service was "shell" (a
          service available only on stream sockets), and either
          hints.ai_protocol was IPPROTO_UDP, or hints.ai_socktype was
          SOCK_DGRAM; or the error could occur if service was not NULL,
          and hints.ai_socktype was SOCK_RAW (a socket type that does
          not support the concept of services).

   EAI_SOCKTYPE
          The requested socket type is not supported.  This could occur,
          for example, if hints.ai_socktype and hints.ai_protocol are
          inconsistent (e.g., SOCK_DGRAM and IPPROTO_TCP, respectively).

   EAI_SYSTEM
          Other system error, check errno for details.

   The gai_strerror() function translates these error codes to a human
   readable string, suitable for error reporting.

FILES top

   /etc/gai.conf

ATTRIBUTES top

   For an explanation of the terms used in this section, see
   attributes(7).

   ┌────────────────┬───────────────┬────────────────────┐
   │Interface       │ Attribute     │ Value              │
   ├────────────────┼───────────────┼────────────────────┤
   │getaddrinfo()   │ Thread safety │ MT-Safe env locale │
   ├────────────────┼───────────────┼────────────────────┤
   │freeaddrinfo(), │ Thread safety │ MT-Safe            │
   │gai_strerror()  │               │                    │
   └────────────────┴───────────────┴────────────────────┘

CONFORMING TO top

   POSIX.1-2001, POSIX.1-2008.  The getaddrinfo() function is documented
   in RFC 2553.

NOTES top

   getaddrinfo() supports the address%scope-id notation for specifying
   the IPv6 scope-ID.

   AI_ADDRCONFIG, AI_ALL, and AI_V4MAPPED are available since glibc
   2.3.3.  AI_NUMERICSERV is available since glibc 2.3.4.

   According to POSIX.1, specifying hints as NULL should cause ai_flags
   to be assumed as 0.  The GNU C library instead assumes a value of
   (AI_V4MAPPED | AI_ADDRCONFIG) for this case, since this value is
   considered an improvement on the specification.

EXAMPLE top

   The following programs demonstrate the use of getaddrinfo(),
   gai_strerror(), freeaddrinfo(), and getnameinfo(3).  The programs are
   an echo server and client for UDP datagrams.

Client program

   #include <sys/types.h>
   #include <sys/socket.h>
   #include <netdb.h>
   #include <stdio.h>
   #include <stdlib.h>
   #include <unistd.h>
   #include <string.h>

   #define BUF_SIZE 500

   int
   main(int argc, char *argv[])
   {
       struct addrinfo hints;
       struct addrinfo *result, *rp;
       int sfd, s, j;
       size_t len;
       ssize_t nread;
       char buf[BUF_SIZE];

       if (argc < 3) {
           fprintf(stderr, "Usage: %s host port msg...\n", argv[0]);
           exit(EXIT_FAILURE);
       }

       /* Obtain address(es) matching host/port */

       memset(&hints, 0, sizeof(struct addrinfo));
       hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
       hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
       hints.ai_flags = 0;
       hints.ai_protocol = 0;          /* Any protocol */

       s = getaddrinfo(argv[1], argv[2], &hints, &result);
       if (s != 0) {
           fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
           exit(EXIT_FAILURE);
       }

       /* getaddrinfo() returns a list of address structures.
          Try each address until we successfully connect(2).
          If socket(2) (or connect(2)) fails, we (close the socket
          and) try the next address. */

       for (rp = result; rp != NULL; rp = rp->ai_next) {
           sfd = socket(rp->ai_family, rp->ai_socktype,
                        rp->ai_protocol);
           if (sfd == -1)
               continue;

           if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
               break;                  /* Success */

           close(sfd);
       }

       if (rp == NULL) {               /* No address succeeded */
           fprintf(stderr, "Could not connect\n");
           exit(EXIT_FAILURE);
       }

       freeaddrinfo(result);           /* No longer needed */

       /* Send remaining command-line arguments as separate
          datagrams, and read responses from server */

       for (j = 3; j < argc; j++) {
           len = strlen(argv[j]) + 1;
                   /* +1 for terminating null byte */

           if (len > BUF_SIZE) {
               fprintf(stderr,
                       "Ignoring long message in argument %d\n", j);
               continue;
           }

           if (write(sfd, argv[j], len) != len) {
               fprintf(stderr, "partial/failed write\n");
               exit(EXIT_FAILURE);
           }

           nread = read(sfd, buf, BUF_SIZE);
           if (nread == -1) {
               perror("read");
               exit(EXIT_FAILURE);
           }

           printf("Received %zd bytes: %s\n", nread, buf);
       }

       exit(EXIT_SUCCESS);
   }

Server program

   #include <sys/types.h>
   #include <stdio.h>
   #include <stdlib.h>
   #include <unistd.h>
   #include <string.h>
   #include <sys/socket.h>
   #include <netdb.h>

   #define BUF_SIZE 500

   int
   main(int argc, char *argv[])
   {
       struct addrinfo hints;
       struct addrinfo *result, *rp;
       int sfd, s;
       struct sockaddr_storage peer_addr;
       socklen_t peer_addr_len;
       ssize_t nread;
       char buf[BUF_SIZE];

       if (argc != 2) {
           fprintf(stderr, "Usage: %s port\n", argv[0]);
           exit(EXIT_FAILURE);
       }

       memset(&hints, 0, sizeof(struct addrinfo));
       hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
       hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
       hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */
       hints.ai_protocol = 0;          /* Any protocol */
       hints.ai_canonname = NULL;
       hints.ai_addr = NULL;
       hints.ai_next = NULL;

       s = getaddrinfo(NULL, argv[1], &hints, &result);
       if (s != 0) {
           fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
           exit(EXIT_FAILURE);
       }

       /* getaddrinfo() returns a list of address structures.
          Try each address until we successfully bind(2).
          If socket(2) (or bind(2)) fails, we (close the socket
          and) try the next address. */

       for (rp = result; rp != NULL; rp = rp->ai_next) {
           sfd = socket(rp->ai_family, rp->ai_socktype,
                   rp->ai_protocol);
           if (sfd == -1)
               continue;

           if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
               break;                  /* Success */

           close(sfd);
       }

       if (rp == NULL) {               /* No address succeeded */
           fprintf(stderr, "Could not bind\n");
           exit(EXIT_FAILURE);
       }

       freeaddrinfo(result);           /* No longer needed */

       /* Read datagrams and echo them back to sender */

       for (;;) {
           peer_addr_len = sizeof(struct sockaddr_storage);
           nread = recvfrom(sfd, buf, BUF_SIZE, 0,
                   (struct sockaddr *) &peer_addr, &peer_addr_len);
           if (nread == -1)
               continue;               /* Ignore failed request */

           char host[NI_MAXHOST], service[NI_MAXSERV];

           s = getnameinfo((struct sockaddr *) &peer_addr,
                           peer_addr_len, host, NI_MAXHOST,
                           service, NI_MAXSERV, NI_NUMERICSERV);
           if (s == 0)
               printf("Received %zd bytes from %s:%s\n",
                       nread, host, service);
           else
               fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));

           if (sendto(sfd, buf, nread, 0,
                       (struct sockaddr *) &peer_addr,
                       peer_addr_len) != nread)
               fprintf(stderr, "Error sending response\n");
       }
   }

http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
参考这篇文章 不整了,这篇博客的代码很不错。

相关标签: c++ linux