(五)epoll实现TCP服务端

1.epoll是由poll改进而来的。

2.epoll跟poll在编程实现上有一定程度相似。

3.epoll主要通过结构体 epoll_event 来实现监听。

(!!!!!!不想看原理的可以直接拉到后面看代码!!!!!)


//epoll_event结构体struct epoll_event  
{  
  uint32_t events;                         /* Epoll events */  
  epoll_data_t data;                     /* User data variable */  
} __attribute__ ((__packed__));  
  
typedef union epoll_data  
{  
  void *ptr;  
  int fd;  
  uint32_t u32;  
  uint64_t u64;  
} epoll_data_t;  

虽然我知道贴出来可能都没卵用,我还是直接说吧。epoll编程的几个步骤:
1.创建 epoll_event 类型的结构体2个, 如: struct epoll_event events,wait_events 前者是用于把套接字描述符加到 epoll句柄中的,后者是用于判断发生了什么事件
struct epoll_event event;             //用于往epoll句柄中加入新的需要监测的套接字
struct epoll_event wait_event;        //用于装epoll_wait()的返回信息,判断哪一个套接字发生响应


2.用 epoll_create()创造一个 epoll句柄: 
int epfd = epoll_create(10);      //这个10没什么大概意义,只要是正整数即可。


3.给结构体epoll_event 赋值并加入到 epoll句柄 epfd中。epoll_ctl()
event.data.fd = sockfd;      //sockfd为 socket()函数返回的监听套接字
event.events = EPOLLIN;     //设置期望该套接字发生的事件为可读事件
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event);  //EPOLL_CTL_ADD表示往epfd中加入新的套接字来检测


4.等待套接字响应。epoll_wait()
int ret = epoll_wait(epfd,&wait_event,num,-1);
//解释一下上面的这个函数:
第一个参数:epoll句柄(由epoll_create()返回的)
第二个参数:struct epoll_event 类型的结构体,放在该位置,当epoll_wait返回时,就会把响应的套接字信息放到这个结构体中。
第三个参数:正在监测的套接字个数。
第四个参数:-1表示一直阻塞直到有套接字发生响应,0表示不阻塞,无论有没有套接字响应 都返回,>0表示阻塞的时间,毫秒为单位。
这个函数的返回值ret 为发生响应的套接字的个数。


5.判断epfd中哪个套接字发生响应
if((wait_event.data.fd == sockfd)&&(wait_event.events & EPOLLIN ==EPOLLIN))   //若套接字sockfd发生响应,并且为可读
{......}

下面给出代码:
服务端:

#include "myhead.h"
#define MAX 50

void write_to_all(char *wbuf);    //此函数用于把一个客户端发来的消息转发给其他客户端
int client_fd[50];       //此数组用于装连接进来的客户端的套接字文件描述符
int main()
{
	int sockfd,newsockfd;
	struct sockaddr_in saddr;

	int size = sizeof(struct sockaddr_in);
	
	//创建2个epoll_event结构体
	struct epoll_event event;	//用于往epoll句柄加入新的待检测套接字
	struct epoll_event wait_event;   //用于装epoll_wait()返回的响应的套接字的消息,判断哪个套接字发生响应

	int on = 1;
	int ret;
	
	//设置TCP套接字的一些属性
	bzero(&saddr,size);
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);

	sockfd = socket(AF_INET,SOCK_STREAM,0);
	setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));	//设置端口复用,不设也可以,对功能没影响
	bind(sockfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr));
	listen(sockfd,10);

	//epoll 相关参数
	int max = 0;
	memset(client_fd,-1,sizeof(client_fd));

	int epfd;
	//创建epoll句柄epfd
	epfd = epoll_create(10);   //这是10没啥意义,只要是正数即可。

	//加入监听套接字
	event.data.fd = sockfd;
	event.events = EPOLLIN;
	epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event);

	//加入标准输入,使服务端可以发信息
	event.data.fd = 0;
	event.events = EPOLLIN;
	epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event);

	int i;
	while(1)
	{
		//等待套接字发生某种响应,否则一直阻塞
		ret = epoll_wait(epfd,&wait_event,max+2,-1);
		//判断是否有新客户端连入
		if((wait_event.data.fd ==sockfd) && 
			(wait_event.events &EPOLLIN == EPOLLIN))
		{
			int len = sizeof(struct sockaddr);
			struct sockaddr_in caddr;
			newsockfd = accept(sockfd,(struct sockaddr*)&caddr,
				&len);
			//newsockfd加入client_fd
			for(i=0;i<MAX;i++)
			{
				if(client_fd[i] != -1)
					continue;
				else
				{
					client_fd[i] = newsockfd;
					break;
				}
			}		

			//newsockfd加入epoll句柄 epfd
			event.data.fd = newsockfd;
			event.events = EPOLLIN;
			epoll_ctl(epfd,EPOLL_CTL_ADD,newsockfd,&event);
			max+=1;

			ret --;//处理完一个响应后,ret减一
			if(ret<=0)//若ret已经为0,则说明已经处理完所有的发生响应的套接字,就可以继续epoll_wait()等待套接字发生再响应
			{
				continue;
			}
		}
		if((wait_event.data.fd ==0)&&(wait_event.events & EPOLLIN==EPOLLIN))
		{
			char wbuf[50]={0};
			bzero(wbuf,50);
			scanf("%s",wbuf);
			write_to_all(wbuf);
			ret-=1;
		}
	
		//判断有没有其他客户端的数据发来
		for(i=0;i<MAX;i++)
		{
			char rbuf[50]={0};
			if((wait_event.data.fd ==client_fd[i])
				&&(wait_event.events&EPOLLIN==EPOLLIN))
			{
				int recv_num;
				bzero(rbuf,50);
				recv_num = recv(client_fd[i],rbuf,50,0);
				if(recv_num == 0)
				{
					client_fd[i] = -1;
					close(client_fd[i]);
					max -=1;
				}
				else
				{
					printf("%s\n",rbuf);
					bzero(rbuf,50);
				}
				ret-=1;
				if(ret<=0)
					break;
			}
			
		}
	}
}

void write_to_all(char *wbuf)
{
	int i;
	for(i=0;i<MAX;i++)
	{
		if(client_fd[i] !=-1)
		{
			write(client_fd[i],wbuf,50);
		}
	}
}

客户端:
客户端有很多种写法,这里就给出一种用poll写的,因为也懒得再写,就用之前用开的客户端代码:

#include"myhead.h"
char rbuf[50];
char wbuf[50];
char ipbuf[50];

int main()
{
	int sockfd;
	int ret,port;
	struct pollfd pollfd[2];
	struct sockaddr_in saddr;
	int size = sizeof(struct sockaddr_in);
	bzero(&saddr,size);

	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = inet_addr("192.168.152.128");

	sockfd = socket(AF_INET,SOCK_STREAM,0);
	ret=connect(sockfd,(struct sockaddr*)&saddr,sizeof(struct sockaddr));
	if(ret == 0)
	{
		inet_ntop(AF_INET,(void*)&saddr.sin_addr.s_addr,ipbuf,50);
		port = ntohs(saddr.sin_port);
		printf("%s,%d\n",ipbuf,port);
	}
	pollfd[0].fd = STDIN_FILENO;
	pollfd[0].events = POLLIN;

	pollfd[1].fd = sockfd;
	pollfd[1].events = POLLIN;
	while(1)
	{
		poll(pollfd,2,-1);
		if((pollfd[0].revents & POLLIN)==POLLIN)
		{
			puts("message from keyboard");
			bzero(wbuf,50);
			scanf("%s",wbuf);
			write(sockfd,wbuf,50);
		}
	
		if((pollfd[1].revents & POLLIN)==POLLIN)
		{
			bzero(rbuf,50);
			read(sockfd,rbuf,50);
			printf("%s\n",rbuf);
		}
	}

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值