Linux C:epoll实现多路IO转接,并发服务器

epoll相比于之前学的多进程/多线程的并发服务器,以及select和poll来说效率都高得多,并且连接数不受限制,采用类似树的结构。
特此分享一下本人在Linux下写的epoll简单的并发服务器端的代码。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<sys/epoll.h>
#define maxepoll 1000
//===============================================================
// 实现功能:	主函数,建立一个TCP并发服务器
//===============================================================
int main(int argc, char *argv[])
{
	int sockfd = 0;	// 套接字
	int connfd = 0;
	int err_log = 0;
	struct sockaddr_in my_addr;	// 服务器地址结构体
	unsigned short port = 8888; // 监听端口
	pthread_t thread_id;

	printf("TCP Server Started at port %d!\n", port);
	sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
	if (sockfd < 0)
	{
		perror("socket error");
		exit(-1);
	}
	bzero(&my_addr, sizeof(my_addr));	// 初始化服务器地址
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(port);
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	printf("Binding server to port %d\n", port);
	//设置端口复用
	int flag = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag));
	// 绑定
	err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
	if (err_log != 0)
	{
		perror("bind error");
		close(sockfd);
		exit(-1);
	}
	// 监听,套接字变被动
	err_log = listen(sockfd, 4);
	if (err_log != 0)
	{
		perror("listen error");
		close(sockfd);
		exit(-1);
	}
	//建立根结点
	int epfd = epoll_create(maxepoll);
	//初始化树
	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.fd = sockfd;
	epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd,&ev);
	//创建下面获取改变的客户端存储
	struct epoll_event all[maxepoll];

	//客户端结构体
	struct sockaddr_in client_addr;
	socklen_t client_len = sizeof(client_addr);

	printf("Waiting client...\n");
	while (1)
	{

		int num = epoll_wait(epfd, all, maxepoll, -1);
		for (int i = 0; i < num; ++i)
		{
			int fd = all[i].data.fd;
			if (fd == sockfd)//判断是否有新链接请求
			{
				int cfd = accept(fd, (struct sockaddr*)&client_addr, &client_len);
				if (cfd == -1)
				{
					perror("accept error");
					exit(1);
				}
				//把新的客户端结点挂到树上
				ev.events = EPOLLIN;
				ev.data.fd = cfd;
				epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
				//打印连接的客户端信息
				char cli_ip[INET_ADDRSTRLEN] = "";	
				inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
				printf("-----client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));
			}
			else//如果客户端发来消息改变了epoll表
			{
				if (!all[i].events&EPOLLIN)//如果不是读操作直接略过当前的操作
				{
					continue;
				}
				char buf[1024] = { 0 };//清空缓冲区
				int len = recv(fd, &buf, sizeof(buf), 0);
				if (len == -1)
				{
					perror("recv error");
					break;
				}
				else if(len==0)
				{
					printf("客户端fd:%d关闭,即将删除该结点\n",fd);
					close(fd);//别忘了关闭文件描述符
					epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);//删除结点
				}
				else
				{
					printf("来自 fd:%d,内容:%s\n", fd, buf);
					send(fd, buf, len, 0);//把收到的内容重新发给客户端
				}
			}
		}

	}
	close(sockfd);
	return 0;
}

Guess you like

Origin blog.csdn.net/qq_40861091/article/details/88772184