Linux下基于C++11的socket网络编程(epoll版本)

第一: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号,我到时候邮箱发给你。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值