设置非阻塞socket收发数据

 

非阻塞式I/O包括非阻塞输入操作,非阻塞输出操作,非阻塞接收外来连接,非阻塞发起外出连接。包括的函数有:read, readv, recv, recvfrom, recvmsg, write, writev, send, sendto, sendmsg, accept。

    将socket 设置为非阻塞模式有三种方法:

    (1)创建socket的时候,指定socket是异步的,在type的参数中设置SOCK_NONBLOCK标志即可。

[objc] view plain copy

  1. int socket(int domain, int type, int protocol);  
  2. int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);  

    (2)使用fcntl函数:[objc] view plain copy

  1. fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);  

    (3)使用ioctl函数:[objc] view plain copy

  1. ioctl(sockfd, FIONBIO, 1);  //1:非阻塞 0:阻塞  

下面改写linux网络编程:使用多进程实现socket同时收发数据 中的程序[objc] view plain copy

  1. /*============================================================================= 
  2. #     FileName: nonblocktcp.c 
  3. #         Desc: set the connetfd unblock 
  4. #       Author: licaibiao 
  5. #   LastChange: 2017-02-14  
  6. =============================================================================*/  
  7. #include<stdio.h>  
  8. #include<sys/types.h>  
  9. #include<sys/socket.h>  
  10. #include<unistd.h>  
  11. #include<stdlib.h>  
  12. #include<errno.h>  
  13. #include<arpa/inet.h>  
  14. #include<netinet/in.h>  
  15. #include<string.h>  
  16. #include<signal.h>  
  17. #include <fcntl.h>  
  18.   
  19. #define MAXLINE 256  
  20. #define PORT    6666  
  21.   
  22.   
  23. void process_out(int signo)  
  24. {  
  25.     exit(EXIT_SUCCESS);  
  26. }  
  27.   
  28.   
  29. void write_func(int pid, int fd)  
  30. {  
  31.     char* write = "I am server";  
  32.       
  33.     printf("write id = %d\n",pid);  
  34.   
  35.     signal(SIGUSR1,process_out);    
  36.     while(1)  
  37.     {  
  38.         sleep(1);  
  39.         send(fd,write,strlen(write)+1,0);  
  40.     }  
  41.   
  42. }  
  43.   
  44.   
  45. void read_func(int pid, int fd)  
  46. {  
  47.     char readbuff[MAXLINE];  
  48.     int n = 0;  
  49.   
  50.     printf("read id = %d \n",pid);  
  51.   
  52.     memset(&readbuff,0,sizeof(readbuff));  
  53.       
  54.     while(1)  
  55.     {  
  56.         n = recv(fd, readbuff, MAXLINE, 0);   
  57.         if(n > 0)              
  58.         {  
  59.             printf("server recv data: %s \n",readbuff);  
  60.         }  
  61.         else if(n == 0)       
  62.         {  
  63.             break;  
  64.         }  
  65.         sleep(1);  
  66.         //printf("===\n");  
  67.     };  
  68.       
  69.     printf("exit read function\n");  
  70.     kill(pid, SIGUSR1);   
  71.     exit(EXIT_SUCCESS);  
  72. }  
  73.   
  74.   
  75. int main(void)  
  76. {  
  77.     int listenfd,connetfd;  
  78.     int on = 1;  
  79.     int addrlen = 0;  
  80.     int flags;  
  81.     pid_t pid, pid_child, pid_send;  
  82.     struct sockaddr_in server_addr;  
  83.     struct sockaddr_in client_addr;  
  84.   
  85.   
  86.     if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)  
  87.     {  
  88.         printf("create socket err \n");  
  89.     }  
  90.       
  91.     addrlen = sizeof(struct sockaddr_in);  
  92.     memset(&server_addr, 0, addrlen);  
  93.     server_addr.sin_family = AF_INET;      
  94.     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);   
  95.     server_addr.sin_port = htons(PORT);   
  96.   
  97.     if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)     
  98.     {  
  99.         printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);  
  100.         exit(0);  
  101.     }  
  102.   
  103.       
  104.     if( bind(listenfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1)  
  105.     {  
  106.         printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);  
  107.         exit(0);  
  108.     }  
  109.       
  110.       
  111.     if( listen(listenfd, 10) == -1)  
  112.     {  
  113.         printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);  
  114.         exit(0);  
  115.     }  
  116.       
  117.     printf("wait client accpt \n");  
  118.     while(1)  
  119.     {  
  120.           
  121.         if( (connetfd = accept(listenfd, (struct sockaddr*)&client_addr, &addrlen)) == -1)  
  122.         {  
  123.             printf("accept socket error: %s(errno: %d)",strerror(errno),errno);  
  124.             continue;  
  125.         }  
  126.   
  127.         /* set NONBLOCK */  
  128.         flags = fcntl(connetfd, F_GETFL, 0);  
  129.         fcntl(connetfd, F_SETFL, flags | O_NONBLOCK);  
  130.   
  131.         signal(SIGCHLD, SIG_IGN);  
  132.         pid = fork();  
  133.         if(pid == -1)  
  134.         {  
  135.             printf("fork err \n");  
  136.         }  
  137.       
  138.         if(pid == 0)                      
  139.         {  
  140.             pid_child = fork();   
  141.             if(pid_child == 0)            
  142.             {  
  143.                 pid_send = getpid();      
  144.                 read_func(pid_send, connetfd);  
  145.             }  
  146.             else  
  147.             {  
  148.                 pid_send = getpid();      
  149.                 write_func(pid_send,connetfd);  
  150.             }  
  151.         }  
  152.   
  153.     }  
  154. }  

   在该程序中,当接收到客户端的一个连接后,将连接描述符设置成非阻塞的:[objc] view plain copy

  1. flags = fcntl(connetfd, F_GETFL, 0);  
  2. fcntl(connetfd, F_SETFL, flags | O_NONBLOCK);  

    这样设置之后,在读和写函数中的send和recv函数都变为了非阻塞模式。在这里我们使用的是sleep来做一个延时循环检测数据可读和数据可发送。在这里常用的套接字超时并不是sleep函数。我们可以使用下面的三种套接字超时方法。

套接字超时:

    (1)调用alarm,它在指定超时期满时产生SIGALARM信号,这与Linux信号处理类似。

[objc] view plain copy

  1. static void sig_alrm(int signo)  
  2. {  
  3.     return;         /* just interrupt the recvfrom() */  
  4. }  
  5.   
  6. void dg_cli(FILEFILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)  
  7. {  
  8.     int n;  
  9.     char    sendline[MAXLINE], recvline[MAXLINE + 1];  
  10.   
  11.     signal(SIGALRM, sig_alrm);  
  12.   
  13.     while (Fgets(sendline, MAXLINE, fp) != NULL) {  
  14.   
  15.         sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);  
  16.   
  17.         alarm(5);  
  18.         if ( (n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {  
  19.             if (errno == EINTR)  
  20.                 fprintf(stderr, "socket timeout\n");  
  21.             else  
  22.                 err_sys("recvfrom error");  
  23.         } else {  
  24.             alarm(0);  
  25.             recvline[n] = 0;    /* null terminate */  
  26.             fputs(recvline, stdout);  
  27.         }  
  28.     }  
  29. }  

    (2)在select 中阻塞等待I/O(select 有内置的时间限制),以此代替直接阻塞在read或write调用上。

[objc] view plain copy

  1. int readable_select(int fd, int sec)  
  2. {  
  3.     fd_set          rset;  
  4.     struct timeval  tv;  
  5.   
  6.     FD_ZERO(&rset);  
  7.     FD_SET(fd, &rset);  
  8.   
  9.     tv.tv_sec = sec;  
  10.     tv.tv_usec = 0;  
  11.   
  12.     return(select(fd+1, &rset, NULL, NULL, &tv));  
  13.         /* 4> 0 if descriptor is readable */  
  14. }  

    (3)使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。

[objc] view plain copy

  1. void dg_cli(FILEFILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)  
  2. {  
  3.     int             n;  
  4.     char            sendline[MAXLINE], recvline[MAXLINE + 1];  
  5.     struct timeval  tv;  
  6.   
  7.     tv.tv_sec = 5;  
  8.     tv.tv_usec = 0;  
  9.     setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));  
  10.   
  11.     while (fgets(sendline, MAXLINE, fp) != NULL) {  
  12.   
  13.         sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);  
  14.   
  15.         n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);  
  16.         if (n < 0) {  
  17.             if (errno == EWOULDBLOCK) {  
  18.                 fprintf(stderr, "socket timeout\n");  
  19.                 continue;  
  20.             } else  
  21.                 err_sys("recvfrom error");  
  22.         }  
  23.   
  24.         recvline[n] = 0;    /* null terminate */  
  25.         fputs(recvline, stdout);  
  26.     }  
  27. }  

装载:http://blog.csdn.net/li_wen01/article/details/55098317

猜你喜欢

转载自blog.csdn.net/wlf_go/article/details/81129136