linux网络编程:设置非阻塞socket收发数据

原创 2018年04月17日 15:01:31
非阻塞式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, 0NULLNULL)) < 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, NULLNULL, &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, 0NULLNULL);  
  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


linux socket的阻塞和非阻塞设置方法

非阻塞IO 和阻塞IO:       在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:       基本概念:              ...
  • u010871058
  • u010871058
  • 2017-07-26 15:00:22
  • 1611

Linux网络编程:使用select函数实现socket 收发数据

所谓的回射是指:客户端A向服务端B发送数据,服务端B接收到数据之后,再将接收到的数据发送回客户端B。所谓的迭代服务器,是指服务器端只用一个进程处理或线程处理所有客户端的请求。与之对应的是并发服务器,并...
  • li_wen01
  • li_wen01
  • 2017-02-13 10:37:58
  • 3176

linux网络编程:使用多进程实现socket同时收发数据

前面已讲过使用一个进程实现服务端和客户端P2P通信的实例,但是它只能同时处理一个客户端的连接。如果要实现并发处理多个客户端的连接并且实现P2P通信,可以使用多进程来处理。相比与多线程来说,多进程耗费的...
  • li_wen01
  • li_wen01
  • 2016-09-27 23:58:27
  • 2269

如何设置linux socket为非阻塞

将一个socket 设置成阻塞模式和非阻塞模式,使用fcntl方法,即: 设置成非阻塞模式: 先用fcntl的F_GETFL获取flags,用F_SETFL设置flags|O_NONBLOCK; ...
  • haoyu_linux
  • haoyu_linux
  • 2015-03-16 15:27:39
  • 4038

Linux,socket,非阻塞,fcntl

http://www.2cto.com/os/201108/100481.html Linux,socket,非阻塞,fcntl 2011-08-17      0 个评论    ...
  • wangyin159
  • wangyin159
  • 2015-07-30 20:49:59
  • 625

linux 网络编程:使用两线程实现socket同时收发数据

工作中最近有使用到socket 向客户端同时发送和接收数据,因为是嵌入式linux设备,且要求只能同时一个客户端连接该端口。考虑到节省系统资源,只创建了两个线程分别实现服务端的收发数据。下面直接上代码...
  • li_wen01
  • li_wen01
  • 2016-09-26 00:51:21
  • 4628

C++非阻塞模式Socket编程

iocServer.cpp: #include #include using namespace std; #include #define BUF_SIZE 64 #pragma com...
  • u012388338
  • u012388338
  • 2014-04-16 10:28:12
  • 3827

C++网络编程例子说明异步非阻塞Socket的基本原理和工作机制

  • 2010年07月27日 23:15
  • 28KB
  • 下载

win32网络编程入门

// socket.cpp : 定义控制台应用程序的入口点。 // //服务器端 //SOCKET连接过程   //根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步...
  • wang03989
  • wang03989
  • 2014-12-31 16:10:31
  • 344

linux下非阻塞的tcp认识与理解

tcp协议本身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的.不管是否阻塞,send发送的大小,并不代表对端recv到多少的数据. 在阻塞模式下, send函数的过程是将应用程序请求发...
  • rethyx
  • rethyx
  • 2013-12-26 11:09:01
  • 982
收藏助手
不良信息举报
您举报文章:linux网络编程:设置非阻塞socket收发数据
举报原因:
原因补充:

(最多只允许输入30个字)