linux c socket之异步IO

[cpp]  view plain copy
  1. /*  
  2.  * File:   UDPEchoClient-TimeOut.c 
  3.  * Author: 云守护 
  4.  */  
  5. #include <stdio.h>  
  6. #include <stdlib.h>  
  7. #include <string.h>  
  8. #include <unistd.h>  
  9. #include <errno.h>  
  10. #include <signal.h>  
  11. #include <sys/socket.h>  
  12. #include <netinet/in.h>  
  13. #include <netdb.h>  
  14. #include "DieWithMessage.h"  
  15. //超时设置  
  16. static const unsigned int TIMEOUT_SEC=3;  
  17. //重连次数  
  18. static const unsigned int MAXTRIES=5;  
  19. //已经尝试次数  
  20. unsigned int tries=0;  
  21. //闹铃到来处理函数,超时时间到  
  22. void CatchAlarm(int ignored);  
  23. int main(int argc, char** argv) {  
  24.     if(argc<3||argc>4)  
  25.         DieWithUserMessage("param","<Server Address/Name> <Echo Word> [<Server port/service>]\n");  
  26.     char *server=argv[1];  
  27.     char* echoString=argv[2];  
  28.     size_t echoStringLen=strlen(echoString);  
  29.     if(echoStringLen>1024*8)  
  30.         DieWithUserMessage(echoString,"too long");  
  31.     char * service=(argc==4)?argv[3]:"echo";  
  32.     //配置地址信息  
  33.     struct addrinfo addr_criteria;  
  34.     memset(&addr_criteria,0,sizeof(addr_criteria));  
  35.     addr_criteria.ai_family=AF_UNSPEC;  
  36.     addr_criteria.ai_socktype=SOCK_DGRAM;  
  37.     addr_criteria.ai_protocol=IPPROTO_UDP;  
  38.       
  39.     struct addrinfo *server_addr;  
  40.     //自动获取地址信息  
  41.     int ret=getaddrinfo(server,service,&addr_criteria,&server_addr);  
  42.     if(ret!=0)  
  43.         DieWithUserMessage("getaddrinfo() failed!",gai_strerror(ret));  
  44.     //建立socket  
  45.     int sock=socket(server_addr->ai_family,server_addr->ai_socktype,server_addr->ai_protocol);  
  46.     if(sock<0)  
  47.         DieWithSystemMessage("socket failed!");  
  48.     //信号处理者  
  49.     struct sigaction handler;  
  50.     //设置SIGALRM信号到来处理函数  
  51.     handler.sa_handler=CatchAlarm;  
  52.     //填充信号掩码  
  53.     if(sigfillset(&handler.sa_mask)<0)  
  54.         DieWithSystemMessage("sigfillset() failed!");  
  55.     //信号标志归零  
  56.     handler.sa_flags=0;  
  57.     //注册信号量  
  58.     if(sigaction(SIGALRM,&handler,0)<0)  
  59.         DieWithSystemMessage("sigaction() failed for SIGALRM");  
  60.     //开始发送数据  
  61.     ssize_t numBytes=sendto(sock,echoString,echoStringLen,0,server_addr->ai_addr,server_addr->ai_addrlen);  
  62.     if(numBytes<0)  
  63.         DieWithSystemMessage("send() failed!");  
  64.     else if(numBytes!=echoStringLen)  
  65.     {  
  66.         DieWithUserMessage("sendto() error","sent unexpected number of bytes");          
  67.     }  
  68.     //然后接收数据,都是循环接收的  
  69.     struct sockaddr_storage fromaddr;  
  70.     socklen_t fromaddrLen=sizeof(fromaddr);  
  71.     //设置定时器,超时设置  
  72.     alarm(TIMEOUT_SEC);  
  73.     char buffer[BUFSIZ+1];  
  74.     //接收数据  
  75.     while((numBytes=recvfrom(sock,buffer,BUFSIZ,0,(struct sockaddr*)&fromaddr,&fromaddrLen))<0)  
  76.     {  
  77.         if(errno=EINTR)//闹铃响了,即超时了。执行了CatchAlarm  
  78.         {  
  79.             if(tries<MAXTRIES)  
  80.             {   //小于重试次数,继续重新发送数据  
  81.                 numBytes=sendto(sock,echoString,echoStringLen,0,(struct sockaddr*)server_addr->ai_addr,server_addr->ai_addrlen);  
  82.                 if(numBytes<0)  
  83.                     DieWithSystemMessage("sendto() failed!");  
  84.                 else if(numBytes!=echoStringLen)  
  85.                 {  
  86.                     DieWithUserMessage("sendto() error","sent unexpected number of bytes");  
  87.                 }  
  88.             }else{  
  89.                 DieWithUserMessage("No Response","Unable to communitcate with server");  
  90.             }  
  91.         }else{  
  92.             DieWithSystemMessage("recvfrom() failed!");  
  93.               
  94.         }  
  95.     }  
  96.     //闹铃归零  
  97.     alarm(0);  
  98.     buffer[echoStringLen]='\0';  
  99.     printf("Received: %s \n",buffer);  
  100.     close(sock);  
  101.     return (EXIT_SUCCESS);  
  102. }  
  103. //闹铃响了,执行该函数  
  104. void CatchAlarm(int ignored){  
  105.     tries+=1;  
  106. }  

[cpp]  view plain copy
  1. /*  
  2.  * File:   UDPEchoServer-SIGIO.c 
  3.  * Author: 云守护 
  4.  */  
  5. #include <stdio.h>  
  6. #include <stdlib.h>  
  7. #include <string.h>  
  8. #include <unistd.h>  
  9. #include <fcntl.h>  
  10. #include <sys/file.h>  
  11. #include <signal.h>  
  12. #include <errno.h>  
  13. #include <sys/types.h>  
  14. #include <sys/socket.h>  
  15. #include <netdb.h>  
  16. #include "DieWithMessage.h"  
  17. //用户空闲时任务  
  18. void UseIdleTime();  
  19. //异步IO处理者  
  20. void SIGIOHandler(int signalType);  
  21. int server_sock;  
  22. #define MAXSTRINGLENGTH 1024*8  
  23.   
  24. //打印客户端地址消息  
  25. void PrintSocketAddress(const struct sockaddr *address, FILE *stream) {  
  26.   // Test for address and stream  
  27.   if (address == NULL || stream == NULL)  
  28.     return;  
  29.   
  30.   void *numericAddress; // Pointer to binary address  
  31.   // Buffer to contain result (IPv6 sufficient to hold IPv4)  
  32.   char addrBuffer[INET6_ADDRSTRLEN];  
  33.   in_port_t port; // Port to print  
  34.   // Set pointer to address based on address family  
  35.   switch (address->sa_family) {  
  36.   case AF_INET:  
  37.     numericAddress = &((struct sockaddr_in *) address)->sin_addr;  
  38.     port = ntohs(((struct sockaddr_in *) address)->sin_port);  
  39.     break;  
  40.   case AF_INET6:  
  41.     numericAddress = &((struct sockaddr_in6 *) address)->sin6_addr;  
  42.     port = ntohs(((struct sockaddr_in6 *) address)->sin6_port);  
  43.     break;  
  44.   default:  
  45.     fputs("[unknown type]", stream);    // Unhandled type  
  46.     return;  
  47.   }  
  48.   // Convert binary to printable address  
  49.   if (inet_ntop(address->sa_family, numericAddress, addrBuffer,  
  50.       sizeof(addrBuffer)) == NULL)  
  51.     fputs("[invalid address]", stream); // Unable to convert  
  52.   else {  
  53.     fprintf(stream, "%s", addrBuffer);  
  54.     if (port != 0)                // Zero not valid in any socket addr  
  55.       fprintf(stream, "-%u", port);  
  56.   }  
  57. }  
  58.   
  59. int main(int argc, char** argv) {  
  60.     if(argc!=2)  
  61.         DieWithUserMessage("param","<server port/service>");  
  62.     char *service=argv[1];  
  63.     //配置地址信息  
  64.     struct addrinfo addr_criteria;  
  65.     memset(&addr_criteria,0,sizeof(addr_criteria));  
  66.     addr_criteria.ai_family=AF_UNSPEC; //地址族  
  67.     addr_criteria.ai_flags=AI_PASSIVE;  
  68.     addr_criteria.ai_socktype=SOCK_DGRAM; //流  
  69.     addr_criteria.ai_protocol=IPPROTO_UDP;//UDP  
  70.       
  71.     struct addrinfo *server_addr;  
  72.     //自动获取,消除IP4和IP6的依赖  getaddrinfo() is reentrant and allows programs to eliminate IPv4-versus-IPv6 depen‐ dencies.  
  73.     int retVal=getaddrinfo(NULL,service,&addr_criteria,&server_addr);  
  74.     if(retVal!=0)  
  75.         DieWithUserMessage("getaddrinfo() failed!",gai_strerror(retVal));  
  76.     //建立socket  
  77.     server_sock=socket(server_addr->ai_family,server_addr->ai_socktype,server_addr->ai_protocol);  
  78.     if(server_sock<0)  
  79.         DieWithSystemMessage("socket() failed!");  
  80.     //绑定端口  
  81.     if(bind(server_sock,server_addr->ai_addr,server_addr->ai_addrlen)<0)  
  82.     {  
  83.         DieWithSystemMessage("bind() failed!");  
  84.     }  
  85.     freeaddrinfo(server_addr);  
  86.       
  87.     printf("server success %s\n",service);  
  88.     //操作信号,设置异步IO  
  89.     struct sigaction handler;  
  90.     //设置异步到来时的处理函数  
  91.     handler.sa_handler=SIGIOHandler;  
  92.     //填充信号掩码  
  93.     if(sigfillset(&handler.sa_mask)<0)  
  94.         DieWithSystemMessage("sigfillset() failed!");  
  95.     //标志设置0,即无标志  
  96.     handler.sa_flags=0;  
  97.     //注册SIGIO信号  
  98.     if(sigaction(SIGIO,&handler,0)<0)  
  99.         DieWithSystemMessage("sigaction() failed for SIGIO");  
  100.     //设置server_sock接收SIGIO信号信息  
  101.     if(fcntl(server_sock,F_SETOWN,getpid())<0)  
  102.         DieWithSystemMessage("fcntl failed!");  
  103.     //使用非阻塞IO和SIGIO信号发送  
  104.     if(fcntl(server_sock,F_SETFL,O_NONBLOCK|FASYNC)<0)  
  105.         DieWithSystemMessage("Unable to put client sock into non-blocking/async mode");  
  106.     for(;;)  
  107.     {  
  108.         //死循环,等待异步IO的到来。就不用使用多线程了。  
  109.         UseIdleTime();  
  110.     }  
  111.     return (EXIT_SUCCESS);  
  112. }  
  113. void UseIdleTime()  
  114. {  
  115.     puts(".");  
  116.     sleep(3);//3s  
  117. }  
  118. //异步IO处理函数  
  119. void SIGIOHandler(int signalType)  
  120. {  
  121.     ssize_t numBytesRcvd;  
  122.     do{  
  123.         //只要有输入  
  124.         struct sockaddr_storage client_addr;  
  125.         size_t client_Len=sizeof(client_addr);  
  126.         char buffer[MAXSTRINGLENGTH];  
  127.         //接收数据  
  128.         numBytesRcvd=recvfrom(server_sock,buffer,MAXSTRINGLENGTH,0,(struct sockaddr*)&client_addr,&client_Len);  
  129.         if(numBytesRcvd<0)  
  130.         {  
  131.             if(errno!=EWOULDBLOCK)  
  132.             {  
  133.                 //判断recvfrom函数是否阻塞  
  134.                 DieWithSystemMessage("recvfrom failed!");  
  135.             }  
  136.         }else{  
  137.                 fprintf(stdout,"handling client ");  
  138.                 //打印地址消息  
  139.                 PrintSocketAddress((struct sockaddr*)&client_addr,stdout);  
  140.                 fputc('\n',stdout);  
  141.                 //把数据发送回去  
  142.                 ssize_t numByteSent=sendto(server_sock,buffer,numBytesRcvd,0,(struct sockaddr*)&client_addr,sizeof(client_addr));  
  143.                 if(numByteSent<0)  
  144.                     DieWithSystemMessage("sendto() failed!");  
  145.                 else if(numByteSent!=numBytesRcvd)  
  146.                 {  
  147.                     DieWithUserMessage("sendto()","sent unexpected number of bytes");  
  148.                       
  149.                 }  
  150.         }  
  151.         //循环接收数据  
  152.     }while(numBytesRcvd>=0);  
  153.       
  154. }  

[cpp]  view plain copy
  1. /*  
  2.  * File:   DieWithMessage.h 
  3.  * Author: root 
  4.  * 
  5.  * Created on 2013年11月13日, 下午3:52 
  6.  */  
  7.   
  8. #ifndef DIEWITHMESSAGE_H  
  9. #define DIEWITHMESSAGE_H  
  10.   
  11. #ifdef  __cplusplus  
  12. extern "C" {  
  13. #endif  
  14. #include <stdio.h>  
  15. #include <stdlib.h>  
  16.  void  DieWithUserMessage(const char *msg,const char * detail)  
  17.  {  
  18.      fputs(msg,stderr);  
  19.      fputs(":",stderr);  
  20.      fputs(detail,stderr);  
  21.      fputs("\n",stderr);  
  22.      exit(1);  
  23.  }  
  24.  void DieWithSystemMessage(const char* msg)  
  25.  {  
  26.      perror(msg);  
  27.      exit(1);  
  28.  }  
  29.   
  30.   
  31.   
  32. #ifdef  __cplusplus  
  33. }  
  34. #endif  
  35.   
  36. #endif  /* DIEWITHMESSAGE_H */  


from: http://blog.csdn.net/earbao/article/details/16337639


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。下面详细介绍一下! Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明): int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 先说明两个结构体: 第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。 第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。 具体解释select的参数: int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。 fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。 fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。 fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。 struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。 返回值: 负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值