C++ socket网络编程——即时通信系统( 二 )


3)epoll有两种工作方式,LT(Level triggered) 水平触发 、ET(Edge triggered)边沿触发 。LT是select/poll的工作方式,比较低效,而ET是epoll具有的高速工作方式 。更多epoll之ET LT
Epoll 用法(三步曲):
第一步:int epoll_create(int size)系统调用,创建一个epoll句柄,参数size用来告诉内核监听的数目,size为epoll支持的最大句柄数 。
第二步:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 事件注册函数
参数 epfd为epoll的句柄 。参数op 表示动作 三个宏来表示:EPOLL_CTL_ADD注册新fd到epfd 、EPOLL_CTL_MOD 修改已经注册的fd的监听事件、EPOLL_CTL_DEL从epfd句柄中删除fd 。参数fd为需要监听的标识符 。参数结构体epoll_event告诉内核需要监听的事件 。
第三步:int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout) 等待事件的产生,通过调用收集在epoll监控中已经发生的事件 。参数struct epoll_event 是事件队列 把就绪的事件放进去 。
eg. 服务端使用epoll的时候步骤如下:
1.调用epoll_create()在linux内核中创建一个事件表 。
2.然后将文件描述符(监听套接字listener)添加到事件表中
3.在主循环中,调用epoll_wait()等待返回就绪的文件描述符集合 。
4.分别处理就绪的事件集合,本项目中一共有两类事件:新用户连接事件和用户发来消息事件 。
六:代码结构

C++ socket网络编程——即时通信系统

文章插图
 
每个文件的作用:
1.Common.h:公共头文件,包括所有需要的宏以及socket网络编程头文件,以及消息结构体(用来表示消息类别等)
2.Client.h Client.cpp :客户端类的实现
3.Server.h Server.cpp : 服务端类的实现
4.ClientMain.cpp ServerMain.cpp 客户端及服务端的主函数 。
七:代码实现
Common.h
定义一些共用的宏定义,包括一些共用的网络编程相关头文件 。
1)定义一个函数将文件描述符fd添加到epfd表示的内核事件表中供客户端和服务端两个类使用 。
2)定义一个信息数据结构,用来表示传送的信息,结构体包括发送方fd, 接收方fd,用来表示消息类别的type,还有文字信息 。
函数recv() send() write() read() 参数传递是字符串,所以在传送前/接受后要把结构体转换为字符串/字符串转换为结构体 。
#ifndef CHATROOM_COMMON_H
#define CHATROOM_COMMON_H
 
#include <IOStream>
#include <list>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
// 默认服务器端IP地址
#define SERVER_IP "127.0.0.1"
 
// 服务器端口号
#define SERVER_PORT 8888
 
// int epoll_create(int size)中的size
// 为epoll支持的最大句柄数
#define EPOLL_SIZE 5000
 
// 缓冲区大小65535
#define BUF_SIZE 0xFFFF
 
// 新用户登录后的欢迎信息
#define SERVER_WELCOME "Welcome you join to the chat room! Your chat ID is: Client #%d"
 
// 其他用户收到消息的前缀
#define SERVER_MESSAGE "ClientID %d say >> %s"
#define SERVER_PRIVATE_MESSAGE "Client %d say to you privately >> %s"
#define SERVER_PRIVATE_ERROR_MESSAGE "Client %d is not in the chat room yet~"
// 退出系统
#define EXIT "EXIT"
 
// 提醒你是聊天室中唯一的客户
#define CAUTION "There is only one int the char room!"
 
// 注册新的fd到epollfd中
// 参数enable_et表示是否启用ET模式,如果为True则启用,否则使用LT模式
static void addfd( int epollfd, int fd, bool enable_et )
{
struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLIN;
if( enable_et )
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
// 设置socket为非阻塞模式
// 套接字立刻返回,不管I/O是否完成,该函数所在的线程会继续运行
//eg. 在recv(fd...)时,该函数立刻返回,在返回时,内核数据还没准备好会返回WSAEWOULDBLOCK错误代码
fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0)| O_NONBLOCK);
printf("fd added to epoll!nn");
}
 
//定义信息结构,在服务端和客户端之间传送


推荐阅读