linux网络编程:设置非阻塞socket收发数据
2017-02-14 15:23
591 查看
非阻塞式I/O包括非阻塞输入操作,非阻塞输出操作,非阻塞接收外来连接,非阻塞发起外出连接。包括的函数有:read, readv, recv, recvfrom, recvmsg, write, writev, send, sendto, sendmsg, accept。
将socket 设置为非阻塞模式有三总方法:
(1)创建socket的时候,指定socket是异步的,在type的参数中设置SOCK_NONBLOCK标志即可。
这样设置之后,在读和写函数中的send和recv函数都变为了非阻塞模式。在这里我们使用的是sleep来做一个延时循环检测数据可读和数据可发送。在这里常用的套接字超时并不是sleep函数。我们可以使用下面的三种套接字超时方法。
套接字超时:
(1)调用alarm,它在指定超时期满时产生SIGALARM信号,这与linux信号处理类似。
(2)在select 中阻塞等待I/O(select 有内置的时间限制),以此代替直接阻塞在read或write调用上。
(3)使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。
将socket 设置为非阻塞模式有三总方法:
(1)创建socket的时候,指定socket是异步的,在type的参数中设置SOCK_NONBLOCK标志即可。
int socket(int domain, int type, int protocol); int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);(2)使用fcntl函数:
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);(3)使用ioctl函数:
ioctl(sockfd, FIONBIO, 1); //1:非阻塞 0:阻塞下面改写linux网络编程:使用多进程实现socket同时收发数据 中的程序
/*============================================================================= # FileName: nonblocktcp.c # Desc: set the connetfd unblock # Author: licaibiao # LastChange: 2017-02-14 =============================================================================*/ #include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<stdlib.h> #include<errno.h> #include<arpa/inet.h> #include<netinet/in.h> #include<string.h> #include<signal.h> #include <fcntl.h> #define MAXLINE 256 #define PORT 6666 void process_out(int signo) { exit(EXIT_SUCCESS); } void write_func(int pid, int fd) { char* write = "I am server"; printf("write id = %d\n",pid); signal(SIGUSR1,process_out); while(1) { sleep(1); send(fd,write,strlen(write)+1,0); } } void read_func(int pid, int fd) { char readbuff[MAXLINE]; int n = 0; printf("read id = %d \n",pid); memset(&readbuff,0,sizeof(readbuff)); while(1) { n = recv(fd, readbuff, MAXLINE, 0); if(n > 0) { printf("server recv data: %s \n",readbuff); } else if(n == 0) { break; } sleep(1); //printf("===\n"); }; printf("exit read function\n"); kill(pid, SIGUSR1); exit(EXIT_SUCCESS); } int main(void) { int listenfd,connetfd; int on = 1; int addrlen = 0; int flags; pid_t pid, pid_child, pid_send; struct sockaddr_in server_addr; struct sockaddr_in client_addr; if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { printf("create socket err \n"); } addrlen = sizeof(struct sockaddr_in); memset(&server_addr, 0, addrlen); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(PORT); if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } if( bind(listenfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } if( listen(listenfd, 10) == -1) { printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } printf("wait client accpt \n"); while(1) { if( (connetfd = accept(listenfd, (struct sockaddr*)&client_addr, &addrlen)) == -1) { printf("accept socket error: %s(errno: %d)",strerror(errno),errno); continue; } /* set NONBLOCK */ flags = fcntl(connetfd, F_GETFL, 0); fcntl(connetfd, F_SETFL, flags | O_NONBLOCK); signal(SIGCHLD, SIG_IGN); pid = fork(); if(pid == -1) { printf("fork err \n"); } if(pid == 0) { pid_child = fork(); if(pid_child == 0) { pid_send = getpid(); read_func(pid_send, connetfd); } else { pid_send = getpid(); write_func(pid_send,connetfd); } } } }在该程序中,当接收到客户端的一个连接后,将连接描述符设置成非阻塞的:
flags = fcntl(connetfd, F_GETFL, 0); fcntl(connetfd, F_SETFL, flags | O_NONBLOCK);
这样设置之后,在读和写函数中的send和recv函数都变为了非阻塞模式。在这里我们使用的是sleep来做一个延时循环检测数据可读和数据可发送。在这里常用的套接字超时并不是sleep函数。我们可以使用下面的三种套接字超时方法。
套接字超时:
(1)调用alarm,它在指定超时期满时产生SIGALARM信号,这与linux信号处理类似。
static void sig_alrm(int signo) { return; /* just interrupt the recvfrom() */ } void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) { int n; char sendline[MAXLINE], recvline[MAXLINE + 1]; signal(SIGALRM, sig_alrm); while (Fgets(sendline, MAXLINE, fp) != NULL) { sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); alarm(5); if ( (n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) { if (errno == EINTR) fprintf(stderr, "socket timeout\n"); else err_sys("recvfrom error"); } else { alarm(0); recvline = 0; /* null terminate */ fputs(recvline, stdout); } } }
(2)在select 中阻塞等待I/O(select 有内置的时间限制),以此代替直接阻塞在read或write调用上。
int readable_select(int fd, int sec) { fd_set rset; struct timeval tv; FD_ZERO(&rset); FD_SET(fd, &rset); tv.tv_sec = sec; tv.tv_usec = 0; return(select(fd+1, &rset, NULL, NULL, &tv)); /* 4> 0 if descriptor is readable */ }
(3)使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。
void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) { int n; char sendline[MAXLINE], recvline[MAXLINE + 1]; struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); while (fgets(sendline, MAXLINE, fp) != NULL) { sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); if (n < 0) { if (errno == EWOULDBLOCK) { fprintf(stderr, "socket timeout\n"); continue; } else err_sys("recvfrom error"); } recvline = 0; /* null terminate */ fputs(recvline, stdout); } }