实现流程:

一个简单的TCP服务器,使用了epoll进行事件驱动编程。以下是代码的主要部分的解释:

1. 创建套接字

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

2. 设置SO_REUSEADDR选项

这里创建了一个IPv4的TCP套接字。

int val = 1; 
int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&val, sizeof(int));

3. 绑定套接字到地址和端口

这确保在套接字关闭后,可以立即重新使用该地址和端口进行绑定。

struct sockaddr_in addr; 
addr.sin_family = AF_INET; 
addr.sin_addr.s_addr = htonl(INADDR_ANY); 
addr.sin_port = htons(9999); 
ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));

4. 监听套接字

服务器套接字被绑定到所有可用的IPv4地址(INADDR_ANY)和端口9999。

ret = listen(sockfd, 1024);

5. 创建epoll实例

服务器开始监听连接,最多可以有1024个连接在等待队列中。

int epid = epoll_create(1);

6. 添加服务器套接字到epoll

创建一个epoll实例,用于管理事件。

struct epoll_event ev; 
ev.events = EPOLLIN; 
ev.data.fd = sockfd; 
ret = epoll_ctl(epid, EPOLL_CTL_ADD, sockfd, &ev);

7. 主循环

将服务器套接字添加到epoll实例中,并设置事件为EPOLLIN,即当套接字上有数据可读时,epoll会通知。

while (1) { ... }

主循环中,服务器等待客户端的连接或消息,然后进行相应的处理。

接受新连接: 当`epfd`(epoll的文件描述符)上有数据可读时,表示有新的连接或消息。如果是服务器套接字`sockfd`上有数据可读,那么表示有新的客户端连接。服务器接受这个连接,并将新的客户端套接字添加到epoll中。

处理客户端消息: 如果是客户端套接字上有数据可读,服务器读取这些数据,并进行处理。首先,它检查是否是从客户端接收到的第一条消息(即客户端的名字)。如果是,它将保存这个名字。否则,它将这个消息广播给所有连接的客户端(除了发送该消息的客户端)。

8. 关闭资源

//关闭 close(epid); 
close(sockfd);

在程序结束时,关闭epoll实例和服务器套接字。

完整实现:

#include<iostream>
#include<string>
#include<sys/epoll.h>
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
#include<map>

const int MAX_CONN = 1024;

struct Clinent
{
	int sockfd;
	std::string name;
};

int main() {

	//std::cout << "hello" << std::endl;
	//信箱
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0) {
		perror("sockfd create error");
		return -1;
	}

        // 解决在close之后会有一个WAIT_TIME,导致bind失败的问题
	int val = 1;
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&val, sizeof(int));
    if (ret == -1)
	{
		printf("setsockopt");
		exit(1);
	}
	//
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;   //ipv4家族
	addr.sin_addr.s_addr = htonl(INADDR_ANY);//所有ip地址  host to net
	addr.sin_port = htons(9999);//进行主机字节序和网络字节序之间的转换

	ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)); 

	if (ret < 0) {
		perror("bind error");
		return -1;
	}

	 ret = listen(sockfd, 1024);
	if (ret < 0) {
		perror("listen error");
		return -1;
	}

	int epid = epoll_create(1);
	if (epid < 0) {
		perror("epoll create error");
		return -1;
	}
  
	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.fd = sockfd;

	ret = epoll_ctl(epid, EPOLL_CTL_ADD, sockfd, &ev);
	if (ret < 0) {
		perror("epoll_ctl error");
		return -1;
	}
	//保存客户端
	std::map<int, Clinent> clients;

	while (1) {
		struct epoll_event evs[MAX_CONN];
		int n = epoll_wait(epid, evs, MAX_CONN, -1);
		if (n < 0)
		{
			perror("epoll_wait error");
			break;
		}
		for (int i = 0; i < n; i++) {
			int fd = evs[i].data.fd;
			if (fd == sockfd) {

				struct sockaddr_in client_addr;
				socklen_t client_addr_len = sizeof(client_addr);
			    int client_sockfd = accept(sockfd, (struct sockaddr*)& client_addr, &client_addr_len );
				if (client_sockfd < 0 ) {
					perror("accept error");
					continue;
				}

				struct epoll_event ev_client;
				ev_client.events = EPOLLIN; 
				ev_client.data.fd = client_sockfd;
				ret = epoll_ctl(epid, EPOLL_CTL_ADD, client_sockfd, &ev_client);
				if (ret < 0) {
					perror("epoll_ctl error");
					return -1;
				}
				std::cout << client_addr.sin_addr.s_addr << "conneting..." << std::endl;

				//保存该客户端信息
				Clinent client;
				client.sockfd = client_sockfd;
				client.name = "";

				clients[client_sockfd] = client;

			}
			else//客户端有消息
			{
				char buffer[1024];
				//接受消息
				int n = read(fd, buffer, 1024);
				if (n < 0) {

					break;
				}
				else if (n == 0) {
					//断开
					close(fd);
					epoll_ctl(epid, EPOLL_CTL_DEL, fd, 0);

					clients.erase(fd);
				}
				else {
					std::string msg(buffer, n);
					if (clients[fd].name == "")
					{
						clients[fd].name = msg;
					}
					else {
						std::string name = clients[fd].name;

						for (auto& c : clients) {
							if (c.first != fd) {
								//发送消息
								write(c.first, ('[' + name + ']' + ": " + msg).c_str(), msg.size() + name.size()+4);
							}
						}
					}
				}
			}
		}
	}
	//关闭
	close(epid);
	close(sockfd);

}