【Linux】多路IO转接服务器--epoll实现

epoll_create函数

int epoll_create(int size);     //创建一棵监听红黑树

size:创建的红黑树的监听节点数量。(仅供内核参考。)
返回值:指向新创建的红黑树的根节点的 fd。
失败: -1 errno

epoll_ctl函数

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);     //操作监听红黑树

epfd:epoll_create 函数的返回值。 epfd
op:对该监听红黑数所做的操作。
        EPOLL_CTL_ADD 添加 fd 到 监听红黑树
        EPOLL_CTL_MOD 修改 fd 在 监听红黑树上的监听事件。
        EPOLL_CTL_DEL 将一个 fd 从监听红黑树上摘下(取消监听)
fd:       

         待监听的 fd
event: 本质 struct epoll_event 结构体 地址
        成员 events:                EPOLLIN / EPOLLOUT / EPOLLERR
        成员 data: 联合体(共用体):
                int fd; 对应监听事件的 fd
                void *ptr;
                uint32_t u32;
                uint64_t u64;
返回值:成功 0; 失败: -1 errno

epoll_wait函数

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);     //阻塞监听。

epfd:epoll_create 函数的返回值。 epfd
events:传出参数,【数组】, 满足监听条件的 那些 fd 结构体。
maxevents:数组 元素的总个数。 1024
        struct epoll_event evnets[1024]
timeout:
        -1: 阻塞
        0: 不阻塞
        >0: 超时时间 (毫秒)
返回值:
        > 0: 满足监听的 总个数。 可以用作循环上限。
        0: 没有 fd 满足监听事件
        -1:失败。 errno

epoll_server.c

/*************************************************************************
    > File Name: epoll_server.c
    > Author: zsy
    > Created Time: 2023年07月25日 星期二 15时09分43秒
 ************************************************************************/

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


#define SERV_PORT 9999
#define MAXEVENTS 1024



int main(int argc, char* argv[])
{
	int lfd, cfd, epfd;
	int ret, nready;
	int i, j;
	int read_size;
	char buf[1024];


	// 服务器的地址结构
	struct sockaddr_in serv_addr;
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(SERV_PORT);

	lfd = socket(AF_INET, SOCK_STREAM, 0);

	bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

	listen(lfd, 5);


	// 客户端的地址结构
	struct sockaddr_in clnt_addr;
	socklen_t clnt_addr_size;
	char clnt_ip[1024];
	unsigned short int clnt_port;


	// cfd = accept(lfd, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
	// printf("client ip is: %s, port is: %d \n", 
	// 	inet_ntop(AF_INET, &clnt_addr.sin_addr.s_addr, clnt_ip, sizeof(clnt_ip)), 
	// 	ntohs(clnt_addr.sin_port));



	struct epoll_event tep, ep[MAXEVENTS];

	// 创建一个红黑树
	epfd = epoll_create(100);

	// 给红黑树添加一个lfd结点
	tep.events = EPOLLIN;	
	tep.data.fd = lfd;

	epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tep);
	

	

	while(1){

		// lfd 遇到了读事件,即需要产生一个与客户端通讯的套接字
		// timeout = -1, 阻塞等待lfd读事件. 
		nready = epoll_wait(epfd, ep, MAXEVENTS, -1);
		if(nready == -1){
			perror("epoll_wait error \n");
			continue;
		}

		for(i = 0; i < nready; i++){

			// 当lfd满足条件,说明有客户端请求通信。
			if(ep[i].data.fd == lfd){

				clnt_addr_size = sizeof(clnt_addr);
				cfd = accept(lfd, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
				printf("client ip is: %s, port is: %d \n", 
					inet_ntop(AF_INET, &clnt_addr.sin_addr.s_addr, clnt_ip, sizeof(clnt_ip)), 
					ntohs(clnt_addr.sin_port));	
				

				// 将新建立的cfd加入红黑树
				tep.events = EPOLLIN;
				tep.data.fd = cfd;

				epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tep);
			}

			// 除了lfd,就是cfd, 说明有客户端在传输数据。
			// 在ep数组里的都是有通信请求的
			else{
				read_size = read(ep[i].data.fd, buf, sizeof(buf));
				if(read_size == 0){
					// 该客户端已经断开连接
					// 先摘结点,再断开。
					printf("client closed \n");
					epoll_ctl(epfd, EPOLL_CTL_DEL, ep[i].data.fd, NULL);
					close(ep[i].data.fd);
				}
				else if(read_size < 0){
					printf("read error \n");
					// 先摘结点,再断开。
					epoll_ctl(epfd, EPOLL_CTL_DEL, ep[i].data.fd, NULL);
					close(ep[i].data.fd);					
				}
				else{
					write(STDOUT_FILENO, buf, read_size);

					for(j = 0; j < read_size; j++){
						buf[j] = toupper(buf[j]);
					}
					write(ep[i].data.fd, buf, read_size);
				}
			}
		}
	}


	close(lfd);
	printf("OVER!\n");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值