Linux网络编程 - UDP网络编程应用



1. UDP网络编程基础

UDP通信流程


对于UDP方式,发送数据时需要显示指定数据包的目的地址,因此不能使用read/write/send/recv函数。

使用sendto和recvfrom


第一个参数为发送的目标socket对象。
第二个参数为欲发送的数据信息。
第三个参数为发送数据的大小。
第四个参数为flags,如send函数所示。
第五个参数欲发送数据的目标地址,其结构体前面已经介绍。
第六个参数为此结构体的大小。


使用AF_INET实现UDP点对点通信示例

接收端代码

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. #include <string.h>  
  4.   
  5. #include <sys/types.h>  
  6.   
  7. #include <netinet/in.h>  
  8.   
  9. #include <sys/socket.h>  
  10.   
  11. #include <errno.h>  
  12.   
  13. #include <stdlib.h>  
  14.   
  15. #include <arpa/inet.h>  
  16.   
  17.   
  18.   
  19. int main(int argc, char **argv)  
  20.   
  21. {  
  22.   
  23.     struct sockaddr_in s_addr;  
  24.   
  25.     struct sockaddr_in c_addr;  
  26.   
  27.     int sock;  
  28.   
  29.     socklen_t addr_len;  
  30.   
  31.     int len;  
  32.   
  33.     char buff[128];  
  34.   
  35.   
  36.   
  37.     if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)   
  38.   
  39.     {  
  40.   
  41.         perror("socket");  
  42.   
  43.         exit(errno);  
  44.   
  45.     } else  
  46.   
  47.         printf("create socket.\n\r");  
  48.   
  49.   
  50.   
  51.     memset(&s_addr, 0, sizeof(struct sockaddr_in));  
  52.   
  53.   
  54.   
  55.     s_addr.sin_family = AF_INET;  
  56.   
  57.     s_addr.sin_port = htons(7838);  
  58.   
  59.     s_addr.sin_addr.s_addr = INADDR_ANY;  
  60.   
  61.   
  62.   
  63.     if ((bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr))) == -1)   
  64.   
  65.     {  
  66.   
  67.         perror("bind");  
  68.   
  69.         exit(errno);  
  70.   
  71.     } else  
  72.   
  73.         printf("bind address to socket.\n\r");  
  74.   
  75.   
  76.   
  77.     addr_len = sizeof(c_addr);  
  78.   
  79.     while (1)   
  80.   
  81.     {  
  82.   
  83.         len = recvfrom(sock, buff, sizeof(buff) - 1, 0,  
  84.   
  85.                 (struct sockaddr *) &c_addr, &addr_len);  
  86.   
  87.         if (len < 0)   
  88.   
  89.         {  
  90.   
  91.             perror("recvfrom");  
  92.   
  93.             exit(errno);  
  94.   
  95.         }  
  96.   
  97.   
  98.   
  99.         buff[len] = '\0';  
  100.   
  101.         printf("recive come from %s:%d message:%s\n\r",  
  102.   
  103.             inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port), buff);  
  104.   
  105.     }  
  106.   
  107.     return 0;  
  108.   
  109. }<span style="color:#339999;">  
  110. </span>  

发送端代码

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <sys/types.h>  
  4. #include <netinet/in.h>  
  5. #include <sys/socket.h>  
  6. #include <errno.h>  
  7. #include <stdlib.h>  
  8. #include <arpa/inet.h>  
  9.   
  10. int main(int argc, char **argv)  
  11. {  
  12.     struct sockaddr_in s_addr;  
  13.     int sock;  
  14.     int addr_len;  
  15.     int len;  
  16.     char buff[128];  
  17.   
  18.     if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)   
  19.     {  
  20.         perror("socket");  
  21.         exit(errno);  
  22.     } else  
  23.         printf("create socket.\n\r");  
  24.   
  25.     s_addr.sin_family = AF_INET;  
  26.     s_addr.sin_port = htons(7838);  
  27.     if (argv[1])  
  28.         s_addr.sin_addr.s_addr = inet_addr(argv[1]);  
  29.     else {  
  30.         printf("input sever ip!\n");  
  31.         exit(0);  
  32.     }  
  33.   
  34.     addr_len = sizeof(s_addr);  
  35.     strcpy(buff, "hello i'm here");  
  36.     len = sendto(sock, buff, strlen(buff), 0,  
  37.             (struct sockaddr *) &s_addr, addr_len);  
  38.     if (len < 0) {  
  39.         printf("\n\rsend error.\n\r");  
  40.         return 3;  
  41.     }  
  42.   
  43.     printf("send success.\n\r");  
  44.     return 0;  
  45. }<span style="color:#339999;">  
  46. </span>  

运行结果

接收端

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. $ ./udp_simple_rcv  
  2. create socket.  
  3. bind address to socket.  
  4. recive come from 172.18.229.60:38412 message:hello i'm here  
发送端

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. $ ./udp_simple_send 172.18.229.60  
  2. create socket.  
  3. send success.<span style="color:#339999;">  
  4. </span>  

2. UDP广播通信

单播、组播与广播基本概念

单播:点对点的传送,即一对一的。TCP方式和UDP方式都可以实现单播,且TCP只能是单播的方式。
广播:处于同一个广播域的所有主机都将收到消息,是一点对多点的方式,广播只能由UDP完成。
组播:消息只会从主机发到加入到同一个组播组(例如230.1.1.1)的主机的对应端口,组播也只能由UDP完成。

广播地址是某网段中主机位全为1的IP地址,例如:

[plain]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. 10.0.0.0/8网段的广播地址为10.255.255.255。  
  2. 172.168.0.0/16的广播地址为172.168.255.255。  
  3. 202.115.1.0/24的广播地址为202.115.1.255。  
  4. 202.115.0.0/23的广播地址为202.115.1.255。  

单播数据帧格式

广播数据帧格式


允许某个socket发送广播消息

使某个socket可以发送广播消息(修改发送端),需要设置该socket属性为SO_BROADCAST,如下所示:
   setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
广播信息(目的MAC地址为FF:FF:FF:FF:FF:FF)会被复制并发到同一个广播域内的每个主机的网卡,网卡收到消息后提交给操作系统去处理,操作系统发现有程序在对应端口接收UDP数据则把消息转给相应的程序去处理,如果没有程序接收来自该端口的UDP消息,则操作系统丢弃该消息。
因此,不管主机是否有程序接收广播消息,广播消息一定会被网卡收到并提交给操作系统去处理,所以会造成网络上流量增大,对不接收广播消息的主机造成一定的负担。

UDP广播通信示例

发送端流程:
以UDP方式创建sokcet对象;
设置socket对象为可发送广播消息属性;
将消息以广播方式发送。
接收端流程:
以UDP方式创建socket对象;
绑定接收数据的端口和IP地址,接收端绑定的该主机的IP地址必须设置为INADDR_ANY。否则不能收到消息;
以阻塞方式接收UDP数据;
输出接收到的广播消息。 

发送端代码

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. #include <string.h>  
  4.   
  5. #include <sys/types.h>  
  6.   
  7. #include <netinet/in.h>  
  8.   
  9. #include <sys/socket.h>  
  10.   
  11. #include <errno.h>  
  12.   
  13. #include <stdlib.h>  
  14.   
  15. #include <arpa/inet.h>  
  16.   
  17. int main(int argc, char **argv)  
  18.   
  19. {  
  20.   
  21.         struct sockaddr_in s_addr;  
  22.   
  23.         int sock;  
  24.   
  25.         int addr_len;  
  26.   
  27.         int len;  
  28.   
  29.         char buff[128];  
  30.   
  31.         int yes;  
  32.   
  33.         if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)    
  34.   
  35.         {  
  36.   
  37.                 perror("socket");  
  38.   
  39.                 exit(EXIT_FAILURE);  
  40.   
  41.         } else  
  42.   
  43.                 printf("create socket.\n\r");  
  44.   
  45.         yes = 1;  
  46.   
  47.         setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));    
  48.   
  49.                                           
  50.   
  51.         s_addr.sin_family = AF_INET;  
  52.   
  53.         s_addr.sin_port = htons(8080);  
  54.   
  55.         if (argv[1])  
  56.   
  57.                 s_addr.sin_addr.s_addr = inet_addr(argv[1]);   
  58.   
  59.         else {  
  60.   
  61.                 printf("input sever ip!\n");  
  62.   
  63.                 exit(0);  
  64.   
  65.         }  
  66.   
  67.         addr_len = sizeof(s_addr);  
  68.   
  69.         strcpy(buff, "hello message");        
  70.   
  71.         len = sendto(sock, buff, strlen(buff), 0,         
  72.   
  73.                         (struct sockaddr *) &s_addr, addr_len);  
  74.   
  75.         if (len < 0) {  
  76.   
  77.                 printf("\n\rsend error.\n\r");  
  78.   
  79.                 exit(EXIT_FAILURE);  
  80.   
  81.         }  
  82.   
  83.         printf("send success.\n\r");  
  84.   
  85.         return 0;  
  86.   
  87. }  

接收端代码

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. #include <string.h>  
  4.   
  5. #include <sys/types.h>  
  6.   
  7. #include <netinet/in.h>  
  8.   
  9. #include <sys/socket.h>  
  10.   
  11. #include <errno.h>  
  12.   
  13. #include <stdlib.h>  
  14.   
  15. #include <arpa/inet.h>  
  16.   
  17. int main(int argc, char **argv)  
  18.   
  19. {  
  20.   
  21.     struct sockaddr_in s_addr;  
  22.   
  23.     struct sockaddr_in c_addr;  
  24.   
  25.     int sock;  
  26.   
  27.     socklen_t addr_len;  
  28.   
  29.     int len;  
  30.   
  31.     char buff[128];  
  32.   
  33.     if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)   
  34.   
  35.     {  
  36.   
  37.         perror("socket");  
  38.   
  39.         exit(EXIT_FAILURE);  
  40.   
  41.     } else  
  42.   
  43.         printf("create socket.\n\r");  
  44.   
  45.     memset(&s_addr, 0, sizeof(struct sockaddr_in));  
  46.   
  47.   
  48.   
  49.     s_addr.sin_family = AF_INET;                              
  50.   
  51.     s_addr.sin_port = htons(8080);    
  52.   
  53.     s_addr.sin_addr.s_addr = INADDR_ANY;  
  54.   
  55.     if ((bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr))) == -1)   
  56.   
  57.     {  
  58.   
  59.         perror("bind");  
  60.   
  61.         exit(EXIT_FAILURE);  
  62.   
  63.     } else  
  64.   
  65.         printf("bind address to socket.\n\r");  
  66.   
  67.     addr_len = sizeof(c_addr);  
  68.   
  69.     while (1)   
  70.   
  71.     {  
  72.   
  73.         len = recvfrom(sock, buff, sizeof(buff) - 1, 0,   
  74.   
  75.                 (struct sockaddr *) &c_addr, &addr_len);  
  76.   
  77.         if (len < 0)   
  78.   
  79.         {  
  80.   
  81.             perror("recvfrom");  
  82.   
  83.             exit(EXIT_FAILURE);  
  84.   
  85.         }  
  86.   
  87.         buff[len] = '\0';  
  88.   
  89.         printf("recive come from %s:%d message:%s\n\r",  
  90.   
  91.             inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port), buff);  
  92.   
  93.     }  
  94.   
  95.     return 0;  
  96.   
  97. }  

运行结果

发送端(运行四次)

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. $ ./udp_brodcast_send 172.18.229.60  
  2. create socket.  
  3. send success.  
接收端(可以在同一网段下运行多个接收端)

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. $ ./udp_brodcast_rcv  
  2. create socket.  
  3. bind address to socket.  
  4. recive come from 172.18.229.60:41864 message:hello message  
  5. recive come from 172.18.229.60:58463 message:hello message  
  6. recive come from 172.18.229.60:46442 message:hello message  
  7. recive come from 172.18.229.60:59744 message:hello message<span style="color:#339999;">  
  8. </span>  

3. UDP组播通信

组播地址 

组播地址范围是D类IP地址,即224.0.0.1-239.255.255.255。 

组播MAC地址产生办法 


组播数据帧


组播通信编程

在传播时,和广播一样,组播消息会被复制的发到网络上所有主机的网卡,但只有宣布加入该组(例如230.1.1.1)的主机的网卡才会把数据提交给操作系统去处理。如果没有加入组,则网卡直接将数据丢弃。

如果某socket期望接收组播消息,需要设置该socket对象属性,如下所示:
 setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(struct ip_mreq));

组播通信示例

发送端流程如下:
以UDP方式创建socket对象;
初始化发送数据所目的地址和端口;
绑定本机IP地址和端口;
向组播组内所有主机发送数据。
接收端流程:
以UDP方式创建socket,获取组播地址和本机地址,将当前主机加入到该组中;
绑定本机IP地址和端口;
接收消息并输出。

发送端代码

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <sys/types.h>  
  2.   
  3. #include <sys/socket.h>  
  4.   
  5. #include <arpa/inet.h>  
  6.   
  7. #include <stdio.h>  
  8.   
  9. #include <stdlib.h>  
  10.   
  11. #include <string.h>  
  12.   
  13. #define BUFLEN 255  
  14.   
  15. int main(int argc, char **argv)  
  16.   
  17. {  
  18.   
  19.         struct sockaddr_in peeraddr, myaddr;  
  20.   
  21.         int sockfd;  
  22.   
  23.         char recmsg[BUFLEN + 1];  
  24.   
  25.         unsigned int socklen;  
  26.   
  27.         sockfd = socket(AF_INET, SOCK_DGRAM, 0);      
  28.   
  29.         if (sockfd < 0)  
  30.   
  31.         {  
  32.   
  33.                 printf("socket creating error\n");  
  34.   
  35.                 exit(EXIT_FAILURE);  
  36.   
  37.         }  
  38.   
  39.         socklen = sizeof(struct sockaddr_in);  
  40.   
  41.         memset(&peeraddr, 0, socklen);  
  42.   
  43.         peeraddr.sin_family = AF_INET;  
  44.   
  45.         peeraddr.sin_port = htons(8080);          
  46.   
  47.         if (argv[1]) {                    
  48.   
  49.                 if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) {  
  50.   
  51.                         printf("wrong group address!\n");  
  52.   
  53.                         exit(EXIT_FAILURE);  
  54.   
  55.                 }  
  56.   
  57.         }   
  58.   
  59.         else {  
  60.   
  61.                 printf("no group address!\n");  
  62.   
  63.                 exit(EXIT_FAILURE);  
  64.   
  65.         }  
  66.   
  67.         memset(&myaddr, 0, socklen);  
  68.   
  69.         myaddr.sin_family = AF_INET;  
  70.   
  71.         myaddr.sin_port = htons(23456);       
  72.   
  73.         if (argv[2]) {                
  74.   
  75.                 if (inet_pton(AF_INET, argv[2], &myaddr.sin_addr) <= 0)   
  76.   
  77.                 {  
  78.   
  79.                         printf("self ip address error!\n");  
  80.   
  81.                         exit(EXIT_FAILURE);  
  82.   
  83.                 }  
  84.   
  85.         } else  
  86.   
  87.                 myaddr.sin_addr.s_addr = INADDR_ANY;  
  88.   
  89.         if (bind(sockfd, (struct sockaddr *) &myaddr,sizeof(struct sockaddr_in)) == -1)   
  90.   
  91.         {                                 
  92.   
  93.                 printf("Bind error\n");  
  94.   
  95.                 exit(EXIT_FAILURE);  
  96.   
  97.         }  
  98.   
  99.         for (;;) {  
  100.   
  101.                 bzero(recmsg, BUFLEN + 1);  
  102.   
  103.                 printf("input message to send:");  
  104.   
  105.                 if (fgets(recmsg, BUFLEN, stdin) == (char *) EOF)     
  106.   
  107.                         exit(EXIT_FAILURE);;  
  108.   
  109.                 if (sendto(sockfd, recmsg, strlen(recmsg), 0,(struct sockaddr *) &peeraddr,  
  110.   
  111.                         sizeof(struct sockaddr_in)) < 0)               
  112.   
  113.                 {  
  114.   
  115.                         printf("sendto error!\n");  
  116.   
  117.                        exit(EXIT_FAILURE);;  
  118.   
  119.                 }  
  120.   
  121.                 printf("sned message:%s", recmsg);    
  122.   
  123.         }  
  124.   
  125. }  

接收端代码

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <sys/types.h>  
  2.   
  3. #include <sys/socket.h>  
  4.   
  5. #include <arpa/inet.h>  
  6.   
  7. #include <stdio.h>  
  8.   
  9. #include <stdlib.h>  
  10.   
  11. #include <string.h>  
  12.   
  13. #include <netdb.h>  
  14.   
  15. #include <errno.h>  
  16.   
  17. #define BUFLEN 255  
  18.   
  19.   
  20.   
  21. int main(int argc, char **argv)  
  22.   
  23. {  
  24.   
  25.     struct sockaddr_in peeraddr;  
  26.   
  27.     struct in_addr ia;  
  28.   
  29.     int sockfd;  
  30.   
  31.     char recmsg[BUFLEN + 1];  
  32.   
  33.     unsigned int socklen, n;  
  34.   
  35.     struct hostent *group;  
  36.   
  37.     struct ip_mreq mreq;  
  38.   
  39.   
  40.   
  41.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);  
  42.   
  43.     if (sockfd < 0)   
  44.   
  45.     {  
  46.   
  47.         printf("socket creating err in udptalk\n");  
  48.   
  49.         exit(EXIT_FAILURE);  
  50.   
  51.     }  
  52.   
  53.     bzero(&mreq, sizeof(struct ip_mreq));  
  54.   
  55.     if (argv[1])   
  56.   
  57.     {  
  58.   
  59.         if ((group = gethostbyname(argv[1])) == (struct hostent *) 0)   
  60.   
  61.         {  
  62.   
  63.             perror("gethostbyname");  
  64.   
  65.             exit(EXIT_FAILURE);  
  66.   
  67.         }  
  68.   
  69.     }   
  70.   
  71.     else   
  72.   
  73.     {  
  74.   
  75.         printf("you should give me a group address, 224.0.0.0-239.255.255.255\n");  
  76.   
  77.         exit(EXIT_FAILURE);  
  78.   
  79.     }  
  80.   
  81.     bcopy((void *) group->h_addr, (void *) &ia, group->h_length);  
  82.   
  83.     bcopy(&ia, &mreq.imr_multiaddr.s_addr, sizeof(struct in_addr));   
  84.   
  85.     //mreq.imr_interface.s_addr = htonl(INADDR_ANY);  
  86.   
  87.     if (argv[2]) {  
  88.   
  89.         if (inet_pton(AF_INET, argv[2], &mreq.imr_interface.s_addr) <= 0)   
  90.   
  91.         {     
  92.   
  93.             printf("Wrong dest IP address!\n");  
  94.   
  95.             exit(EXIT_FAILURE);  
  96.   
  97.         }  
  98.   
  99.     }   
  100.       
  101.   
  102.     if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(struct ip_mreq)) == -1)   
  103.   
  104.     {  
  105.   
  106.         perror("setsockopt");  
  107.   
  108.         exit(EXIT_FAILURE);  
  109.   
  110.     }  
  111.   
  112.     socklen = sizeof(struct sockaddr_in);  
  113.   
  114.     memset(&peeraddr, 0, socklen);  
  115.   
  116.     peeraddr.sin_family = AF_INET;  
  117.   
  118.     peeraddr.sin_port = htons(8080);  
  119.   
  120.       
  121.   
  122.     if (argv[1]) {  
  123.   
  124.         if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0)   
  125.   
  126.         {     
  127.   
  128.             printf("Wrong dest IP address!\n");  
  129.   
  130.             exit(EXIT_FAILURE);  
  131.   
  132.         }  
  133.   
  134.     }   
  135.   
  136.     else   
  137.   
  138.     {  
  139.   
  140.         printf("no group address given, 224.0.0.0-239.255.255.255\n");  
  141.   
  142.         exit(EXIT_FAILURE);  
  143.   
  144.     }  
  145.   
  146.     if (bind(sockfd, (struct sockaddr *) &peeraddr,sizeof(struct sockaddr_in)) == -1)   
  147.   
  148.     {  
  149.   
  150.         printf("Bind error\n");  
  151.   
  152.         exit(EXIT_FAILURE);  
  153.   
  154.     }  
  155.   
  156.   
  157.     for (;;)   
  158.   
  159.     {  
  160.   
  161.         bzero(recmsg, BUFLEN + 1);  
  162.   
  163.         n = recvfrom(sockfd, recmsg, BUFLEN, 0,(struct sockaddr *) &peeraddr, &socklen);  
  164.   
  165.         if (n < 0)   
  166.   
  167.         {  
  168.   
  169.             printf("recvfrom err in udptalk!\n");  
  170.   
  171.             exit(EXIT_FAILURE);  
  172.   
  173.         }   
  174.   
  175.         else   
  176.   
  177.         {  
  178.   
  179.   
  180.   
  181.             recmsg[n] = 0;  
  182.   
  183.             printf("peer:%s", recmsg);  
  184.   
  185.         }  
  186.   
  187.     }  
  188.   
  189. }   

运行结果

接收端

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. $ ./udp_group_brodcast_rcv 230.1.1.1 172.18.229.60  
  2. peer:hello  
  3. peer:send test  
  4. peer:yes  
  5. peer:end  
发送端

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. $ ./udp_group_brodcast_send 230.1.1.1 172.18.229.60  
  2. input message to send:hello  
  3. sned message:hello  
  4. input message to send:send test  
  5. sned message:send test  
  6. input message to send:yes  
  7. sned message:yes  
  8. input message to send:end  
  9. sned message:end<span style="color:#339999;">  
  10. </span>  

4. socket信号驱动(UDP)

SIGIO信号处理机制 

为了使一个套接字使用信号驱动I/O操作,需要至少以下三步操作:
(1)安装SIGIO信号,在该处理函数中设定处理办法。
(2)套接字的拥有者必须被设定。一般来说是使用fcntl 函数的F_SETOWN 参数来进行设定拥有者。
(3)套接字必须被允许使用异步I/O。一般是通过调用fcntl 函数的F_SETFL 命令,将即设置为O_ASYNC。
SIGIO 的缺省动作是被忽略。在设置套接字的属主之前必须将SIGIO 的信号处理函数设好,如果以相反的顺序调用这两个函数调用,那么在fcntl 函数调用之后,signal 函数调用之前就有一小段时间程序可能接收到SIGIO 信号。那样的话,信号将会被丢弃。

UDP 套接字的SIGIO 信号

套接字收到了一个数据报的数据包。
套接字发生了异步错误。

TCP 套接字的SIGIO 信号 

对于一个TCP 套接字来说, SIGIO信号发生的几率太高了, SIGIO 信号不能告诉究竟发生了什么事情。在TCP连接中, SIGIO信号将会在这个时候产生:
在一个监听某个端口的套接字上成功的建立了一个新连接。
一个断线的请求被成功的初始化。
一个断线的请求成功的结束。
套接字的某一个通道(发送通道或是接收通道)被关闭。
套接字接收到新数据。
套接字将数据发送出去。

发生了一个异步I/O 的错误。

信号驱动方式处理UDP数据示例

服务器代码

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. #include <stdlib.h>  
  4.   
  5. #include <unistd.h>  
  6.   
  7. #include <fcntl.h>  
  8.   
  9. #include <errno.h>  
  10.   
  11. #include <string.h>  
  12.   
  13. #include <signal.h>  
  14.   
  15. #include <netinet/in.h>  
  16.   
  17. #include <arpa/inet.h>  
  18.   
  19. #include <sys/socket.h>  
  20.   
  21. #include <sys/types.h>  
  22.   
  23. #include <sys/ioctl.h>  
  24.   
  25. #define MAX_LENTH 1500  
  26.   
  27.   
  28.   
  29. static int nqueue = 0;  
  30.   
  31. void sigio_handler(int signum)  
  32.   
  33. {  
  34.   
  35.     if (signum == SIGIO)  
  36.   
  37.         nqueue++;  
  38.   
  39.     printf("signum=%d,nqueue=%d\n",signum,nqueue);  
  40.   
  41.   
  42.   
  43.     return;  
  44.   
  45. }   
  46.   
  47. static recv_buf[MAX_LENTH];  
  48.   
  49. int main(int argc, char *argv[])   
  50.   
  51. {  
  52.   
  53.     int sockfd, on = 1;  
  54.   
  55.     struct sigaction action;  
  56.   
  57.     sigset_t newmask, oldmask;   
  58.   
  59.     struct sockaddr_in ser_addr;  
  60.   
  61.   
  62.     if(argc!=3)  
  63.   
  64.     {  
  65.   
  66.         printf("use: %s ip_add port\n",argv[0]);  
  67.   
  68.         exit(EXIT_FAILURE);  
  69.   
  70.     }  
  71.   
  72.     memset(&ser_addr, 0, sizeof(ser_addr));  
  73.   
  74.     ser_addr.sin_family = AF_INET;  
  75.   
  76.   
  77.   
  78.     if (inet_aton(argv[1], (struct in_addr *) & ser_addr.sin_addr.s_addr) == 0)  
  79.   
  80.     {     
  81.   
  82.         perror(argv[1]);  
  83.   
  84.         exit(EXIT_FAILURE);  
  85.   
  86.     }  
  87.   
  88.     ser_addr.sin_port = htons(atoi(argv[2]));     
  89.   
  90.       
  91.   
  92.     if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {  
  93.   
  94.         perror("Create socket failed");  
  95.   
  96.         exit(EXIT_FAILURE);  
  97.   
  98.     }  
  99.   
  100.     if (bind(sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) == -1) {  
  101.   
  102.         perror("Bind socket failed");  
  103.   
  104.         exit(EXIT_FAILURE);  
  105.   
  106.     }  
  107.   
  108.   
  109.     memset(&action, 0, sizeof(action));  
  110.   
  111.     action.sa_handler = sigio_handler;  
  112.   
  113.     action.sa_flags = 0;  
  114.   
  115.     sigaction(SIGIO, &action, NULL);  
  116.   
  117.     if (fcntl(sockfd, F_SETOWN, getpid()) == -1) {  
  118.   
  119.         perror("Fcntl F_SETOWN ");  
  120.   
  121.         exit(EXIT_FAILURE);   
  122.   
  123.     }  
  124.   
  125.     if (ioctl(sockfd, FIOASYNC, &on) == -1) {  
  126.   
  127.         perror("Ioctl FIOASYNC");  
  128.   
  129.         exit(EXIT_FAILURE);   
  130.   
  131.     }  
  132.   
  133.     sigemptyset(&oldmask);  
  134.   
  135.     sigemptyset(&newmask);  
  136.   
  137.     sigaddset(&newmask, SIGIO);  
  138.   
  139.     printf("get ready\n");  
  140.   
  141.     while (1)   
  142.   
  143.     {  
  144.   
  145.         int len;  
  146.   
  147.         sigprocmask(SIG_BLOCK, &newmask, &oldmask);  
  148.   
  149.         while (nqueue == 0)  
  150.   
  151.             sigsuspend(&oldmask);  
  152.   
  153.         memset(recv_buf,'\0',MAX_LENTH);  
  154.   
  155.         len = recv(sockfd, recv_buf, MAX_LENTH, MSG_DONTWAIT);  
  156.   
  157.         if (len == -1 && errno == EAGAIN)   
  158.   
  159.             nqueue = 0;  
  160.   
  161.         sigprocmask(SIG_SETMASK, &oldmask, NULL);  
  162.   
  163.         if (len >= 0)  
  164.   
  165.             printf("recv %d byte(s),msg is %s\n", len,recv_buf);  
  166.   
  167.     }  
  168.   
  169. }<span style="color:#339999;">  
  170. </span>  

客户端代码

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2.   
  3. #include <string.h>  
  4.   
  5. #include <stdlib.h>  
  6.   
  7. #include <unistd.h>  
  8.   
  9. #include <fcntl.h>  
  10.   
  11. #include <errno.h>  
  12.   
  13. #include <netinet/in.h>       /*socket address struct*/  
  14.   
  15. #include <arpa/inet.h>            /*host to network convertion*/  
  16.   
  17. #include <sys/socket.h>  
  18.   
  19. #include <signal.h>  
  20.   
  21. #define MAX_LENTH 1500  
  22.   
  23.   
  24.   
  25. int main(int argc,char *argv[])  
  26.   
  27. {  
  28.   
  29.     struct sockaddr_in addr;  
  30.   
  31.     int sock_fd,ret;  
  32.   
  33.     char snd_buf[MAX_LENTH];  
  34.   
  35.     if(argc!=3)  
  36.   
  37.     {  
  38.   
  39.         printf("use: %s ip_add port\n",argv[0]);  
  40.   
  41.         exit(EXIT_FAILURE);           
  42.   
  43.     }  
  44.   
  45.     memset(&addr,0,sizeof(addr));  
  46.   
  47.     addr.sin_family =  AF_INET;  
  48.   
  49.     if (inet_aton(argv[1], (struct in_addr *)&addr.sin_addr.s_addr) == 0)  
  50.   
  51.     {     
  52.   
  53.         perror(argv[1]);  
  54.   
  55.         exit(EXIT_FAILURE);  
  56.   
  57.     }  
  58.   
  59.     addr.sin_port = htons(atoi(argv[2]));         
  60.   
  61.     if((sock_fd = socket(AF_INET,SOCK_DGRAM,0))==-1)  
  62.   
  63.     {  
  64.   
  65.         perror("socket");  
  66.   
  67.         exit(EXIT_FAILURE);  
  68.   
  69.     }  
  70.   
  71.     if(ret = connect(sock_fd,(struct sockaddr *)&addr,sizeof(addr))==-1)  
  72.   
  73.     {  
  74.   
  75.         perror("Connect");  
  76.   
  77.         exit(EXIT_FAILURE);       
  78.   
  79.     }         
  80.   
  81.     while(1)  
  82.   
  83.     {  
  84.   
  85.         printf("input msg to send:");  
  86.   
  87.         memset(snd_buf,'\0',MAX_LENTH);  
  88.   
  89.         fgets(snd_buf,MAX_LENTH-1,stdin);  
  90.   
  91.         write(sock_fd,snd_buf,MAX_LENTH-1);  
  92.   
  93.     }  
  94. }  

运行结果

服务器

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. $ ./sigio_server 172.18.229.60 9000  
  2. get ready  
  3. signum=29,nqueue=1  
  4. recv 1499 byte(s),msg is hello  
  5.   
  6. signum=29,nqueue=1  
  7. recv 1499 byte(s),msg is client test  
  8.   
  9. signum=29,nqueue=1  
  10. recv 1499 byte(s),msg is yes  
  11.   
  12. signum=29,nqueue=1  
  13. recv 1499 byte(s),msg is end  
客户端

[cpp]  view plain  copy
  print ? 在CODE上查看代码片 派生到我的代码片
  1. $ ./sigio_client 172.18.229.60 9000  
  2. input msg to send:hello  
  3. input msg to send:client test  
  4. input msg to send:yes  
  5. input msg to send:end<span style="color:#339999;">  
  6. </span>  

原文链接

http://blog.csdn.net/geng823/article/details/41865507


sigprocmask()函数实例详解 (2012-07-26 22:48:37)转载▼
功能描述:设定对信号屏蔽集内的信号的处理方式(阻塞或不阻塞)。
用法:
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数:
how:用于指定信号修改的方式,可能选择有三种:
SIG_BLOCK //加入信号到进程屏蔽。
SIG_UNBLOCK //从进程屏蔽里将信号删除。
SIG_SETMASK //将set的值设定为新的进程屏蔽。
set:为指向信号集的指针,在此专指新设的信号集,如果仅想读取现在的屏蔽值,可将其置为NULL。
oldset:也是指向信号集的指针,在此存放原来的信号集。
返回说明:
成功执行时,返回0。失败返回-1,errno被设为EINVAL。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{
   printf("Deal SIGINT");  //SIGINT信号处理函数
}
 
int main()
{
sigset_t newmask;
sigset_t oldmask;
sigset_t pendmask;
struct sigaction act;
act.sa_handler = handler;  //handler为信号处理函数首地址
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0);  //信号捕捉函数,捕捉Ctrl+C


sigemptyset(&newmask);//初始化信号量集
sigaddset(&newmask, SIGINT);//将SIGINT添加到信号量集中


sigprocmask(SIG_BLOCK, &newmask, &oldmask);//将newmask中的SIGINT阻塞掉,并保存当前信号屏蔽字到Oldmask


sleep (5);//休眠5秒钟,说明:在5s休眠期间,任何SIGINT信号都会被阻塞,如果在5s内收到任何键盘的Ctrl+C信号,则此时会把这些信息存在内核的队列中,等待5s结束后,可能要处理此信号。 
sigpending(&pendmask);//检查信号是悬而未决的,
if (sigismember(&pendmask, SIGINT))//SIGINT是悬而未决的。所谓悬而未决,是指SIGINT被阻塞还没有被处理
{
printf("/nSIGINT pending/n");
}
sigprocmask(SIG_SETMASK, &oldmask, NULL);//恢复被屏蔽的信号SIGINT
 
 //此处开始处理信号,调用信号处理函数
printf("SIGINT unblocked/n");


return (0);
}
注意:上面还有一种方式:
sigprocmask(SIG_BLOCK, &newmask, NULL); //阻塞
sigprocmask(SIG_UNBLOCK, &newmask, NULL);//取消阻塞


sigsuspend()函数作用详解

1)头文件:#include <signal.h>

2)一个保护临界区代码的错误实例:(sigprocmask()和pause()实现)

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

 

void handler(int sig)    //信号处理函数的实现

{

   printf("SIGINT sig");

}

int main()

{

    sigset_t new,old;

    struct sigaction act;

    act.sa_handler = handler;  //信号处理函数handler

    sigemptyset(&act.sa_mask);

    act.sa_flags = 0;

    sigaction(SIGINT, &act, 0);  //准备捕捉SIGINT信号

    sigemptyset(&new);

    sigaddset(&new, SIGINT);

    sigprocmask(SIG_BLOCK, &new, &old);  //将SIGINT信号阻塞,同时保存当前信号集

    printf("Blocked");

    sigprocmask(SIG_SETMASK, &old, NULL);  //取消阻塞

    pause();

    return 0;

}

上面实例的问题是:本来期望pause()之后,来SIGINT信号,可以结束程序;可是,如果当“取消阻塞”和“pause”之间,正好来了SIGINT信号,结果程序因为pause的原因会一直挂起。。。

解决的方式,当然是sigsuspend()函数了。

 

3)使用sigsuspend()的程序

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

void handler(int sig)   //信号处理程序

{

   if(sig == SIGINT)

      printf("SIGINT sig");

   else if(sig == SIGQUIT)

      printf("SIGQUIT sig");

   else

      printf("SIGUSR1 sig");

}

 

int main()

{

    sigset_t new,old,wait;   //三个信号集

    struct sigaction act;

    act.sa_handler = handler;

    sigemptyset(&act.sa_mask);

    act.sa_flags = 0;

    sigaction(SIGINT, &act, 0);    //可以捕捉以下三个信号:SIGINT/SIGQUIT/SIGUSR1

    sigaction(SIGQUIT, &act, 0);

    sigaction(SIGUSR1, &act, 0);

   

    sigemptyset(&new);

    sigaddset(&new, SIGINT);  //SIGINT信号加入到new信号集中

    sigemptyset(&wait);

    sigaddset(&wait, SIGUSR1);  //SIGUSR1信号加入wait

    sigprocmask(SIG_BLOCK, &new, &old);       //将SIGINT阻塞,保存当前信号集到old中

   

    //临界区代码执行    

  

    if(sigsuspend(&wait) != -1)  //程序在此处挂起;用wait信号集替换new信号集。即:过来SIGUSR1信  号,阻塞掉,程序继续挂起;过来其他信号,例如SIGINT,则会唤醒程序。执行sigsuspend的原子操作。注意:如果“sigaddset(&wait, SIGUSR1);”这句没有,则此处不会阻塞任何信号,即过来任何信号均会唤醒程序。

        printf("sigsuspend error");

    printf("After sigsuspend");

    sigprocmask(SIG_SETMASK, &old, NULL);

    return 0;

}

sigsuspend的原子操作是:

(1)设置新的mask阻塞当前进程(上面是用wait替换new,即阻塞SIGUSR1信号)

(2)收到SIGUSR1信号,阻塞,程序继续挂起;收到其他信号,恢复原先的mask(即包含SIGINT信号的)。

(3)调用该进程设置的信号处理函数(程序中如果先来SIGUSR1信号,然后过来SIGINT信号,则信号处理函数会调用两次,打印不同的内容。第一次打印SIGINT,第二次打印SIGUSR1,因为SIGUSR1是前面阻塞的)

(4)待信号处理函数返回,sigsuspend返回了。(sigsuspend将捕捉信号和信号处理函数集成到一起了)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值