TCP 状态转换 
红客户端 绿服务器端 黑色异常
 2MSL(Maximum Segment Lifetime)  主动断开连接的一方, 最后进入一个 TIME_WAIT状态, 这个状态会持续: 2msl 为了确保服务器端可以收到ACK,确保正确关闭 msl: 官方建议: 2分钟, 实际是30s 当 TCP 连接主动关闭方接收到被动关闭方发送的 FIN 和最终的 ACK 后,连接的主动关闭方必须处于TIME_WAIT 状态并持续 2MSL 时间。
这样就能够让 TCP 连接的主动关闭方在它发送的 ACK 丢失的情况下重新发送最终的 ACK。因为如果最后一个ack丢失的话,被动关闭方会一直等待,等不到就会再发送FIN,直到收到ACK;
主动关闭方重新发送的最终 ACK 并不是因为被动关闭方重传了 ACK(它们并不消耗序列号,被动关闭方也不会重传),而是因为被动关闭方重传了它的 FIN。事实上,被动关闭方总是重传 FIN 直到它收到一个最终的 ACK。  
 
4.24 半关闭 端口复用 半关闭(能接收 不能发) 当 TCP 链接中 A 向 B 发送 FIN 请求关闭,另一端 B 回应 ACK 之后(A 端进入 FIN_WAIT_2状态),并没有立即发送 FIN 给 A,A 方处于半连接状态(半开关),此时 A 可以接收 B 发送的数据,但是 A  已经不能再向 B 发送数据 。  
从程序的角度,可以使用 API shutdown (SHUT_WR) 来控制实现半连接状态:  
#include  <sys/socket.h>  int  shutdown (int  sockfd, int  how)  ;sockfd: 需要关闭的socket的描述符 how: 允许为shutdown操作选择以下几种方式: 	SHUT_RD(0 ): 关闭sockfd上的读功能,此选项将不允许sockfd进行读操作。该套接字不再接收数据,任何当前在套接字接受缓冲区的数据将被无声的丢弃掉。 	SHUT_WR(1 ): 关闭sockfd的写功能,此选项将不允许sockfd进行写操作。进程不能在对此套接字发出写操作。 	SHUT_RDWR(2 ):关闭sockfd的读写功能。相当于调用shutdown两次 相当于close:首先是以SHUT_RD,然后以SHUT_WR。 
 
使用 close 中止一个连接,但它只是减少描述符的引用计数,并不直接关闭连接,只有当描述符的引用计数为 0 时才关闭连接。shutdown 不考虑描述符的引用计数,直接关闭描述符。也可选择中止一个方向的连接,只中止读或只中止写。(有子进程则加一) 注意:
如果有多个进程共享一个套接字,close 每被调用一次,计数减 1 ,直到计数为 0 时,也就是所用进程都调用了 close,套接字将被释放。 
在多进程中如果一个进程调用了 shutdown(sfd, SHUT_RDWR) 后,其它的进程将无法进行通信。但如果一个进程 close(sfd) 将不会影响到其它进程。   
 
端口复用 端口复用的用途是:防止服务器重启时 之前绑定的端口还没释放。程序突然退出而系统没有释放端口。
tcp_server.c
#include  <stdio.h>  #include  <ctype.h>  #include  <arpa/inet.h>  #include  <unistd.h>  #include  <stdlib.h>  #include  <string.h>  int  main (int  argc, char  *argv[])   {         int  lfd = socket(PF_INET, SOCK_STREAM, 0 );     if (lfd == -1 ) {         perror("socket" );         return  -1 ;     }     struct  sockaddr_in  saddr ;     saddr.sin_family = AF_INET;     saddr.sin_addr.s_addr = INADDR_ANY;     saddr.sin_port = htons(9999 );          int  optval = 1 ;      setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof (optval));           int  ret = bind(lfd, (struct sockaddr *)&saddr, sizeof (saddr));     if (ret == -1 ) {         perror("bind" );         return  -1 ;     }          ret = listen(lfd, 8 );     if (ret == -1 ) {         perror("listen" );         return  -1 ;     }          struct  sockaddr_in  cliaddr ;     socklen_t  len = sizeof (cliaddr);     int  cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);     if (cfd == -1 ) {         perror("accpet" );         return  -1 ;     }          char  cliIp[16 ];     inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIp, sizeof (cliIp));     unsigned  short  cliPort = ntohs(cliaddr.sin_port);          printf ("client's ip is %s, and port is %d\n" , cliIp, cliPort );          char  recvBuf[1024 ] = {0 };     while (1 ) {         int  len = recv(cfd, recvBuf, sizeof (recvBuf), 0 );          if (len == -1 ) {             perror("recv" );             return  -1 ;         } else  if (len == 0 ) {             printf ("客户端已经断开连接...\n" );             break ;         } else  if (len > 0 ) {             printf ("read buf = %s\n" , recvBuf);         }                  for (int  i = 0 ; i < len; ++i) {             recvBuf[i] = toupper (recvBuf[i]);         }         printf ("after buf = %s\n" , recvBuf);                  ret = send(cfd, recvBuf, strlen (recvBuf) + 1 , 0 );          if (ret == -1 ) {             perror("send" );             return  -1 ;         }     }          close(cfd);     close(lfd);     return  0 ; } 
 
tcp_client.c
#include  <stdio.h>  #include  <arpa/inet.h>  #include  <stdlib.h>  #include  <unistd.h>  #include  <string.h>  int  main ()   {         int  fd = socket(PF_INET, SOCK_STREAM, 0 );     if (fd == -1 ) {         perror("socket" );         return  -1 ;     }     struct  sockaddr_in  seraddr ;     inet_pton(AF_INET, "127.0.0.1" , &seraddr.sin_addr.s_addr);     seraddr.sin_family = AF_INET;     seraddr.sin_port = htons(9999 );          int  ret = connect(fd, (struct sockaddr *)&seraddr, sizeof (seraddr));     if (ret == -1 ){         perror("connect" );         return  -1 ;     }     while (1 ) {         char  sendBuf[1024 ] = {0 };         fgets(sendBuf, sizeof (sendBuf), stdin );         write(fd, sendBuf, strlen (sendBuf) + 1 );                   int  len = read(fd, sendBuf, sizeof (sendBuf));          if (len == -1 ) {             perror("read" );             return  -1 ;         }else  if (len > 0 ) {             printf ("read buf = %s\n" , sendBuf);         } else  {             printf ("服务器已经断开连接...\n" );             break ;         }     }     close(fd);     return  0 ; } 
 
运行服务器端(9999)、运行客户端(36514)之后,第二个答案里的第三行是专门用来通信的socket
如果断开服务器,客户端被动关闭处于close wait状态 ,服务器端主动关闭,处于fw2
如果快速断开被动关闭的客户端 服务器处于time-wait 等待2msl后释放端口
端口复用最常用的用途是: 1 防止服务器重启时之前绑定的端口还未释放 2 程序突然退出而系统没有释放 端口
#include  <sys/types.h>  #include  <sys/socket.h>  int  setsockopt (int  sockfd, int  level, int  optname, const  void  *optval, socklen_toptlen)  ;参数:     - sockfd : 要操作的文件描述符     - level : 级别 - SOL_SOCKET (端口复用的级别)     - optname : 选项的名称         - SO_REUSEADDR         - SO_REUSEPORT     - optval : 端口复用的值(整型)         - 1  : 可以复用         - 0  : 不可以复用     - optlen : optval参数的大小 端口复用,设置的时机是在服务器绑定端口之前。 setsockopt(); bind(); 
 
 
4.25 IO多路复用 I/O 多路复用使得程序能同时监听多个文件描述符,能够提高程序的性能,  Linux 下实现 I/O 多路复用的 系统调用主要有 select、poll 和 epoll。  
文件和内存!!!的IO。
4.26 select 
主旨思想:
首先要构造一个关于文件描述符的列表,将要监听的文件描述符添加到该列表中。 
调用一个系统函数,监听该列表中的文件描述符,直到这些描述符中的一个或者多个进行I/O操作时,该函数才返回。
这个函数是阻塞 
函数对文件描述符的检测的操作是由内核完成的 
 
 
在返回时,它会告诉进程有多少(哪些)描述符要进行I/O操作。 
 
 
#include  <sys/time.h>  #include  <sys/types.h>  #include  <unistd.h>  #include  <sys/select.h>  int  select (int  nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)  ;- 参数:     - nfds : 委托内核检测的最大文件描述符的值 + 1      - readfds : 要检测的文件描述符的读的集合,委托内核检测哪些文件描述符的读的属性     	- 一般检测读操作         - 对应的是对方发送过来的数据,因为读是被动的接收数据,检测的就是读缓冲区         - 是一个传入传出参数     - writefds : 要检测的文件描述符的写的集合,委托内核检测哪些文件描述符的写的属性     	- 委托内核检测写缓冲区是不是还可以写数据(不满的就可以写)     - exceptfds : 检测发生异常的文件描述符的集合(一般不用)     - timeout : 设置的超时时间         struct  timeval  {             long  tv_sec;              long  tv_usec;          };         - NULL  : 永久阻塞,直到检测到了文件描述符有变化         - tv_sec = 0  tv_usec = 0 , 不阻塞         - tv_sec > 0  tv_usec > 0 , 阻塞对应的时间 - 返回值 :     - -1  : 失败     - >0 (n) : 检测的集合中有n个文件描述符发生了变化          void  FD_CLR(int  fd, fd_set *set );int  FD_ISSET (int  fd, fd_set *set )  ;void  FD_SET (int  fd, fd_set *set )  ;void  FD_ZERO (fd_set *set )  ;
 
100 101没有发送,就置零
#include  <stdio.h>  #include  <arpa/inet.h>  #include  <unistd.h>  #include  <stdlib.h>  #include  <string.h>  #include  <sys/select.h>  int  main ()   {         int  lfd = socket(PF_INET, SOCK_STREAM, 0 );      struct  sockaddr_in  saddr ;     saddr.sin_port = htons(9999 );     saddr.sin_family = AF_INET;     saddr.sin_addr.s_addr = INADDR_ANY;          bind(lfd, (struct sockaddr *)&saddr, sizeof (saddr));          listen(lfd, 8 );          fd_set rdset, tmp;     FD_ZERO(&rdset);     FD_SET(lfd, &rdset);     int  maxfd = lfd;     while (1 ) {         tmp = rdset;                  int  ret = select(maxfd + 1 , &tmp, NULL , NULL , NULL );          if (ret == -1 ) {             perror("select" );             exit (-1 );         } else  if (ret == 0 ) {              continue ;         } else  if (ret > 0 ) {                           if (FD_ISSET(lfd, &tmp)) {                                   struct sockaddr_in cliaddr;                 int  len = sizeof (cliaddr);                 int  cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);                                  FD_SET(cfd, &rdset);                                  maxfd = maxfd > cfd ? maxfd : cfd;             }             for (int  i = lfd + 1 ; i <= maxfd; i++) {                  if (FD_ISSET(i, &tmp)) {                                          char  buf[1024 ] = {0 };                     int  len = read(i, buf, sizeof (buf));                     if (len == -1 ) {                         perror("read" );                         exit (-1 );                     } else  if (len == 0 ) {                         printf ("client closed...\n" );                         close(i);                         FD_CLR(i, &rdset);                     } else  if (len > 0 ) {                         printf ("read buf = %s\n" , buf);                         write(i, buf, strlen (buf) + 1 );                     }                 }             }         }     }     close(lfd);     return  0 ; } 
 
#include  <stdio.h>  #include  <arpa/inet.h>  #include  <stdlib.h>  #include  <unistd.h>  #include  <string.h>  int  main ()   {         int  fd = socket(PF_INET, SOCK_STREAM, 0 );     if (fd == -1 ) {         perror("socket" );         return  -1 ;     }     struct  sockaddr_in  seraddr ;     inet_pton(AF_INET, "127.0.0.1" , &seraddr.sin_addr.s_addr);     seraddr.sin_family = AF_INET;     seraddr.sin_port = htons(9999 );          int  ret = connect(fd, (struct sockaddr *)&seraddr, sizeof (seraddr));     if (ret == -1 ){         perror("connect" );         return  -1 ;     }     int  num = 0 ;     while (1 ) {         char  sendBuf[1024 ] = {0 };         sprintf (sendBuf, "send data %d" , num++);         write(fd, sendBuf, strlen (sendBuf) + 1 );                  int  len = read(fd, sendBuf, sizeof (sendBuf));         if (len == -1 ) {             perror("read" );             return  -1 ;         }else  if (len > 0 ) {             printf ("read buf = %s\n" , sendBuf);         } else  {             printf ("服务器已经断开连接...\n" );             break ;         }                  usleep(1000 );     }     close(fd);     return  0 ; } 
 
结果是多客户端可以连接到服务器。
poll 解决select 的第三个和第四个缺点。
4.28 poll #include  <poll.h>  struct  pollfd  {    int  fd;      short  events;      short  revents;  }; struct  pollfd  myfd ;  myfd.fd = 5 ; myfd.events = POLLIN | POLLOUT;  int  poll (struct pollfd *fds, nfds_t  nfds, int  timeout)  ;- 参数:     - fds : 是一个struct  pollfd  结构体数组,这是一个需要检测的文件描述符的集合     - nfds  :  这个是第一个参数数组中最后一个有效元素的下标 + 1     - timeout : 阻塞时长         0  : 不阻塞         -1  : 阻塞,当检测到需要检测的文件描述符有变化,解除阻塞         >0  : 阻塞的时长 - 返回值:     -1  : 失败     >0 (n) : 成功,n表示检测到集合中有n个文件描述符发生变化 
 
#include  <stdio.h>  #include  <arpa/inet.h>  #include  <unistd.h>  #include  <stdlib.h>  #include  <string.h>  #include  <poll.h>  int  main ()   {         int  lfd = socket(PF_INET, SOCK_STREAM, 0 );     struct  sockaddr_in  saddr ;     saddr.sin_port = htons(9999 );     saddr.sin_family = AF_INET;     saddr.sin_addr.s_addr = INADDR_ANY;          bind(lfd, (struct sockaddr *)&saddr, sizeof (saddr));          listen(lfd, 8 );          struct  pollfd  fds [1024];     for (int  i = 0 ; i < 1024 ; i++) {         fds[i].fd = -1 ;          fds[i].events = POLLIN;      }     fds[0 ].fd = lfd;     int  nfds = 0 ;     while (1 ) {                  int  ret = poll(fds, nfds + 1 , -1 );         if (ret == -1 ) {             perror("poll" );             exit (-1 );         } else  if (ret == 0 ) {             continue ;         } else  if (ret > 0 ) {                          if (fds[0 ].revents & POLLIN) {                                  struct sockaddr_in cliaddr;                 int  len = sizeof (cliaddr);                 int  cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);                                  for (int  i = 1 ; i < 1024 ; i++) {                     if (fds[i].fd == -1 ) {                         fds[i].fd = cfd;                         fds[i].events = POLLIN;                         break ;                     }                 }                                  nfds = nfds > cfd ? nfds : cfd;             }             for (int  i = 1 ; i <= nfds; i++) {                 if (fds[i].revents & POLLIN) {                                          char  buf[1024 ] = {0 };                     int  len = read(fds[i].fd, buf, sizeof (buf));                     if (len == -1 ) {                         perror("read" );                         exit (-1 );                     } else  if (len == 0 ) {                         printf ("client closed...\n" );                         close(fds[i].fd);                         fds[i].fd = -1 ;                     } else  if (len > 0 ) {                         printf ("read buf = %s\n" , buf);                         write(fds[i].fd, buf, strlen (buf) + 1 );                     }                 }             }         }     }     close(lfd);     return  0 ; } 
 
 
poll 解决select 的第三个和第四个缺点。仍然还是需要遍历。
4.29 epoll 没有用户态到内核态的切换,之前是线性数据结构,现在是红黑树数据结构。
epoll是如何实现的?首先使用epoll_create在内核区创建实例,是一个eventpoll的结构体类型,返回文件描述符。
这个数据结构rbr红黑树结构、rdlist链表 。如果有数据了就从rbr放入rdlist中。以前拷贝回去的都是所有的,而这里拷贝回去的是就绪的。
#include  <sys/epoll.h>  int  epoll_create (int  size)  ;    - 参数:     	size : 目前没有意义了。随便写一个数,必须大于0      - 返回值:         -1  : 失败         > 0  : 文件描述符,操作epoll实例的      typedef  union  epoll_data  {    void  *ptr;     int  fd;     uint32_t  u32;     uint64_t  u64; } epoll_data_t ; struct  epoll_event  {    uint32_t  events;      epoll_data_t  data;  };     常见的Epoll检测事件events:     - EPOLLIN     - EPOLLOUT     - EPOLLERR      int  epoll_ctl (int  epfd, int  op, int  fd, struct epoll_event *event)  ;    - 参数:         - epfd : epoll实例对应的文件描述符         - op : 要进行什么操作             EPOLL_CTL_ADD: 添加             EPOLL_CTL_MOD: 修改             EPOLL_CTL_DEL: 删除         - fd : 要检测的文件描述符         - event : 检测文件描述符什么事情      int  epoll_wait (int  epfd, struct epoll_event *events, int  maxevents, int  timeout)  ;    - 参数:         - epfd : epoll实例对应的文件描述符         - events : 传出参数,保存了发送了变化的文件描述符的信息         - maxevents : 第二个参数结构体数组的大小         - timeout : 阻塞时间             - 0  : 不阻塞             - -1  : 阻塞,直到检测到fd数据发生变化,解除阻塞             - > 0  : 阻塞的时长(毫秒)     - 返回值:         - 成功,返回发送变化的文件描述符的个数 > 0          - 失败 -1  
 
#include  <stdio.h>  #include  <arpa/inet.h>  #include  <unistd.h>  #include  <stdlib.h>  #include  <string.h>  #include  <sys/epoll.h>  int  main ()   {         int  lfd = socket(PF_INET, SOCK_STREAM, 0 );     struct  sockaddr_in  saddr ;     saddr.sin_port = htons(9999 );     saddr.sin_family = AF_INET;     saddr.sin_addr.s_addr = INADDR_ANY;          bind(lfd, (struct sockaddr *)&saddr, sizeof (saddr));          listen(lfd, 8 );          int  epfd = epoll_create(100 );          struct  epoll_event  epev ;     epev.events = EPOLLIN;     epev.data.fd = lfd;     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);     struct  epoll_event  epevs [1024];     while (1 ) {         int  ret = epoll_wait(epfd, epevs, 1024 , -1 );         if (ret == -1 ) {             perror("epoll_wait" );             exit (-1 );         }         printf ("ret = %d\n" , ret);         for (int  i = 0 ; i < ret; i++) {             int  curfd = epevs[i].data.fd;             if (curfd == lfd) {                                  struct  sockaddr_in  cliaddr ;                 int  len = sizeof (cliaddr);                 int  cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);                 epev.events = EPOLLIN;                 epev.data.fd = cfd;                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev);             } else  {                 if (epevs[i].events & EPOLLOUT) {                     continue ;                 }                                     char  buf[1024 ] = {0 };                 int  len = read(curfd, buf, sizeof (buf));                 if (len == -1 ) {                     perror("read" );                     exit (-1 );                 } else  if (len == 0 ) {                     printf ("client closed...\n" );                     epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL );                     close(curfd);                 } else  if (len > 0 ) {                     printf ("read buf = %s\n" , buf);                     write(curfd, buf, strlen (buf) + 1 );                 }             }         }     }     close(lfd);     close(epfd);     return  0 ; } 
 
4.31 epoll 的两种工作模式 
struct  epoll_event  {    uint32_t  events;      epoll_data_t  data;  }; 常见的Epoll检测事件: - EPOLLIN - EPOLLOUT - EPOLLERR - EPOLLET 
 
#include  <stdio.h>  #include  <arpa/inet.h>  #include  <unistd.h>  #include  <stdlib.h>  #include  <string.h>  #include  <sys/epoll.h>  int  main ()   {         int  lfd = socket(PF_INET, SOCK_STREAM, 0 );     struct  sockaddr_in  saddr ;     saddr.sin_port = htons(9999 );     saddr.sin_family = AF_INET;     saddr.sin_addr.s_addr = INADDR_ANY;          bind(lfd, (struct sockaddr *)&saddr, sizeof (saddr));          listen(lfd, 8 );          int  epfd = epoll_create(100 );          struct  epoll_event  epev ;     epev.events = EPOLLIN;     epev.data.fd = lfd;     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);     struct  epoll_event  epevs [1024];     while (1 ) {         int  ret = epoll_wait(epfd, epevs, 1024 , -1 );         if (ret == -1 ) {             perror("epoll_wait" );             exit (-1 );         }         printf ("ret = %d\n" , ret);         for (int  i = 0 ; i < ret; i++) {             int  curfd = epevs[i].data.fd;             if (curfd == lfd) {                                  struct  sockaddr_in  cliaddr ;                 int  len = sizeof (cliaddr);                 int  cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);                 epev.events = EPOLLIN;                 epev.data.fd = cfd;                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev);             } else  {                 if (epevs[i].events & EPOLLOUT) {                     continue ;                 }                                     char  buf[5 ] = {0 };                 int  len = read(curfd, buf, sizeof (buf));                 if (len == -1 ) {                     perror("read" );                     exit (-1 );                 } else  if (len == 0 ) {                     printf ("client closed...\n" );                     epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL );                     close(curfd);                 } else  if (len > 0 ) {                     printf ("read buf = %s\n" , buf);                     write(curfd, buf, strlen (buf) + 1 );                 }             }         }     }     close(lfd);     close(epfd);     return  0 ; } 
 
#include  <stdio.h>  #include  <arpa/inet.h>  #include  <stdlib.h>  #include  <unistd.h>  #include  <string.h>  int  main ()   {         int  fd = socket(PF_INET, SOCK_STREAM, 0 );     if (fd == -1 ) {         perror("socket" );         return  -1 ;     }     struct  sockaddr_in  seraddr ;     inet_pton(AF_INET, "127.0.0.1" , &seraddr.sin_addr.s_addr);     seraddr.sin_family = AF_INET;     seraddr.sin_port = htons(9999 );          int  ret = connect(fd, (struct sockaddr *)&seraddr, sizeof (seraddr));     if (ret == -1 ){         perror("connect" );         return  -1 ;     }     int  num = 0 ;     while (1 ) {         char  sendBuf[1024 ] = {0 };                  fgets(sendBuf, sizeof (sendBuf), stdin );         write(fd, sendBuf, strlen (sendBuf) + 1 );                  int  len = read(fd, sendBuf, sizeof (sendBuf));         if (len == -1 ) {             perror("read" );             return  -1 ;         }else  if (len > 0 ) {             printf ("read buf = %s\n" , sendBuf);         } else  {             printf ("服务器已经断开连接...\n" );             break ;         }     }     close(fd);     return  0 ; } 
 
#include  <stdio.h>  #include  <arpa/inet.h>  #include  <unistd.h>  #include  <stdlib.h>  #include  <string.h>  #include  <sys/epoll.h>  #include  <fcntl.h>  #include  <errno.h>  int  main ()   {         int  lfd = socket(PF_INET, SOCK_STREAM, 0 );     struct  sockaddr_in  saddr ;     saddr.sin_port = htons(9999 );     saddr.sin_family = AF_INET;     saddr.sin_addr.s_addr = INADDR_ANY;          bind(lfd, (struct sockaddr *)&saddr, sizeof (saddr));          listen(lfd, 8 );          int  epfd = epoll_create(100 );          struct  epoll_event  epev ;     epev.events = EPOLLIN;     epev.data.fd = lfd;     epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);     struct  epoll_event  epevs [1024];     while (1 ) {         int  ret = epoll_wait(epfd, epevs, 1024 , -1 );         if (ret == -1 ) {             perror("epoll_wait" );             exit (-1 );         }         printf ("ret = %d\n" , ret);         for (int  i = 0 ; i < ret; i++) {             int  curfd = epevs[i].data.fd;             if (curfd == lfd) {                                  struct  sockaddr_in  cliaddr ;                 int  len = sizeof (cliaddr);                 int  cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);                                  int  flag = fcntl(cfd, F_GETFL);                 flag | O_NONBLOCK;                 fcntl(cfd, F_SETFL, flag);                 epev.events = EPOLLIN | EPOLLET;                     epev.data.fd = cfd;                 epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev);             } else  {                 if (epevs[i].events & EPOLLOUT) {                     continue ;                 }                                    char  buf[5 ];                 int  len = 0 ;                 while ( (len = read(curfd, buf, sizeof (buf))) > 0 ) {                                                               write(STDOUT_FILENO, buf, len);                     write(curfd, buf, len);                 }                 if (len == 0 ) {                     printf ("client closed...." );                 }else  if (len == -1 ) {                     if (errno == EAGAIN) {                         printf ("data over....." );                     }else  {                         perror("read" );                         exit (-1 );                     }                 }             }         }     }     close(lfd);     close(epfd);     return  0 ; } 
 
4.32 UDP通信 
而tcp
#include  <sys/types.h>  #include  <sys/socket.h>  ssize_t  sendto (int  sockfd, const  void  *buf, size_t  len, int  flags, const  struct sockaddr *dest_addr, socklen_t  addrlen)  ;    - 参数:         - sockfd : 通信的fd         - buf : 要发送的数据         - len : 发送数据的长度         - flags : 0          - dest_addr : 通信的另外一端的地址信息         - addrlen : 地址的内存大小 ssize_t  recvfrom (int  sockfd, void  *buf, size_t  len, int  flags, struct sockaddr *src_addr, socklen_t  *addrlen)  ;    - 参数:         - sockfd : 通信的fd         - buf : 接收数据的数组         - len : 数组的大小         - flags : 0          - src_addr : 用来保存另外一端的地址信息,不需要可以指定为NULL          - addrlen : 地址的内存大小 
 
#include  <stdio.h>  #include  <stdlib.h>  #include  <unistd.h>  #include  <string.h>  #include  <arpa/inet.h>  int  main ()   {         int  fd = socket(PF_INET, SOCK_DGRAM, 0 );          if (fd == -1 ) {         perror("socket" );         exit (-1 );     }        struct  sockaddr_in  addr ;     addr.sin_family = AF_INET;     addr.sin_port = htons(9999 );     addr.sin_addr.s_addr = INADDR_ANY;          int  ret = bind(fd, (struct sockaddr *)&addr, sizeof (addr));     if (ret == -1 ) {         perror("bind" );         exit (-1 );     }          while (1 ) {         char  recvbuf[128 ];         char  ipbuf[16 ];         struct  sockaddr_in  cliaddr ;         int  len = sizeof (cliaddr);                  int  num = recvfrom(fd, recvbuf, sizeof (recvbuf), 0 , (struct sockaddr *)&cliaddr, &len);         printf ("client IP : %s, Port : %d\n" ,              inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ipbuf, sizeof (ipbuf)),             ntohs(cliaddr.sin_port));         printf ("client say : %s\n" , recvbuf);                  sendto(fd, recvbuf, strlen (recvbuf) + 1 , 0 , (struct sockaddr *)&cliaddr, sizeof (cliaddr));     }     close(fd);     return  0 ; } 
 
#include  <stdio.h>  #include  <stdlib.h>  #include  <unistd.h>  #include  <string.h>  #include  <arpa/inet.h>  int  main ()   {         int  fd = socket(PF_INET, SOCK_DGRAM, 0 );          if (fd == -1 ) {         perror("socket" );         exit (-1 );     }             struct  sockaddr_in  saddr ;     saddr.sin_family = AF_INET;     saddr.sin_port = htons(9999 );     inet_pton(AF_INET, "127.0.0.1" , &saddr.sin_addr.s_addr);     int  num = 0 ;          while (1 ) {                  char  sendBuf[128 ];         sprintf (sendBuf, "hello , i am client %d \n" , num++);         sendto(fd, sendBuf, strlen (sendBuf) + 1 , 0 , (struct sockaddr *)&saddr, sizeof (saddr));                  int  num = recvfrom(fd, sendBuf, sizeof (sendBuf), 0 , NULL , NULL );         printf ("server say : %s\n" , sendBuf);         sleep(1 );     }     close(fd);     return  0 ; } 
 
可以实现多个客户端。
4.33 UDP广播 向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广播消息都包含一个特殊的IP地址,这个IP中子网内主机标志部分的二进制全部为1(255)。  a.只能在局域网中使用。 b.客户端需要绑定服务器广播使用的端口,才可以接收到广播消息。  
int  setsockopt (int  sockfd, int  level, int  optname,const  void  *optval, socklen_t  optlen)  ;    - sockfd : 文件描述符     - level : SOL_SOCKET     - optname : SO_BROADCAST     - optval : int 类型的值,为1 表示允许广播     - optlen : optval的大小 
 
#include  <stdio.h>  #include  <stdlib.h>  #include  <unistd.h>  #include  <string.h>  #include  <arpa/inet.h>  int  main ()   {         int  fd = socket(PF_INET, SOCK_DGRAM, 0 );     if (fd == -1 ) {         perror("socket" );         exit (-1 );     }             int  op = 1 ;     setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &op, sizeof (op));               struct  sockaddr_in  cliaddr ;     cliaddr.sin_family = AF_INET;     cliaddr.sin_port = htons(9999 );     inet_pton(AF_INET, "192.168.193.255" , &cliaddr.sin_addr.s_addr);          int  num = 0 ;     while (1 ) {                 char  sendBuf[128 ];         sprintf (sendBuf, "hello, client....%d\n" , num++);                  sendto(fd, sendBuf, strlen (sendBuf) + 1 , 0 , (struct sockaddr *)&cliaddr, sizeof (cliaddr));         printf ("广播的数据:%s\n" , sendBuf);         sleep(1 );     }     close(fd);     return  0 ; } 
 
#include  <stdio.h>  #include  <stdlib.h>  #include  <unistd.h>  #include  <string.h>  #include  <arpa/inet.h>  int  main ()   {         int  fd = socket(PF_INET, SOCK_DGRAM, 0 );     if (fd == -1 ) {         perror("socket" );         exit (-1 );     }        struct  in_addr  in ;          struct  sockaddr_in  addr ;     addr.sin_family = AF_INET;     addr.sin_port = htons(9999 );     addr.sin_addr.s_addr = INADDR_ANY;     int  ret = bind(fd, (struct sockaddr *)&addr, sizeof (addr));     if (ret == -1 ) {         perror("bind" );         exit (-1 );     }          while (1 ) {                  char  buf[128 ];                  int  num = recvfrom(fd, buf, sizeof (buf), 0 , NULL , NULL );         printf ("server say : %s\n" , buf);     }     close(fd);     return  0 ; } 
 
4.33 组播(多播) 单播地址标识单个 IP 接口,广播地址标识某个子网的所有 IP 接口,多播地址标识一组 IP 接口。单播和广播是寻址方案的两个极端(要么单个要么全部),多播则意在两者之间提供一种折中方案。多播数据报只应该由对它感兴趣的接口接收,也就是说由运行相应多播会话应用系统的主机上的接口接收。另外,广播一般局限于局域网内使用,而多播则既可以用于局域网,也可以跨广域网使用。 a.组播既可以用于局域网,也可以用于广域网 b.客户端需要加入多播组,才能接收到多播的数据  
int  setsockopt (int  sockfd, int  level, int  optname,const  void  *optval, socklen_t  optlen)  ;         - level : IPPROTO_IP     - optname : IP_MULTICAST_IF     - optval : struct  in_addr      // 客户端加入到多播组:     - level  :  IPPROTO_IP    - optname : IP_ADD_MEMBERSHIP     - optval : struct  ip_mreq  struct  ip_mreq {          struct  in_addr  imr_multiaddr ;            struct  in_addr  imr_interface ;   }; typedef  uint32_t  in_addr_t ;struct  in_addr {     in_addr_t  s_addr; }; 
 
#include  <stdio.h>  #include  <stdlib.h>  #include  <unistd.h>  #include  <string.h>  #include  <arpa/inet.h>  int  main ()   {         int  fd = socket(PF_INET, SOCK_DGRAM, 0 );     if (fd == -1 ) {         perror("socket" );         exit (-1 );     }             struct  in_addr  imr_multiaddr ;          inet_pton(AF_INET, "239.0.0.10" , &imr_multiaddr.s_addr);     setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &imr_multiaddr, sizeof (imr_multiaddr));               struct  sockaddr_in  cliaddr ;     cliaddr.sin_family = AF_INET;     cliaddr.sin_port = htons(9999 );     inet_pton(AF_INET, "239.0.0.10" , &cliaddr.sin_addr.s_addr);          int  num = 0 ;     while (1 ) {                 char  sendBuf[128 ];         sprintf (sendBuf, "hello, client....%d\n" , num++);                  sendto(fd, sendBuf, strlen (sendBuf) + 1 , 0 , (struct sockaddr *)&cliaddr, sizeof (cliaddr));         printf ("组播的数据:%s\n" , sendBuf);         sleep(1 );     }     close(fd);     return  0 ; } 
 
#include  <stdio.h>  #include  <stdlib.h>  #include  <unistd.h>  #include  <string.h>  #include  <arpa/inet.h>  int  main ()   {         int  fd = socket(PF_INET, SOCK_DGRAM, 0 );     if (fd == -1 ) {         perror("socket" );         exit (-1 );     }        struct  in_addr  in ;          struct  sockaddr_in  addr ;     addr.sin_family = AF_INET;     addr.sin_port = htons(9999 );     addr.sin_addr.s_addr = INADDR_ANY;     int  ret = bind(fd, (struct sockaddr *)&addr, sizeof (addr));     if (ret == -1 ) {         perror("bind" );         exit (-1 );     }     struct  ip_mreq  op ;     inet_pton(AF_INET, "239.0.0.10" , &op.imr_multiaddr.s_addr);     op.imr_interface.s_addr = INADDR_ANY;          setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &op, sizeof (op));          while (1 ) {                  char  buf[128 ];                  int  num = recvfrom(fd, buf, sizeof (buf), 0 , NULL , NULL );         printf ("server say : %s\n" , buf);     }     close(fd);     return  0 ; } 
 
4.35 本地套接字通信 
本地套接字的作用:本地的进程间通信 有关系的进程间的通信,父子进程 没有关系的进程间的通信 本地套接字实现流程和网络套接字类似,一般采用TCP的通信流程。  
 
#define  UNIX_PATH_MAX 108     struct  sockaddr_un  {     sa_family_t  sun_family;      char  sun_path[UNIX_PATH_MAX];  }; 1.  创建监听的套接字	int  lfd = socket(AF_UNIX/AF_LOCAL, SOCK_STREAM, 0 ); 2.  监听的套接字绑定本地的套接字文件 -> server端	struct  sockaddr_un  addr ;          bind(lfd, addr, len); 3.  监听    listen(lfd, 100 ); 4.  等待并接受连接请求    struct  sockaddr_un  cliaddr ;     int  cfd = accept(lfd, &cliaddr, len); 5.  通信    接收数据:read/recv     发送数据:write/send 6.  关闭连接	close(); 1.  创建通信的套接字	int  fd = socket(AF_UNIX/AF_LOCAL, SOCK_STREAM, 0 ); 2.  监听的套接字绑定本地的IP 端口    struct  sockaddr_un  addr ;          bind(lfd, addr, len); 3.  连接服务器    struct  sockaddr_un  serveraddr ;     connect(fd, &serveraddr, sizeof (serveraddr)); 4.  通信    接收数据:read/recv     发送数据:write/send 5.  关闭连接    close(); 
 
#include  <stdio.h>  #include  <string.h>  #include  <unistd.h>  #include  <stdlib.h>  #include  <arpa/inet.h>  #include  <sys/un.h>  int  main ()   {    unlink("server.sock" );          int  lfd = socket(AF_LOCAL, SOCK_STREAM, 0 );     if (lfd == -1 ) {         perror("socket" );         exit (-1 );     }          struct  sockaddr_un  addr ;     addr.sun_family = AF_LOCAL;     strcpy (addr.sun_path, "server.sock" );     int  ret = bind(lfd, (struct sockaddr *)&addr, sizeof (addr));     if (ret == -1 ) {         perror("bind" );         exit (-1 );     }          ret = listen(lfd, 100 );     if (ret == -1 ) {         perror("listen" );         exit (-1 );     }          struct  sockaddr_un  cliaddr ;     int  len = sizeof (cliaddr);          int  cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);     if (cfd == -1 ) {         perror("accept" );         exit (-1 );     }     printf ("client socket filename: %s\n" , cliaddr.sun_path);          while (1 ) {         char  buf[128 ];         int  len = recv(cfd, buf, sizeof (buf), 0 );         if (len == -1 ) {             perror("recv" );             exit (-1 );         } else  if (len == 0 ) {             printf ("client closed....\n" );             break ;         } else  if (len > 0 ) {             printf ("client say : %s\n" , buf);             send(cfd, buf, len, 0 );         }     }     close(cfd);     close(lfd);     return  0 ; } 
 
#include  <stdio.h>  #include  <string.h>  #include  <unistd.h>  #include  <stdlib.h>  #include  <arpa/inet.h>  #include  <sys/un.h>  int  main ()   {    unlink("client.sock" );          int  cfd = socket(AF_LOCAL, SOCK_STREAM, 0 );     if (cfd == -1 ) {         perror("socket" );         exit (-1 );     }          struct  sockaddr_un  addr ;     addr.sun_family = AF_LOCAL;     strcpy (addr.sun_path, "client.sock" );     int  ret = bind(cfd, (struct sockaddr *)&addr, sizeof (addr));     if (ret == -1 ) {         perror("bind" );         exit (-1 );     }          struct  sockaddr_un  seraddr ;     seraddr.sun_family = AF_LOCAL;     strcpy (seraddr.sun_path, "server.sock" );     ret = connect(cfd, (struct sockaddr *)&seraddr, sizeof (seraddr));     if (ret == -1 ) {         perror("connect" );         exit (-1 );     }          int  num = 0 ;     while (1 ) {                  char  buf[128 ];         sprintf (buf, "hello, i am client %d\n" , num++);         send(cfd, buf, strlen (buf) + 1 , 0 );         printf ("client say : %s\n" , buf);                  int  len = recv(cfd, buf, sizeof (buf), 0 );         if (len == -1 ) {             perror("recv" );             exit (-1 );         } else  if (len == 0 ) {             printf ("server closed....\n" );             break ;         } else  if (len > 0 ) {             printf ("server say : %s\n" , buf);         }         sleep(1 );     }     close(cfd);     return  0 ; }