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

编程入门 行业动态 更新时间:2024-10-19 10:25:58

linux网络编程:设置非阻塞socket<a href=https://www.elefans.com/category/jswz/34/1769068.html style=收发数据"/>

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

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

    将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	6666void 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");elseerr_sys("recvfrom error");} else {alarm(0);recvline[n] = 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;} elseerr_sys("recvfrom error");}recvline[n] = 0;	/* null terminate */fputs(recvline, stdout);}
}

更多推荐

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

本文发布于:2024-03-09 10:08:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1724684.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:收发   网络编程   数据   linux   socket

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!