epoll函数实现多路IO转接

一、实现思路

1、服务器

1)socket()
2)set_sockopt() 端口复用。
3)bind() 绑定服务器。
4)listen() 设置监听上限。
5)epoll_create() 创建epoll模型,efd指向红黑树跟结点。
7)epoll_ctl() //将lfd及对应的结构体挂到树上,efd可以找到该树。
8)循环监听:epoll_wait() //epoll为server阻塞监听事件,ep为struct epoll_event数组输出,OPEN_MAX为数组容量,-1 表示阻塞。
9)进入第二层循环,循环次数为epoll_wait的返回值。
10)入股哦不是“读”事件,继续循环。
11)判断满足事件的fd时不时lfd。
12)是。接收连接请求——》挂到树上。
13)不是。读出sockfd——》read:为0,从树上删除结点;为负,出错处理;其它,执行功能。
14)末尾。关闭lfd。关闭efd树。

2、客户端

1)socket() 创建socket。
2)connect() 与服务器建立连接。
3)write() 写数据到socket的写缓冲,将数据发送到服务器。
6)close() 关闭fd。

二、详细代码

1、服务器

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <ctype.h>

#include "warp.h"

#define OPEN_MAX 5000
#define SERV_PORT 666
#define MAXLINE	 8192

int main (int argc, char *argv[])
{
	int i, listenfd, connfd, sockfd;
	int n, num = 0;
	ssize_t nready, efd, res;
	char buf[MAXLINE], str[INET_ADDRSTRLEN];  //INET_ADDRSTRLEN这个宏定义的数字是IP地址字符串的大小
	socklen_t clilen;

	struct sockaddr_in cliaddr, servaddr;
	struct epoll_event tep, ep[OPEN_MAX];	//tep是临时变量;ep为epoll_wait的输出数组,保存了被触发事件的结点。

	listenfd = Socket (AF_INET, SOCK_STREAM, 0);	//创建一个用于监听客户端的套接字

	int opt = 1;
	setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));	//端口复用

	bzero (&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port   = htons (SERV_PORT);
	servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
	Bind (listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));	//绑定服务器

	Listen (listenfd, 20);		//设置监听上限

	efd = epoll_create (OPEN_MAX);	//创建epoll的红黑树
	if (efd == -1)
		PERROR("epoll_create errno");

	tep.events = EPOLLIN; tep.data.fd = listenfd;		//监听listenfd的读事件,即客户端的请求
	res = epoll_ctl (efd, EPOLL_CTL_ADD, listenfd, &tep);	//挂上树
	if (res == -1)
		PERROR ("epoll_ctl error");

	for (; ;)		
	{
		nready = epoll_wait (efd, ep, OPEN_MAX, -1);	循环监听
		if (nready == -1)
			PERROR ("epoll_wait error");

		for (i = 0; i < nready; i++)
		{
			if (!ep[i].events & EPOLLIN)		//只监听读事件
				continue;

			if (ep[i].data.fd == listenfd)		//有客户端发出连接请求
			{
				clilen = sizeof (cliaddr);
				connfd = Accept (listenfd, (struct sockaddr*)&cliaddr,&clilen);

				printf ("receive from %s at PORT %d\n",
						inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
						ntohs(cliaddr.sin_port));
				printf ("cfd %d---client %d\n", connfd, ++num);

				tep.events = EPOLLIN; tep.data.fd =connfd;
				res = epoll_ctl (efd, EPOLL_CTL_ADD, connfd, &tep);
				if (res == -1)
					PERROR ("epoll_ctl error");
			}
			else	//已连接的结点发出的事件
			{
				sockfd = ep[i].data.fd;
				n = Read (sockfd, buf, MAXLINE);

				if (n == 0)
				{
					res = epoll_ctl (efd, EPOLL_CTL_DEL, sockfd, NULL);
					if (res == -1)
						PERROR ("epoll_ctl error");
					Close (sockfd);
					printf ("client[%d] closed connect\n", sockfd);
				}
				else if (n < 0)
				{
					perror ("read n < 0 error: ");
					res = epoll_ctl (efd, EPOLL_CTL_DEL, sockfd, NULL);
					Close (sockfd);
				}
				else
				{
					for (i=0; i<n ;i++)
						buf[i] = toupper (buf[i]);

					Write (STDOUT_FILENO, buf, n);
					Writen (sockfd, buf, n);
				}
			}
				
		}
	}

	Close (listenfd);
	Close (efd);
	return 0;
}

2、客户端

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <errno.h>
#include "warp.h"


#define SEVR_PORT  666

int main (void)
{
	int cfd;
	struct sockaddr_in serv_addr;
	int count = 10;
	char buf[1024] = {0};

	cfd = Socket (AF_INET, SOCK_STREAM, 0);

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons (SEVR_PORT);
	inet_pton (AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
	Connect (cfd, (struct sockaddr *)&serv_addr, sizeof (serv_addr));

	while (count--)
	{
		int ret = 0 ;
		write (cfd, "I LOVE U\n", 9);
		ret = read (cfd, buf, strlen (buf));
		//write (STDOUT_FILENO, buf, ret);
		printf ("%s", buf);
		sleep (1);
	}

	close (cfd);
	return 0;
}

三、测试

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值