一个并发10k的简易群聊天室(使用epoll)

local.h

点击(此处)折叠或打开

  1. #include <sys/socket.h>
  2. #include <sys/epoll.h>
  3. #include <netinet/in.h>
  4. #include <arpa/inet.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <errno.h>
  10. #include <time.h>
  11. #include <list>
  12. #include <string.h>

  13. #define BUF_SIZE 1024 //默认缓冲区
  14. #define SERVER_PORT 8888 //监听端口
  15. //#define SERVER_HOST "192.168.34.15" //服务器IP地址
  16. #define SERVER_HOST "127.0.0.1" //服务器IP地址
  17. #define EPOLL_RUN_TIMEOUT -//epoll的超时时间
  18. #define EPOLL_SIZE 10000 //epoll监听的客户端的最大数目

  19. #define STR_WELCOME "Welcome to seChat! You ID is: Client #%d"
  20. #define STR_MESSAGE "Client #%d>> %s"
  21. #define STR_NOONE_CONNECTED "Noone connected to server except you!"
  22. #define CMD_EXIT "EXIT"

  23. //两个有用的宏定义:检查和赋值并且检测
  24. #define CHK(eval) if(eval < 0){perror("eval"); exit(-1);}
  25. #define CHK2(res, eval) if((res = eval) < 0){perror("eval"); exit(-1);}

  26. //================================================================================================
  27. //函数名: setnonblocking
  28. //函数描述: 设置socket为不阻塞
  29. //输入: [in] sockfd socket标示符
  30. //输出: 无
  31. //返回: 0
  32. //================================================================================================
  33. int setnonblocking(int sockfd);

  34. //================================================================================================
  35. //函数名: handle_message
  36. //函数描述: 处理每个客户端socket
  37. //输入: [in] new_fd socket标示符
  38. //输出: 无
  39. //返回: 返回从客户端接受的数据的长度
  40. //================================================================================================
  41. int handle_message(int new_fd);

utils.h

点击(此处)折叠或打开

  1. #include "local.h"


  2. int setnonblocking(int sockfd)
  3. {
  4.     CHK(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK));
  5.     return 0;
  6. }

server.cpp

点击(此处)折叠或打开

  1. #include "local.h"
  2. #include "utils.h"

  3. using namespace std;

  4. // 存放客户端socket描述符的list
  5. list<int> clients_list;

  6. int main(int argc, char *argv[])
  7. {
  8.     int listener; //监听socket
  9.     struct sockaddr_in addr, their_addr; 
  10.     addr.sin_family = PF_INET;
  11.     addr.sin_port = htons(SERVER_PORT);
  12.     addr.sin_addr.s_addr = inet_addr(SERVER_HOST);
  13.     socklen_t socklen;
  14.     socklen = sizeof(struct sockaddr_in);

  15.     static struct epoll_event ev, events[EPOLL_SIZE];
  16.     ev.events = EPOLLIN | EPOLLET; //对读感兴趣,边沿触发

  17.     char message[BUF_SIZE];

  18.     int epfd; //epoll描述符
  19.     clock_t tStart; //计算程序运行时间

  20.     int client, res, epoll_events_count;

  21.     CHK2(listener, socket(PF_INET, SOCK_STREAM, 0)); //初始化监听socket
  22.     setnonblocking(listener); //设置监听socket为不阻塞
  23.     CHK(bind(listener, (struct sockaddr *)&addr, sizeof(addr))); //绑定监听socket
  24.     CHK(listen(listener, 1)); //设置监听

  25.     CHK2(epfd,epoll_create(EPOLL_SIZE)); //创建一个epoll描述符,并将监听socket加入epoll
  26.     ev.data.fd = listener;
  27.     CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev));

  28.     while(1)
  29.     {
  30.         CHK2(epoll_events_count,epoll_wait(epfd, events, EPOLL_SIZE, EPOLL_RUN_TIMEOUT));
  31.         tStart = clock();
  32.         for(int i = 0; i < epoll_events_count ; i++)
  33.         {
  34.             if(events[i].data.fd == listener) //新的连接到来,将连接添加到epoll中,并发送欢迎消息
  35.             {
  36.                 CHK2(client,accept(listener, (struct sockaddr *) &their_addr, &socklen));
  37.                 setnonblocking(client);
  38.                 ev.data.fd = client;
  39.                 CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev));

  40.                 clients_list.push_back(client); // 添加新的客户端到list
  41.                 bzero(message, BUF_SIZE);
  42.                 res = sprintf(message, STR_WELCOME, client);
  43.                 CHK2(res, send(client, message, BUF_SIZE, 0));

  44.             }else 
  45.             {
  46.                 CHK2(res,handle_message(events[i].data.fd)); //注意:这里并没有调用epoll_ctl重新设置socket的事件类型,但还是可以继续收到客户端发送过来的信息
  47.             }
  48.         }
  49.         printf("Statistics: %d events handled at: %.2f second(s)\n", epoll_events_count, (double)(clock() - tStart)/CLOCKS_PER_SEC);
  50.     }

  51.     close(listener);
  52.     close(epfd);

  53.     return 0;
  54. }

  55. int handle_message(int client) 
  56. {
  57.     char buf[BUF_SIZE], message[BUF_SIZE];
  58.     bzero(buf, BUF_SIZE);
  59.     bzero(message, BUF_SIZE);

  60.     int len;

  61.     CHK2(len,recv(client, buf, BUF_SIZE, 0)); //接受客户端信息

  62.     if(len == 0) //客户端关闭或出错,关闭socket,并从list移除socket
  63.     {
  64.         CHK(close(client));
  65.         clients_list.remove(client);
  66.     }
  67.     else //向客户端发送信息
  68.     { 
  69.         if(clients_list.size() == 1) 
  70.         { 
  71.             CHK(send(client, STR_NOONE_CONNECTED, strlen(STR_NOONE_CONNECTED), 0));
  72.                 return len;
  73.         }
  74.         
  75.         sprintf(message, STR_MESSAGE, client, buf);
  76.         list<int>::iterator it;
  77.         for(it = clients_list.begin(); it != clients_list.end(); it++)
  78.         {
  79.            if(*it != client)
  80.            { 
  81.                 CHK(send(*it, message, BUF_SIZE, 0));
  82.            }
  83.         }
  84.     }

  85.     return len;
  86. }


10kclient.cpp

点击(此处)折叠或打开

  1. #include "local.h"
  2. #include "utils.h"

  3. using namespace std;

  4. char message[BUF_SIZE]; //接受服务器信息
  5. list<int> list_of_clients; //存放所有客户端
  6. int res;
  7. clock_t tStart;

  8. int main(int argc, char *argv[])
  9. {
  10.     int sock; 
  11.     struct sockaddr_in addr;
  12.     addr.sin_family = PF_INET;
  13.     addr.sin_port = htons(SERVER_PORT);
  14.     addr.sin_addr.s_addr = inet_addr(SERVER_HOST);
  15.     
  16.     tStart = clock();

  17.     for(int i=; i<EPOLL_SIZE; i++) //生成EPOLL_SIZE个客户端,这里是10000个,模拟高并发
  18.     {
  19.        CHK2(sock,socket(PF_INET, SOCK_STREAM, 0));
  20.        CHK(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0);
  21.        list_of_clients.push_back(sock);

  22.        bzero(&message, BUF_SIZE);
  23.        CHK2(res,recv(sock, message, BUF_SIZE, 0));
  24.        printf("%s\n", message);
  25.     }
  26.    
  27.     list<int>::iterator it; //移除所有客户端
  28.     for(it = list_of_clients.begin(); it != list_of_clients.end() ; it++)
  29.        close(*it);

  30.     printf("Test passed at: %.2f second(s)\n", (double)(clock() - tStart)/CLOCKS_PER_SEC); 
  31.     printf("Total server connections was: %d\n", EPOLL_SIZE);
  32.     
  33.     return 0;
  34. }


参考资料:
google code  svn路径:http://sechat.googlecode.com/svn/trunk/
http://www.cnblogs.com/venow/archive/2012/11/30/2790031.html
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 很高兴为您提供一个epoll聊天室代码,如下所示:#include <sys/epoll.h> #include <fcntl.h>int main(int argc, char **argv) { int epfd, nfds; struct epoll_event ev, events[MAX_CLIENTS]; char buf[MAX_MSG_SIZE]; int i, connfd, n; // Create epoll file descriptor epfd = epoll_create(MAX_CLIENTS); if (epfd == -1) { perror("epoll_create failed"); exit(EXIT_FAILURE); } // Add the listening socket to the epoll set ev.events = EPOLLIN; ev.data.fd = sockfd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) { perror("epoll_ctl: listen_sock"); exit(EXIT_FAILURE); } // Loop forever, waiting for events for (;;) { nfds = epoll_wait(epfd, events, MAX_CLIENTS, -1); if (nfds == -1) { perror("epoll_wait"); exit(EXIT_FAILURE); } for (i = 0; i < nfds; i++) { // Handle new connections if (events[i].data.fd == sockfd) { connfd = accept(sockfd, (struct sockaddr *) &cliaddr, &clilen); if (connfd == -1) { perror("accept"); exit(EXIT_FAILURE); } setnonblocking(connfd); ev.events = EPOLLIN | EPOLLET; ev.data.fd = connfd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev) == -1) { perror("epoll_ctl: connfd"); exit(EXIT_FAILURE); } } else { // Handle data from a client do { n = read(events[i].data.fd, buf, MAX_MSG_SIZE); if (n > 0) { // Send the message to all clients for (j = 0; j < MAX_CLIENTS; j++) { if (clientfd[j] != 0) { write(clientfd[j], buf, n); } } } } while (n > 0); if (n == 0) { // Connection closed by client close(events[i].data.fd); } else { perror("read"); exit(EXIT_FAILURE); } } } } } ### 回答2: 下面是一个使用epoll实现的简单聊天室代码: ```python import socket import select # 创建一个TCP套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定地址和端口 server_address = ('', 8888) server_socket.bind(server_address) # 开始监听 server_socket.listen(10) # 创建一个epoll对象 epoll = select.epoll() # 注册服务器套接字到epoll对象上,并监听读事件 epoll.register(server_socket.fileno(), select.EPOLLIN) # 保存客户端套接字与地址的映射关系 client_sockets = {} client_addresses = {} while True: events = epoll.poll(1) for fileno, event in events: # 如果是服务器套接字上的事件,表示有新的连接请求 if fileno == server_socket.fileno(): client_socket, client_address = server_socket.accept() print('New client connected:', client_address) # 在epoll对象上注册客户端套接字,并监听读事件 epoll.register(client_socket.fileno(), select.EPOLLIN) # 保存客户端套接字和地址 client_sockets[client_socket.fileno()] = client_socket client_addresses[client_socket.fileno()] = client_address # 如果是客户端套接字上的事件,表示有数据到达 elif event & select.EPOLLIN: receiving_socket = client_sockets[fileno] data = receiving_socket.recv(1024) if data: print('Received data:', data.decode('utf-8')) for client_fileno, client_socket in client_sockets.items(): if client_fileno != fileno: client_socket.sendall(data) else: # 如果没有数据到达,表示客户端已断开连接 epoll.unregister(fileno) receiving_socket.close() del client_sockets[fileno] del client_addresses[fileno] # 关闭服务器套接字和epoll对象 server_socket.close() epoll.close() ``` 这段代码实现了一个简单的聊天室服务器,使用epoll来实现并发处理客户端连接和数据传输。当有新的连接请求时,会将客户端套接字注册到epoll对象上,并监听读事件。当客户端套接字上有数据到达时,会将该数据发送给其他所有客户端。同时也会处理客户端断开连接的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值