C\C++采用epoll实现聊天室

简介: C\C++采用epoll实现聊天室

下面是epoll的实现图




epoll 是一个 Linux 下用于高效 I/O 多路复用的机制,它提供了一组系统调用函数来管理事件和文件描述符。以下是 epoll 的主要函数:


1.epoll_create:创建 epoll 实例


1.epoll_create:创建 epoll 实例

int epoll_create(int size);

创建一个新的 epoll 实例,返回一个文件描述符。

size 参数指定了内核应该为该实例预分配的文件描述符表的大小。这个参数在实际使用中通常可以设置为一个较大的值,表示能够监视的文件描述符的最大数量。



2.epoll_ctl:注册、修改或删除事件

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epfd 是 epoll 实例的文件描述符。

op 指定操作类型,可以是 EPOLL_CTL_ADD(添加新的文件描述符)、EPOLL_CTL_MOD(修改已注册的文件描述符的事件)或 EPOLL_CTL_DEL(从 epoll 实例中删除文件描述符)。

fd 是要操作的目标文件描述符。

event 是一个结构体,用于描述文件描述符上的事件,包括事件类型和关联的用户数据。


3.struct epoll_event 结构体


原文链接:https://blog.csdn.net/festaw/article/details/135173172

   struct epoll_event {
       uint32_t events;   // 表示注册的事件类型
       epoll_data_t data; // 用户数据,通常是文件描述符或其他标识符
   };
  • events 字段表示注册的事件类型,可以是 EPOLLIN(可读事件)、EPOLLOUT(可写事件)、EPOLLERR(错误事件)等。
  • data 字段包含与文件描述符关联的用户数据。

4.epoll_wait:等待事件的发生

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epfd 是 epoll 实例的文件描述符。

events 是用于存储事件的数组。

maxevents 指定了 events 数组的大小,表示最多等待多少个事件。

timeout 指定等待的超时时间,单位是毫秒。如果设置为 -1,表示无限等待,直到有事件发生;如果设置为 0,表示非阻塞模式,立即返回。

返回就绪的文件描述符数量,如果超时则返回 0,如果出错返回 -1。

这些函数共同构成了 epoll 的基本操作。通过这些函数,应用程序可以实现对大量文件描述符的高效管理和事件处理。

这里有个坏习惯,代码没有封装。。。看着逻辑不是很清晰

下一期更新粘包、分包问题

server:(改端口和ip)

#include <iostream>
#include <string>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <map>
#include <arpa/inet.h>
using namespace std;
 
 
//最大连接数
const int MAX_CONN = 1024;
 
 
//保存客户端的信息
 
struct Client
{
  int sockfd;
  string name;//名字
};
 
 
 
int main() {
 
  // 创建epoll实例
  int epld = epoll_create1(0);
  if (epld < 0)
  {
    perror("epoll create error\n");
    return -1;
  }
 
  //创建监听的socket
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0)
  {
    perror("socket error\n");
    return -1;
  }
 
  //绑定本地ip和端口
  struct  sockaddr_in addr;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_family = AF_INET;
  addr.sin_port = htons(9999);
 
  int ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
  if (sockfd < 0)
  {
    perror("bind error\n");
    return -1;
  }
  
 
  //监听客户端
  ret = listen(sockfd, 1024);
  if (ret < 0)
  {
    perror("listen error\n");
    return -1;
  }
 
  //将监听的socket加入epoll
  struct epoll_event ev;
  ev.events = EPOLLIN;
  ev.data.fd = sockfd;
 
  ret = epoll_ctl(epld, EPOLL_CTL_ADD, sockfd, &ev);
  if (ret < 0)
  {
    perror("epoll_ctl error\n");
    return -1;
  }
 
  //保存客户端信息
  map<int, Client> clients;
 
  //循环监听
  while (1)
  {
    struct epoll_event evs[MAX_CONN];
    int n = epoll_wait(epld, evs, MAX_CONN, -1);
    if (n < 0)
    {
      perror("epoll_wait error\n");
      return -1;
    }
    for (int i = 0; i < n; i++)
    {
      int fd = evs->data.fd;
      //如果是监听的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\n");
          continue;
        }
        //将客户端的socket加入epoll
        struct epoll_event ev_client;
        ev_client.events = EPOLLIN;//监测客户端有没有消息过来
        ev_client.data.fd = client_sockfd;
        ret = epoll_ctl(epld,EPOLL_CTL_ADD, client_sockfd,&ev_client);
        if (ret < 0)    
        {
          perror("epoll_ctl error\n");
          break;
        }
        char  ip_str[16] = "";
        inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip_str,16);
        printf("%s正在链接...\n", ip_str);
 
        //保存该客服端的信息
        Client client;
        client.sockfd = client_sockfd;
        client.name = "";
 
        clients[client_sockfd] = client;
      }
      else//如果是客户端消息
      {
        char buffer[1024];
        size_t n = read(fd, buffer, 1024);
        if (n < 0)
        {
          //处理错误
          break;
        }
        else if (n == 0)
        {
          //客服端断开连接
          close(fd);
          epoll_ctl(epld, EPOLL_CTL_ADD, fd, 0);
 
          clients.erase(fd);
        }
        else
        {
          string msg(buffer, n);
          //如果该客户端name为空,说明该消息为这个客户端的用户名
          if (clients[fd].name == "")
          {
            clients[fd].name = msg;
          }
          else//否则是聊天消息
          {
            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);
              }
            }
          }
        }
      }
    }
  }
  
  //关闭epoll实例
  close(epld);
  close(sockfd);  
 
 
}   

client:(改端口和ip)

#include <stdio.h>
#include <WinSock2.h>
#include <Windows.h>
#include <WS2tcpip.h>
 
#pragma comment(lib,"Ws2_32.lib")
#define BUF_SIZE 1024
char szMsg[BUF_SIZE];
 
unsigned SendMsg(void* arg)
{
    SOCKET sock = *((SOCKET*)arg);
    while (1)
    {
        scanf_s("%s", szMsg,999);
        if (!strcmp(szMsg, "QUIT\n") || !strcmp(szMsg, "quit\n"))
        {
            closesocket(sock);
            exit(0);
        }
 
        send(sock, szMsg, strlen(szMsg), 0);
    }
    return 0;
}
 
 
 
unsigned RecvMsg(void* arg)
{
    SOCKET sock = *((SOCKET*)arg);
    char msg[BUF_SIZE];
    while (1)
    {
        int len = recv(sock, msg, sizeof(msg) - 1, 0);
        if (len == -1)
        {
            return -1;
        }
        msg[len] = '\0';
        printf("%s\n", msg);
    }
    return 0;
}
 
 
int main() {
 
    //初始化socket环境
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
 
    wVersionRequested = MAKEWORD(2, 2);
 
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        return -1;
    }
    if (LOBYTE(wsaData.wVersion) != 2 ||
        HIBYTE(wsaData.wVersion) != 2) {
 
        WSACleanup();
        return -1;
    }
    //创建socket
    SOCKET hSock;
    //SOCKADDR_IN servAdr;
    hSock = socket(AF_INET, SOCK_STREAM, 0);
 
    //绑定端口
    SOCKADDR_IN servAdr;
    memset(&servAdr, 0, sizeof(servAdr));
    servAdr.sin_family = AF_INET;
    servAdr.sin_port = htons(9999);
    inet_pton(AF_INET, "**********", &servAdr.sin_addr);
 
    //连接服务器
    if (connect(hSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)
    {
        printf("connect error: %d", GetLastError());
        return -1;
    }
    else
    {
        printf("欢迎来到py交易私人聊天室,请输入你的聊天用户名:");
    }
 
    //循环发消息
    HANDLE hSendHand = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SendMsg, (void*)&hSock, 0, NULL);
 
    //循环收消息
    HANDLE hRecvHand = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RecvMsg, (void*)&hSock, 0, NULL);
 
    //等待线程结束
    WaitForSingleObject(hSendHand, INFINITE);
    WaitForSingleObject(hRecvHand, INFINITE);
 
    closesocket(hSock);
    WSACleanup();
}
 
 
 
 
相关文章
|
30天前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
71 0
|
8月前
|
监控 网络协议 Java
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)(上)
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)
127 0
|
8月前
|
存储 监控 网络协议
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)(下)
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)
143 0
|
30天前
【基于C++HTTP 服务器的epoll 改造】
【基于C++HTTP 服务器的epoll 改造】
|
10月前
|
网络协议 Linux C++
Linux TCP作为服务器连接的单连接、Select、Poll和Epoll方式:C/C++实现高效的服务器通信
在Linux服务器开发中,TCP(Transmission Control Protocol)作为面向连接的通信方式,为实现可靠的服务器通信提供了强大支持。不同的服务器连接方式,如单连接、Select、Poll和Epoll,各有优势,可以根据连接数和性能需求选择合适的方式。本文将深入探讨这四种方式的实现原理,并给出C/C++代码例子,帮助读者更好地理解和使用这些方式。
129 0
|
监控 Linux C++
C++ linux epoll并发服务器模型初探
socket通讯流程图 最简单的可以通讯的C++服务器端代码: #include #include #include #include #include #include #define SERV_PORT 8000 int main(vo...
2522 0
|
1天前
|
程序员 C语言 C++
【C++语言】继承:类特性的扩展,重要的类复用!
【C++语言】继承:类特性的扩展,重要的类复用!
|
1天前
|
C++ 容器 存储
【C++语言】想学STL,先细细拿捏string类,万字详解string类 (内附精美思维导图)
【C++语言】想学STL,先细细拿捏string类,万字详解string类 (内附精美思维导图)
|
1天前
|
C++ 编译器
【C++语言】Date类的代码实现(操作符重载运用)
【C++语言】Date类的代码实现(操作符重载运用)
|
1天前
|
C++ 索引
C++ string类常用操作
C++ string类常用操作