Qt一步步搭建TcpServer1——封装QTcpServer,QTcpSocket
来源:互联网 发布:thinkphp5开发大型cms 编辑:程序博客网 时间:2024/05/01 13:47
- 1序
- 1NetAPI的设计思路
- 2了解QTcpServer
- 3设计QtcpServer
- 4开始
- 2TcpServer的封装
- 3TcpSession的封装
- 4总结
承接上章:Qt一步步搭建TcpServer0——序
要搭建网络库,那么肯定要从Server和Socket开始入手,本章就从封装QTcpServer和QTcpSocket开始入手,做好第一步工作。
1、序
1.1、NetAPI的设计思路
画了一张图来描述思路,一般来说,网络库的思路大都如下:
其实图中注释描述的也比较清楚,能够较为直观体现出Server端的网络库多线程工作流程,我觉得也就没必要再过多解释了。
1.2、了解QTcpServer
官网文档地址:QTcpServer
关于QTcpServer的介绍,官方已经说的比清楚了:
基本上是属于傻瓜式编程,大部分的工作都由底层给我们做好了。通常情况下,我们只需要监听一下端口,绑定个newConnection()就可以在新连接连入的时候获取到套接字:
int port = this->ui->spinBox->value();QTcpServer *tcpserver = new QTcpServer();tcpserver->listen(QHostAddress::Any, port);connect(tcpserver, &QTcpServer::newConnection, this, &MainWindow::SlotNewConnection);
然后通过nextPendingConnection获取套接字就行了。
1.3、设计QtcpServer
关于QTcpServer的设计其实主要有两种方式:
1、组合:如上面官方介绍所言,以组合的形式封装QTcpServer,然后连接newConnection信号槽,在槽函数里nextPendingConnection获取新socket,而这个socket将由QTcpServer来管理,自动删除:
2、继承:继承自QTcpServer,然后重载函数incomingConnection,在函数里自己去创建Socket对象并管理。
方式一和方式二其实都可以,但对我而言,我需要自定义socket,并且这个socket将会抛给上层,上层在整个过程中可能会存在需要使用到socket对象的情况,生命周期需要我的网络库来掌管,那么我将使用方式二。
1.4、开始
新建项目,在pro文件中添加network库:
QT += core gui network
2、TcpServer的封装
TcpServer封装自QTcpServer,基本功能底层已经做了很多,那么我们要做的就是做成能够直接用于项目需求上的接口:
头文件:
class TcpServer : public QTcpServer{ Q_OBJECTpublic: TcpServer(); ~TcpServer(); bool Start(int port); void Stop(); size_t GetSessionSize() const ;public: //新连接回调 std::function<void(TcpSession*)> OnAccepted = nullptr;protected: virtual void incomingConnection(qintptr handle);private: bool IsRunning_ = false; QThread *Thread_; std::vector<TcpSession*> SessionList_;};
一个Server首先需要提供的肯定是Start和Stop,这点不用多说,OnAccepted 是连接回调,通知上层,并把新连接丢给上层,上层拿到之后,是持有还是简单绑定回调就是上层的事情了。incomingConnection是需要重载的函数,qt的net库Accept后通知我们这层,我们再去创建socket(也就是TcpSession)。
cpp:
TcpServer::TcpServer(){}TcpServer::~TcpServer(){ this->Stop();}bool TcpServer::Start(int port){ if(IsRunning_) return true; Thread_ = new QThread(); Thread_->start(); //监听端口 if(!this->listen(QHostAddress::Any, (quint16)port)) return false; IsRunning_ = true; return true;}void TcpServer::Stop(){ if(!IsRunning_) return; this->close(); Thread_->exit(); Thread_->wait(); delete Thread_; for(TcpSession *session : this->SessionList_) { session->Disconnect(); } for(TcpSession *session : this->SessionList_) { delete session; } this->SessionList_.clear(); Thread_ = nullptr; IsRunning_ = false;}size_t TcpServer::GetSessionSize() const{ return this->SessionList_.size();}void TcpServer::incomingConnection(qintptr handle){ TcpSession *session = new TcpSession(); session->setSocketDescriptor(handle); session->moveToThread(this->Thread_); this->SessionList_.push_back(session); //通知上层 if(this->OnAccepted) this->OnAccepted(session);}
仔细一看,好嘛 ,我封装的也啥都没干,只是桥接了下启动和把新连接都丢到一个线程里去了。不着急,这只是第一步。当然,主要原因是该提供的接口函数QTcpServer已经提供了大部分功能(包括error回调也提供了)。
Stop函数,Stop函数主要是负责关闭线程,清理内存等,我在后面会讲,这也有一些需要补充的内容。在目前我们假设已经顺利关服,并且清理了Session。
此时存在一个问题,Session在OnAccept的时候需要往上抛,上层拿到之后可能会持有,写数据和disconnect可能不是在同一个线程。所以断开连接的时候,或者说关服的时候,正好业务层写数据,而session已经被析构了,这就可能存在隐患,所以这里引入shared_ptr:
头文件:
class TcpServer : public QTcpServer{ Q_OBJECTpublic: TcpServer(); ~TcpServer(); bool Start(int port); void Stop(); size_t GetSessionSize() const ;public: //新连接回调 std::function<void(std::shared_ptr<TcpSession> &)> OnAccepted = nullptr;protected: virtual void incomingConnection(qintptr handle);private: bool IsRunning_ = false; QThread*Thread_; std::vector<std::shared_ptr<TcpSession>> SessionList_;};
cpp:
TcpServer::TcpServer(){}TcpServer::~TcpServer(){ this->Stop();}bool TcpServer::Start(int port){ if(IsRunning_) return true; Thread_ = new QThread(); Thread_->start(); //监听端口 if(!this->listen(QHostAddress::Any, (quint16)port)) return false; IsRunning_ = true; return true;}void TcpServer::Stop(){ if(!IsRunning_) return; this->close(); Thread_->exit(); Thread_->wait(); delete Thread_; for(std::shared_ptr<TcpSession> &session : this->SessionList_) { if(session.get()) session.get()->Disconnect(); } this->SessionList_.clear(); Thread_ = nullptr; IsRunning_ = false;}size_t TcpServer::GetSessionSize() const{ return this->SessionList_.size();}void TcpServer::incomingConnection(qintptr handle){ std::shared_ptr<TcpSession> session = std::make_shared<TcpSession>(); session->setSocketDescriptor(handle); session->moveToThread(this->Thread_); this->SessionList_.push_back(session); if(this->OnAccepted) this->OnAccepted(session);}
到这里,TcpServer的封装大致完成,上层可以直接用了,拿到Session之后也不用担心内存问题。
3、TcpSession的封装
相对来说,TcpSession的封装更简单了,这里做封装目前没新增什么,之后可能会加入线程的指针或者别的数据等,不管怎样,还是自定义比较方便。
头文件:
#ifndef TCPSESSION_H#define TCPSESSION_H#include <QTcpSocket>#include <functional>class TcpSession : public QTcpSocket{ Q_OBJECTsignals: void SignalRead(const char *data, int len);public: TcpSession(); ~TcpSession(); void Disconnect(); qint64 Write(const char *data, qint64 len); qint64 Write(const char *data);protected:private slots: void SlotStartRead();};#endif // TCPSESSION_H
cpp:
TcpSession::TcpSession(){ connect(this, &TcpSession::readyRead, this, &TcpSession::SlotStartRead);}TcpSession::~TcpSession(){ disconnect(this, &TcpSession::readyRead, this, &TcpSession::SlotStartRead);}void TcpSession::Disconnect(){ this->disconnectFromHost();}qint64 TcpSession::Write(const char *data, qint64 len){ return this->write(data, len);}qint64 TcpSession::Write(const char *data){ return this->write(data);}void TcpSession::SlotStartRead(){ QByteArray buffer; buffer = this->readAll(); emit this->SignalRead(buffer.toStdString().c_str(), buffer.length());}
代码比较简单,没什么解释的必要。这里的工作,主要是统一了下发送数据和接收数据的接口参数,之后可能会自定义一个参数类型(看我有没有时间更),方便包的传输。
4、总结
到这里,Socket和Server都封装好了,原本的信号槽也可以直接用。主界面写写就可以直接点击启动了,但我们能发现,目前我这里只是把所有的Session都丢到同一个线程去处理了。这就有很大的问题,假如十个,百个连接还好说,但是有千个万个连接呢?显然不能这么处理。
那么怎么办?其实在写博客之前,我也简单浏览了下别人的设计,大抵分为两派,一派是不处理,不用线程;另一派是一个连接一个线程。。。。。。也不是说不行,但是觉得不是特别好。
这个时候,就需要引入线程池,每个连接连入的时候,动态的根据线程池中线程负载情况来分配,尽量均衡的让一个线程处理多个连接,而我们只需要维护这几个线程就行了。那么下篇再说。
- Qt一步步搭建TcpServer1——封装QTcpServer,QTcpSocket
- Qt之QTcpServer/QTcpSocket简单收发信息
- Qt之QTcpServer/QTcpSocket简单收发信息
- Qt一步步搭建TcpServer4——Client的封装与网络库的使用
- Qt之QTcpServer/QTcpSocket简单收发信息(1)
- Qt之QTcpServer/QTcpSocket简单收发信息(2)
- Qt之QTcpServer/QTcpSocket简单收发信息(1)
- Qt之QTcpServer/QTcpSocket简单收发信息(1)
- QT 使用QTcpServer QTcpSocket 建立TCP服务器端 和 客户端
- Qt网络编程QTcpServer和QTcpSocket的理解
- Qt之QTcpServer/QTcpSocket简单收发信息(1)
- Qt下应用QTcpServer与QTcpSocket实现Tcp控制
- Qt下应用QTcpServer与QTcpSocket实现Tcp控制
- Qt之QTcpServer/QTcpSocket简单收发信息(1)
- Qt网络编程QTcpServer和QTcpSocket的理解
- Qt之QTcpServer/QTcpSocket简单收发信息(1)
- Qt之QTcpServer/QTcpSocket简单收发信息(2)
- Qt一步步搭建TcpServer0——序
- The Useless Toy(水题 暴力枚举)
- cookie和jsp的简介
- 【笨鸟先飞】Java重新学习日记7---高级数组之集合
- hdu 5365 计算几何 给几个点判断是否为正方形
- Codeforces 835 D Palindromic characteristics(区间DP)
- Qt一步步搭建TcpServer1——封装QTcpServer,QTcpSocket
- 仿饿了么搜索页面特效
- 指针数组实现字符排序
- Java常用类及其方法(五)Character
- RXD and math HDU
- 英文字体
- JS动态的生成表格
- el表达式及一些运用
- 设计——素材网站