这个我就不说了,我也说不清楚,初学者,多多见谅,百度可以很详细的告诉你。
不再追溯,可以先看看(基础版本)(进程版本)再看这个,谢谢。
工具类我也不再写,可以查看(进程版本),一模一样,直接复制过来就可以用了。
客户端也和(进程版本)一样,所以后面的文章,就不再写客户端。
工具类:略,看声明
客户端:略,看声明
服务端
/*================================================================
* Copyright (C) 2021 ymbLite. All rights reserved.
*
* 文件名称:TcpServerSelect.h
* 创 建 者:ymbLite
* 创建日期:2021年11月05日
* 描 述:服务器端的I/O复用
*
================================================================*/
#ifndef _TCPSERVERSELECT_H
#define _TCPSERVERSELECT_H
#include
#include
#include
#include
using std::cout;
using std::endl;
using std::vector;
class TcpServerSelect{
private:
int max_fd; //select()函数监听的最大文件描述符的数量
fd_set reads; //保存原始的监听描述符结构
fd_set copy_read; //复制一个原始的监听描述符解雇
struct timeval timeout; //设置select函数监听的超时时间
public:
TcpServerSelect(const int _max_fd);//构造函数
//析构函数
//创建监听
void AddSocketFD(const int _max_fd);
//移除监听
void DeleteSocketFD(const int _fd);
//等待select()函数
int SelectWait(vector& ver_fd);
};
#endif //TCPSERVERSELECT_H
/*================================================================
* Copyright (C) 2021 ymbLite. All rights reserved.
*
* 文件名称:TcpServerSelect.cpp
* 创 建 者:ymbLite
* 创建日期:2021年11月05日
* 描 述:
*
================================================================*/
#include "TcpServerSelect.h"
TcpServerSelect::TcpServerSelect(const int _sock_fd):max_fd(_sock_fd){
//初始化原始的fd_set结构体变量
//其实我认为这个结构体变量主要就是用来保存注册的监听信息的
FD_ZERO(&reads);
//先设置监听的位
FD_SET(max_fd , &reads);
}
void TcpServerSelect::AddSocketFD(const int _max_fd){
//构造这个监听位
FD_SET(_max_fd , &reads);
if(_max_fd > max_fd){
//如果新的描述符大于原来的描述符,那么就替换
max_fd = _max_fd;
}
cout<<"新增监听位成功!"<& vec_fd){
//调用在调用的时候,已经传入了初始的监听数量和初始化了一个监听位
copy_read = reads;
//设置监听的时间
timeout.tv_sec = 5;
timeout.tv_usec = 5000;
int select_res;
if( (select_res = select(max_fd + 1 , ©_read , 0 , 0 , &timeout)) == -1){
//调用select函数发生了错误
//退出监听,结束程序
return -1;
}
if(select_res == 0){
//select函数监听到达超时时间,要返回点什么
return 0;
}
//循环获取监听存在变化的位
for(int i = 0 ; i < max_fd + 1 ; ++i){
if(FD_ISSET(i , ©_read)){
//把存在变化的监听位放在容器中
vec_fd.push_back(i);
}
}
return 1;
}
/*================================================================
* Copyright (C) 2021 ymbLite. All rights reserved.
*
* 文件名称:TcpServer.h
* 创 建 者:ymbLite
* 创建日期:2021年11月03日
* 描 述:socket编程中,简单的TCP服务端
*
================================================================*/
#ifndef _TCPSERVER_H
#define _TCPSERVER_H
#include
#include
#include
#include
#include
#include
#include
#include "BaseUtil.h"
#include "TcpServerSelect.h"
const int BUFF_SIZE = 1024;
using std::cout;
using std::endl;
using std::cin;
using std::string;
using std::vector;
class TcpServer{
private:
struct sockaddr_in serv_addr; //服务器中服务端的网络地址结构
struct sockaddr_in clnt_addr; //服务端中客户端的网络地址结构
socklen_t clnt_addr_len; //服务器中客户端的网络地址结构的长度
int m_buflen; //调用Read方法后,读取的子接大小,单位是子接
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);
/*
* 开始使用select函数进行I/O复用
* */
void StartSelect();
/**
* 关闭服务器中服务端的套接字
*/
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!"< _ver_fd;
while(1){
cout<<"开始调用select函数,监听位的变化"< 0){
cout<<"关闭了监听socket"< 0){
cout<<"关闭了连接socket"<
/*================================================================
* 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 : "<"<
这里使用select函数,进行I/O的复用,具体select函数,望参考者直接百度,这样学的更明白,我也只是局限于会用。
程序照样直接复制就可以编译运行。
还是那句话,如果有错误的,希望多多包涵,写错了的或者设计不合理的,可以讨论,我也只是初学者,谢谢。
希望可以帮助到你。
对了,如果要源码的,可以留下qq号,我到时候邮箱发给你。