linux下I/O复用与epoll实际使用(二)

上一节《linux下I/O复用与epoll实际使用(一)》主要讲解了epoll的原理,这一节结合socket的编程,详解select与epoll编程示例。
一、socket编程
在TCP/IP协议中“IP地址+TCP或者UDP端口号”唯一标识网络通讯中的一个进程,"IP+端口号"就称为socket。在TCP协议中,建立连接的两个进程各自有一个socket来标识,name两个socket组成的socjet pair就唯一标识一个连接。
预备知识:
网络字节序:内存中多字节数据相对于内存地址有大端小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。网络数据流同样有大端小端之分,所以发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,接收主机把从网络上接收到的字节按内存从低到高的顺序保存,因此网络数据流的地址应该规定:**先发出的数据是低地址,后发出的数据是高地址。**TCP/IP协议规定网络数据流应该采用大端字节序,即低地址高字节。所以发送主机和接收主机是小段字节序的在发送和接收之前需要做字节序的转换。

为了使网络程序具有可移植性可以调用以下函数进行网络字节数的转换。

 #include <arpa/inet.h>
 uint32_t htonl(uint32_t hostlong);
 uint16_t htons(uint16_t hostshort);
 uint32_t ntohl(uint32_t netlong);
 uint16_t ntohs(uint16_t netshort);

socket地址数据类型及相关函数
sockaddr数据结构
在这里插入图片描述

tcpsocket 实现
实现模型:
1.服务器端 socket -> bind -> listen -> accept(阻塞,三次握手)-> send。
2.客户端 socket -> connect(阻塞,三次握手)-> rcv。
上一节我们已经比较过select与epoll的优缺点。
现在直接上select和epoll的示例代码:
代码在(一)的基础上修改的
server端

#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<string.h>
#include<errno.h>
#include<signal.h>
#include<fcntl.h>
#include<stdbool.h>
#include<sys/epoll.h>
#define _SELECT
int gSetNonblocking(int fd)
{
	int old_option=fcntl(fd,F_GETFL);
	int new_option=old_option|O_NONBLOCK;
	fcntl(fd,F_SETFL,new_option);
	return new_option;
}

/*往 epoll 描述符添加套接字*/
void gAddfd(int epollfd,int fd,bool oneshoot)
{
	struct epoll_event event;
	event.data.fd=fd;
	event.events=EPOLLIN;
	int ret;
	if(oneshoot)
	{
		event.events=event.events|EPOLLONESHOT;
	}
	ret=epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
	gSetNonblocking(fd);
}
void gDelfd(int epollfd,int fd,bool oneshoot)
{
	struct epoll_event event;
	event.data.fd=fd;
	event.events=EPOLLIN;
	int ret;
	if(oneshoot)
	{
		event.events=event.events|EPOLLONESHOT;
	}
	ret=epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
	if(ret!=0)
	{
		if(errno==EEXIST)
		{
			epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&event);
		}
		
	}
}
void gCtrlfd(int epollfd,int fd,bool oneshoot)
{
	struct epoll_event event;
	event.data.fd=fd;
	event.events=EPOLLIN;
	int ret;
	if(oneshoot)
	{
		event.events=event.events|EPOLLONESHOT;
	}
	ret=epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
	if(ret!=0)
	{
		if(errno==EEXIST)
		{
			epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event);
		}
	}
	
}
int main(int argc,char *argv[])
{
	int sock=socket(AF_INET,SOCK_STREAM,0);
	if(sock<0)
		write(STDERR_FILENO,"socket error",14);
	
	struct sockaddr_in addr;
	memset(&addr,0,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_port=htons(atoi(argv[1]));
	addr.sin_addr.s_addr=INADDR_ANY;
	
	bind(sock,(struct sockaddr *)&addr,sizeof(addr));
	listen(sock,32767);
	signal(SIGPIPE,SIG_IGN);
//	signal(SIGINT,sig);
#ifdef _SELECT
	int maxfd=0,i=0;
	int array[4096];
	int ret;
	fd_set rfds;
	fd_set wfds;
	array[0]=sock;
	int array_size=sizeof(array)/sizeof(array[0]);
	for(i=1;i<array_size;i++)
	{
		array[i]=-1;
	}
#else
	int epollfd;
	epollfd=epoll_create(20);
	gAddfd(epollfd,sock,false);

	int connfd[20]={-1};
	int number;
	int i,j;
	int sockfd;
	int count=0;
	struct epoll_event event[512];
#endif

	while(1)
	{
#ifdef _SELECT
		FD_ZERO(&rfds);
		FD_ZERO(&wfds);
		
		for(i=0;i<array_size;i++)
		{
			if(array[i]>0)
			{
				FD_SET(array[i],&rfds);
				FD_SET(array[i],&wfds);
				if(array[i]>maxfd)
				{
					maxfd=array[i];
				}
			}
		}
		ret=select(maxfd+1,&rfds,&wfds,NULL,NULL);
		if(ret>0)
		{
			int j;
			for(j=0;j<array_size;j++)
			{
				if(j==0 && FD_ISSET(array[j],&rfds))
				{
					struct sockaddr_in client;
					socklen_t len=sizeof(client);
					int new_sock=accept(sock,(struct sockaddr *)&client,&len);
					if(new_sock<0)
					{
						perror("accept");
						continue;
					}
					else
					{
						printf("get a new client %s\n",inet_ntoa(client.sin_addr));
						fflush(stdout);
						int k=1;
						for(;k<array_size;++k)
						{
							if(array[k]<0)
							{
								array[k]=new_sock;
								if(new_sock > maxfd)
									maxfd=new_sock;
								break;
							}
						}
						if(k==array_size)
						{
							close(new_sock);
						}
					}
				}else if(j!=0 && FD_ISSET(array[j],&rfds))
				{
						char buf[1024];
						ssize_t s=read(array[j],buf,sizeof(buf)-1);
						if(s>0)
						{
							buf[s]='\0';
							printf("clientsay#%s",buf);
							if(FD_ISSET(array[j],&wfds))
							{
								char *msg="HTTP/1.0 200 OK <\r\n\r\n<html><h1>welecom to select world!!!</h1></html>\r\n";
								write(array[j],msg,strlen(msg));
							}
						}
						else if(0==s)
						{
							printf("client quit\n");
							close(array[j]);
							array[j]=-1;
						}
						else
						{
							perror("read");
							close(array[j]);
							array[j]=-1;
						}
				}
			}
		}
		
#else
			

		number=epoll_wait(epollfd,event,512,-1);
		if(number < 0 && errno !=EINTR)
		{
			printf("epoll failure\n");
			break;
		}
		
		for(i=0;i<number;i++)
		{
			sockfd=event[i].data.fd;
			printf("sockfd=%d,sock=%d,connfd=%d",sockfd,sock,connfd[count]);
			if(sockfd==sock && (event[i].events & EPOLLIN))
			{
				struct sockaddr_in cliaddr;
				socklen_t clilen=sizeof(struct sockaddr_in);
				if(connfd[count]==-1)
				{
					connfd[count]=accept(sock,(struct sockaddr *)&cliaddr,&clilen);
				}else
				{
					for(j=0;j<20;j++)
					{
						if(connfd[j]==-1)
						{
							count=j;
						}
					}
					connfd[count]=accept(sock,(struct sockaddr *)&cliaddr,&clilen);
				}
				if(connfd[count]<0)
				{
					printf("errno is -> %d:%s\n",errno,strerror(errno));
					continue;
				}
				/*设置连接套接字EPOLLONESHOT*/
				gAddfd(epollfd,connfd[count],false);
				count++;
				if(count>19)
				{
					for(j=0;j<count;j++)
					{
						if(connfd[j]==-1)
						{
							count=j;
						}
					}
					/*reserve*/
				}
				
				printf("client connect\n");
			}else
			{
				for(j=0;j<=count;j++)
				{
					if(sockfd==connfd[j] && (event[i].events & EPOLLIN))
					{
						printf("Start sleep(10) ...\n");
						sleep(10);
						char text[512];
						int ret=recv(connfd[j],text,512,0);
						
						while(recv>0)
						{
							if(ret>0)
							{
								text[ret]='\0';
								printf("Recv(%d):%s\n",ret,text);
								
							}
							else if(ret==0)
							{
								printf("Client close socket\n");
								gDelfd(epollfd,connfd[j],false);
								close(connfd[j]);
								connfd[j]=-1;
								break;
							}
							else if(errno == EWOULDBLOCK)
							{
								printf("wouldblock\n");
								break;
							}
							else if(errno == EPIPE)
							{
								
								printf("Broken pipe\n");
								break;
							}
							
							ret=recv(connfd[j],text,512,0);
						}
					}
				}
			}
		}
#endif
	}
	
}

client端

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/un.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/epoll.h>
#include <fcntl.h>
 
#define path "tempfile.socket"

int sock;
void sig(int sig)
{
	char buf[512];
	memset(buf, 0, sizeof(buf));
	sprintf(buf, "close client %d",sock);
	
	write(sock, buf, strlen(buf));
//	sleep(3);
//	exit(-1);
	close(sock);		
}
 
int main(int argc, char *argv[])
{
	if (argc != 2)
	{
		printf("Usage:./exec [port]\n");
		exit(-1);
	}
	signal(SIGINT, sig);
	sock = socket(AF_INET, SOCK_STREAM, 0);
 
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[1]));
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
 
	connect(sock, (struct sockaddr *)&addr, sizeof(addr));
 
	int old = fcntl(sock, F_GETFL);
	int newoption = old | O_NONBLOCK;
	fcntl(sock, F_SETFL, newoption);
 
	char buf[512];
	memset(buf, 0, sizeof(buf));
	int epollfd = epoll_create(5);
 
	struct epoll_event event[128];
 
	struct epoll_event e1,e2;
	e1.data.fd = sock;
	e1.events = EPOLLIN | EPOLLET;
	epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &e1); 
	e2.data.fd = STDIN_FILENO;
	e2.events = EPOLLIN | EPOLLET;
	epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &e2); 
 
	int nR;
	int ret;
	int i;
	while (1)
	{
		int number = epoll_wait(epollfd, event, 128, -1);
		if (number <= 0)
		{
			printf("epoll_wait error\n");
			return 0;
		}
		for(i = 0; i < number; ++i)
		{
 
			if (event[i].data.fd == sock && (event[i].events & EPOLLIN))
			{
				memset(buf, 0, sizeof(buf));
				nR = read(sock, buf, 512);
				if (nR == 0)
				{
					close(sock);
					break;
				}
				write(STDOUT_FILENO, buf, nR);
			}
			else if (event[i].data.fd == STDIN_FILENO && (event[i].events & EPOLLIN))
			{
				printf("please input string:");
				fflush(stdout);
				memset(buf, 0, sizeof(buf));
				nR = read(STDIN_FILENO, buf, 512);
				if (nR <= 0)
				{
					printf("errno[%d]:%s\n", errno, strerror(errno));
				}
				ret = write(sock, buf, nR);
				if (ret == 0 && errno == EINTR)
				{
					printf("write sock error\n");
					exit(-1);
				}
				else if (ret < 0)
				{
					printf("write sock ret < 0\n");
					exit(-1);
				}
				printf("Send [%d]byte\n", ret);
			}
		}
	}
 
	close(sock);
 
	return 0;
}

执行结果:
server
在这里插入图片描述

client
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值