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();
}
 
 
 
 
相关文章
|
6月前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
112 0
|
监控 网络协议 Java
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)(上)
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)
248 0
|
1月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
22 0
Linux C/C++之IO多路复用(poll,epoll)
|
存储 监控 网络协议
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)(下)
I/O多路复用【Linux/网络】(C++实现select、poll和epoll服务器)
215 0
|
6月前
【基于C++HTTP 服务器的epoll 改造】
【基于C++HTTP 服务器的epoll 改造】
|
网络协议 Linux C++
Linux TCP作为服务器连接的单连接、Select、Poll和Epoll方式:C/C++实现高效的服务器通信
在Linux服务器开发中,TCP(Transmission Control Protocol)作为面向连接的通信方式,为实现可靠的服务器通信提供了强大支持。不同的服务器连接方式,如单连接、Select、Poll和Epoll,各有优势,可以根据连接数和性能需求选择合适的方式。本文将深入探讨这四种方式的实现原理,并给出C/C++代码例子,帮助读者更好地理解和使用这些方式。
292 0
|
监控 Linux C++
C++ linux epoll并发服务器模型初探
socket通讯流程图 最简单的可以通讯的C++服务器端代码: #include #include #include #include #include #include #define SERV_PORT 8000 int main(vo...
2554 0
|
1天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
10 4
|
24天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
22 4
|
24天前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
20 4