LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 4473|回复: 6

实现SIGIO驱动的socket编程例子(自己写的,希望能对大家有参考价值)

[复制链接]
发表于 2005-8-18 18:58:48 | 显示全部楼层 |阅读模式
采用异步信号模式进行socket的编程,对于提供程序的运行效率很有好处.
该两个程序版权属于realtang,您可以在遵循GPL授权的许可下任意使用。
接收端:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <errno.h>
  6. #include <netinet/in.h>                 /*socket address struct*/
  7. #include <arpa/inet.h>                        /*host to network convertion*/
  8. #include <sys/socket.h>
  9. #include <sys/types.h>
  10. #include <signal.h>
  11. #include <sys/ioctl.h>
  12. #define MAX_TRANSPORT_LENTH 512

  13. static int g_var = 0;
  14. static int g_skt = 0;
  15. void sig_handler(int signum)
  16. {
  17.         char  buf[MAX_TRANSPORT_LENTH+1] = "";
  18.         int len = 0;
  19.         len = read(g_skt,buf,MAX_TRANSPORT_LENTH);
  20.         if (len<0)
  21.         {
  22.                 perror("Read socket failed");
  23.                 exit(-1);       
  24.         }
  25.         else
  26.         {
  27.                 printf("In SIGIO handler,got msg:%s\n",buf);
  28.         }       
  29. }

  30. int main()
  31. {
  32.         struct sockaddr_in addr;
  33.         memset(&addr,0,sizeof(addr));
  34.         addr.sin_family =  AF_INET;
  35.         addr.sin_addr.s_addr = INADDR_ANY;
  36.         addr.sin_port = htons(50001);
  37.        
  38.         signal(SIGIO,sig_handler);
  39.        
  40.         g_skt = socket(AF_INET,SOCK_DGRAM,0);
  41.         if(g_skt == -1)
  42.         {
  43.                 perror("Create socket failed");
  44.                 exit(-1);
  45.         }
  46.        
  47.         int len = sizeof(addr);
  48.         int ret = 0;

  49.         int on = 1;
  50.     ret = fcntl(g_skt, F_SETOWN, getpid());//Set process or process group ID to receive SIGIO signals
  51.         if(-1 == ret)
  52.         {
  53.                 perror("Fcntl F_SETOWN failed");
  54.                 exit(-1);               
  55.         }
  56.     ret = ioctl(g_skt, FIOASYNC, &on);
  57.         if(-1 == ret)   
  58.         {
  59.                 perror("Fcntl FIOASYNC failed");
  60.                 exit(-1);               
  61.         }   
  62.     ret = ioctl(g_skt, FIONBIO, &on);
  63.         if(-1 == ret)   
  64.         {
  65.                 perror("ioctl FIONBIO failed");
  66.                 exit(-1);               
  67.         }

  68.         ret = bind(g_skt,(struct sockaddr *)&addr,sizeof(addr));
  69.         if(-1 == ret)
  70.         {
  71.                 perror("Bind socket failed");
  72.                 exit(-1);
  73.         }                       
  74.         while(1)
  75.         {
  76.                 printf("I am running\n");
  77.                 sleep(2);               
  78.         }       
  79.         close(g_skt);       
  80. }

复制代码

发送端:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <errno.h>
  6. #include <netinet/in.h>                 /*socket address struct*/
  7. #include <arpa/inet.h>                        /*host to network convertion*/
  8. #include <sys/socket.h>
  9. #include <signal.h>
  10. #define MAX_TRANSPORT_LENTH 512

  11. int main()
  12. {
  13.         struct sockaddr_in addr;
  14.         memset(&addr,0,sizeof(addr));
  15.         addr.sin_family =  AF_INET;
  16.         addr.sin_addr.s_addr = inet_addr("192.168.1.106");
  17.         addr.sin_port = htons(50001);
  18.        
  19.         int sock;
  20.         sock = socket(AF_INET,SOCK_DGRAM,0);
  21.         if(sock == -1)
  22.         {
  23.                 perror("Create socket failed");
  24.                 exit(-1);
  25.         }
  26.        
  27.         int ret;
  28.         ret = connect(sock,(struct sockaddr *)&addr,sizeof(addr));
  29.         if(ret == -1)
  30.         {
  31.                 perror("Connect socket failed");
  32.                 exit(-1);               
  33.         }               
  34.         while(1)
  35.         {
  36.                 printf("Will send messge to server\n");
  37.                 write(sock,"Some unknown infomation\n",MAX_TRANSPORT_LENTH);
  38.                 sleep(1);
  39.         }
  40.        
  41. }

复制代码
 楼主| 发表于 2005-8-18 19:03:36 | 显示全部楼层
上述的程序采用了UDP而不采用TCP是因为TCP的通讯中产生SIGIO信号的情况实在太复杂了。不如UDP来得纯粹,只在接收到信息和检测到异常时才触发SIGIO信号。
回复 支持 反对

使用道具 举报

发表于 2005-8-18 21:15:59 | 显示全部楼层
哦,这个程序的意思是将服务器端的主线程从等待数据中解放出来,而由信号处理函数来处理该端口收到的数据
right?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-8-19 09:04:29 | 显示全部楼层
Post by rickxbx
哦,这个程序的意思是将服务器端的主线程从等待数据中解放出来,而由信号处理函数来处理该端口收到的数据
right?

可以这么说,但不是很全面。这个程序的主要作用是对于某些应用,他基本上埋头干自己的事,对于外界的信息需要知道的很少,但偶尔也会收收发发信息来控制自己运行的轨迹的时候,就可以采用异步信号的方式。因为这样做效率比较高。
如果是采用单线程,非阻塞方式,会浪费很多cpu循环。
如果是采用多线程,会增加资源的利用以及上下文切换的开销。况且,linux对于线程的调度上,对于某些实时线程还存在一些问题。
回复 支持 反对

使用道具 举报

发表于 2005-8-19 12:06:41 | 显示全部楼层
Post by realtang
但偶尔也会收收发发信息

如何做到?
回复 支持 反对

使用道具 举报

发表于 2005-8-20 00:42:21 | 显示全部楼层
modified version


  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <errno.h>
  6. #include <string.h>
  7. #include <signal.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <sys/socket.h>
  11. #include <sys/types.h>
  12. #include <sys/ioctl.h>
  13. #define MAX_LENTH 1500

  14. static int nqueue = 0;
  15. void sigio_handler(int signum)
  16. {
  17.         if (signum = SIGIO)
  18.                 nqueue++;
  19.         return;
  20. }

  21. static recv_buf[MAX_LENTH];
  22. int main(int argc, char *argv[])
  23. {
  24.         int sockfd, on = 1;
  25.         struct sigaction action;
  26.         sigset_t newmask, oldmask;
  27.         struct sockaddr_in addr;

  28.         memset(&addr, 0, sizeof(addr));
  29.         addr.sin_family =  AF_INET;
  30.         addr.sin_addr.s_addr = INADDR_ANY;
  31.         addr.sin_port = htons(50001);
  32.         if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
  33.                 perror("Create socket failed");
  34.                 exit(-1);
  35.         }
  36.         if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
  37.                 perror("Bind socket failed");
  38.                 exit(-1);
  39.         }

  40.         memset(&action, 0, sizeof(action));
  41.         action.sa_handler = sigio_handler;
  42.         action.sa_flags = 0;
  43.         sigaction(SIGIO, &action, NULL);
  44.         if (fcntl(sockfd, F_SETOWN, getpid()) == -1) {
  45.                 perror("Fcntl F_SETOWN failed");
  46.                 exit(-1);               
  47.         }
  48.         if (ioctl(sockfd, FIOASYNC, &on) == -1) {
  49.                 perror("Ioctl FIOASYNC failed");
  50.                 exit(-1);               
  51.         }

  52.         sigemptyset(&oldmask);
  53.         sigemptyset(&newmask);
  54.         sigaddset(&newmask, SIGIO);
  55.         while (1) {
  56.                 int len;
  57.                 sigprocmask(SIG_BLOCK, &newmask, &oldmask);
  58.                 while (nqueue == 0)
  59.                         sigsuspend(&oldmask);
  60.                 len = recv(sockfd, recv_buf, MAX_LENTH, MSG_DONTWAIT);
  61.                 if (len == -1 && errno == EAGAIN)
  62.                         nqueue = 0;
  63.                 sigprocmask(SIG_SETMASK, &oldmask, NULL);
  64.                 if (len >= 0)
  65.                         printf("recv %d byte(s)\n", len);

  66.         }
  67. }
复制代码
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-9-23 22:13:20 | 显示全部楼层
多谢daemon兄的补充,将整个程序改成信号驱动的形式,效率提高很多啊。并且在recv的操作中禁止了SIGIO信号,更加的让程序稳定和安全。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表