第一:epoll
哈哈,百度这个比较直接,可以很清楚的告诉你。
第二:声明
不再追溯,可以先看看(基础版本)(进程版本)(select函数版本)再看这个,谢谢。
工具类我也不再写,可以查看(进程版本),一模一样,直接复制过来就可以用了。
客户端也和(进程版本)一样,所以后面的文章,就不再写客户端。
第三:代码
工具类:略,看声明
客户端:略,看声明
服务端
/*================================================================
* Copyright (C) 2021 ymbLite. All rights reserved.
*
* 文件名称:TcpServerEpoll.h
* 创 建 者:ymbLite
* 创建日期:2021年11月05日
* 描 述:
*
================================================================*/
#ifndef _TCPSERVEREPOLL_H
#define _TCPSERVEREPOLL_H
#include <sys/epoll.h>
#include <unistd.h>
const int EPOLL_SIZE = 100;//可以监视的数量
class TcpServerEpoll{
private:
struct epoll_event event; //创建一个结构体变量,用以保存监视事件的基本信息
int epoll_fd; //epoll的文件描述符
public:
TcpServerEpoll();//构造函数
~TcpServerEpoll();//析构函数
/*
* 注册监测事件
* sock_fd :需要被监测事件的文件描述符
*
* return :成功返回0,失败返回-1
* */
int AddEpollEvent(const int sock_fd);
/*
* 注销监测事件
* sock_fd :监测事件的文件描述符
*
* return :成功返回0,失败返回-1
* */
int DeleteEpollEvent(const int sock_fd);
/*
* 开启监视事件
* events :保存发生变化的文件描述符结构体数组
* return :成功返回发生事件的数量
* 失败返回-1,
*/
int EpollWait(epoll_event* events);
};
#endif //TCPSERVEREPOLL_H
/*================================================================
* Copyright (C) 2021 ymbLite. All rights reserved.
*
* 文件名称:TcpServerEpoll.cpp
* 创 建 者:ymbLite
* 创建日期:2021年11月05日
* 描 述:
*
================================================================*/
#include "TcpServerEpoll.h"
TcpServerEpoll::TcpServerEpoll(){
//创建一个epoll,大小位EPOLL_SIZE,这个函数返回的是一个文件描述符
epoll_fd = epoll_create(EPOLL_SIZE);
}
int TcpServerEpoll::AddEpollEvent(const int sock_fd){
event.events = EPOLLIN;
event.data.fd = sock_fd;
return epoll_ctl(epoll_fd , EPOLL_CTL_ADD , sock_fd , &event);
}
int TcpServerEpoll::DeleteEpollEvent(const int sock_fd){
return epoll_ctl(epoll_fd , EPOLL_CTL_DEL , sock_fd , NULL);
}
int TcpServerEpoll::EpollWait(epoll_event* events){
int event_cnt = epoll_wait(epoll_fd , events , EPOLL_SIZE , -1);
return event_cnt;
}
TcpServerEpoll::~TcpServerEpoll(){
close(epoll_fd);
}
/*================================================================
* Copyright (C) 2021 ymbLite. All rights reserved.
*
* 文件名称:TcpServer.h
* 创 建 者:ymbLite
* 创建日期:2021年11月03日
* 描 述:socket编程中,简单的TCP服务端
*
================================================================*/
#ifndef _TCPSERVER_H
#define _TCPSERVER_H
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/epoll.h>
#include "BaseUtil.h"
#include "TcpServerEpoll.h"
const int BUFF_SIZE = 1024;
using std::cout;
using std::endl;
using std::cin;
using std::string;
class TcpServer{
private:
struct sockaddr_in serv_addr; //服务器中服务端的网络地址结构
struct sockaddr_in clnt_addr; //服务端中客户端的网络地址结构
socklen_t clnt_addr_len; //服务器中客户端的网络地址结构的长度
int m_buflen; //调用Read方法后,读取的子接大小,单位是子接
epoll_event ep_events[EPOLL_SIZE];//保存监测事件变化的数组
TcpServerEpoll ser_epoll; //自己封装的epoll
public:
int serv_sock; //服务器中服务端的socket描述符
int clnt_sock; //服务器中客户端的socket描述符
private:
/*
* 阻塞接受客户端的连接
* return:
* -1:客户端连接失败
* 0:服务端未开启socket服务
* >0:成功接受一个客户端的连接,返回的是客户在服务端中的socket描述符
* */
int Accept();
public:
//构造函数
TcpServer();
//析构函数
~TcpServer();
/*
* 初始化服务器
* 服务端地址使用INADDR_ANY
* port:端口号
* */
bool InitServer(const string& port);
/*
* 接收客户端发送过来的数据
* buffer :接受数据缓冲区的地址,数据的长度保存在m_buflen成员变量中
* timeout :等待数据的超时时间,单位为秒,缺省为0-无限等待
*
* return :true-成功,false-失败,失败的情况有两种,1)超时等待,2)socket连接不可用
* */
bool Read( char* buffer , const int timeout = 0);
/*
* 向客户端写入数据
*
* buffer :带发送数据的缓冲区地址
* ibuflen :待发送数据的大小,单位:字节,缺省值为0,
* 如果是ascii字符串,ibuflen取0
* 如果是二进制数据流,那么ibuflen就取数据块的大小
*
* return :true-成功,false-失败,失败原因为socket连接不可用
* */
bool Write(const char* buffer , const int ibuflen = 0);
void StartEpoll();
/**
* 关闭服务器中服务端的套接字
*/
void CloseServerSock();
/*
* 关闭服务器中客户端的套接字
* */
void CloseClientSock();
};
#endif //TCPSERVER_H
/*================================================================
* Copyright (C) 2021 ymbLite. All rights reserved.
*
* 文件名称:TcpServer.cpp
* 创 建 者:ymbLite
* 创建日期:2021年11月03日
* 描 述:
*
================================================================*/
#include "TcpServer.h"
TcpServer::TcpServer():serv_sock(-1),clnt_sock(-1){
}
TcpServer::~TcpServer(){
CloseServerSock();
}
bool TcpServer::InitServer(const string& port){
//先判断服务端socket是否已开启
if(serv_sock > 0){
CloseServerSock();
serv_sock = -1;
}
//创建服务器中的服务端socket
serv_sock = socket(PF_INET , SOCK_STREAM , 0);
if(serv_sock == -1){
cout<<"服务器创建监听套接字失败,socket() error!"<<endl;
return false;
}
//绑定服务端的socket到相应的网络地址结构中
memset(&serv_addr , 0 , sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(port.c_str()));
if(bind(serv_sock , (struct sockaddr*)& serv_addr , sizeof(serv_addr)) == -1){
cout<<"服务器绑定套接字失败,bind() error!"<<endl;
//关闭服务端的socket
CloseServerSock();
return false;
}
//开始监听服务端的套接字
if(listen(serv_sock , 5) == -1){
cout<<"服务端监听套接字失败,listen() error!"<<endl;
//关闭服务端的socket
CloseServerSock();
return false;
}
if(ser_epoll.AddEpollEvent(serv_sock) == -1){
cout<<"服务端socket注册进epoll失败!"<<endl;
CloseServerSock();
return false;
}
cout<<"服务端初始化完成,开始接受客户端的连接..."<<endl;
return true;
}
int TcpServer::Accept(){
//判断服务端是否已经开启了socket
if(serv_sock == -1){
cout<<"服务器中未开启监听的socket,Accept() error!"<<endl;
return 0;
}
clnt_addr_len = sizeof(clnt_addr);
clnt_sock = accept(serv_sock , (struct sockaddr*)& clnt_addr , &clnt_addr_len);
return clnt_sock;
}
void TcpServer::StartEpoll(){
while(1){
//开始使用epoll监视事件的发生
int event_cnt = ser_epoll.EpollWait(ep_events);
if(event_cnt == -1){
//创建epoll_wait()函数失败
cout<<"epoll_wait() error!"<<endl;
break ;
}
for(int i = 0 ; i < event_cnt ; ++i){
int _fd = ep_events[i].data.fd;
if(_fd == serv_sock){
//有新的连接
int accept_res = Accept();
if(accept_res <= 0){
cout<<"accept() error!"<<endl;
break ;
}
ser_epoll.AddEpollEvent(accept_res);
cout<<"new client【"<<accept_res<<"】connect..."<<endl;
}else{
//出现了读写操作
char buf[BUFF_SIZE];
int buf_len;
if(!TcpBaseRead(_fd , buf , &buf_len)){
//可能是对端关闭了连接
cout<<"client【"<<_fd<<"】断开了连接!"<<endl;
ser_epoll.DeleteEpollEvent(_fd);
close(_fd);
}else{
buf[buf_len] = 0;
cout<<"客户端【"<<_fd<<"】说:"<<buf<<endl;
if(!TcpBaseWrite(_fd , buf , buf_len)){
//关闭
cout<<"client【"<<_fd<<"】断开了连接!"<<endl;
ser_epoll.DeleteEpollEvent(_fd);
close(_fd);
}
}
}
}
}
}
void TcpServer::CloseServerSock(){
if(serv_sock > 0){
cout<<"关闭了监听socket"<<endl;
close(serv_sock);
serv_sock = -1;
}
}
void TcpServer::CloseClientSock(){
if(clnt_sock > 0){
cout<<"关闭了连接socket"<<endl;
close(clnt_sock);
serv_sock = -1;
}
}
/*================================================================
* Copyright (C) 2021 ymbLite. All rights reserved.
*
* 文件名称:main.cpp
* 创 建 者:ymbLite
* 创建日期:2021年11月03日
* 描 述:
*
================================================================*/
#include "TcpServer.h"
int main(int argc ,char* argv[]){
if(argc != 2){
cout<<"Usage : "<<argv[0]<<" <PORT>"<<endl;
return -1;
}
//创建服务端的socket
TcpServer tcp_server;
//初始化服务器
bool init_res = tcp_server.InitServer(argv[1]);
if(!init_res){
cout<<"服务器初始化失败"<<endl;
return -1;
}
//
tcp_server.StartEpoll();
//关闭
tcp_server.CloseServerSock();
return 0;
}
第四:结尾
程序照样直接复制就可以编译运行。
还是那句话,如果有错误的,希望多多包涵,写错了的或者设计不合理的,可以讨论,我也只是初学者,谢谢。
希望可以帮助到你。
对了,如果要源码的,可以留下qq号,我到时候邮箱发给你。