Qt一步步搭建TcpServer3——关闭与启动
来源:互联网 发布:windows toolkit 2.6 编辑:程序博客网 时间:2024/05/16 01:27
- 前言
- 1Server启动
- 2关闭Server
- 1修改TcpSession
- 2 线程池关闭
- 5总结
承接上章: Qt一步步搭建TcpServer2——线程池
本章将在上一章的基础上,讲述如何安全的关闭与启动Server。
前言
首先要更正上一篇的一个文字错误:
在这里这样用信号槽,并且删除Session是安全的。因为其事件循环机制是在按线程来的,在退出session线程之后,才会进入下一个线程的事件循环机制。因此这里不会存在这种隐患。
这里解释下,因为我代码是跟着博客每篇进度来写的,也就是说准备写某篇的时候,再把其功能实现,而设计这种工作本来是不断优化的。每一天的想法都可能不一样,或者说这算是具现每个阶段的过程思路,所以每一篇的代码,可以注意到和前一篇的方式和想法可能会略有出入。但整体不会变化。
1、Server启动
Server端的启动,主要依赖于监听端口和线程数量,可以封成Config类,包含数据的验证和初始化过程,如果需要拓展,也更方便:
TcpServer头文件:
//Server数据格式struct ServerData{ uint16_t Port; uint32_t ThreadNum; //验证逻辑,不合法则设定初始值 void Verify() { if(Port == 0) Port = 12345; if(ThreadNum) ThreadNum = std::thread::hardware_concurrency(); }};class TcpServer : public QTcpServer{ Q_OBJECTpublic: TcpServer(); ~TcpServer(); bool Start(ServerData &conf); ...... //后面代码就不贴了
cpp:
bool TcpServer::Start(ServerData &conf){ if(IsRunning_) return true; //验证数据 conf.Verify(); //启动线程池 SessionThreads_.Start(conf.ThreadNum); //监听端口 if(!this->listen(QHostAddress::Any, (quint16)conf.Port)) return false; IsRunning_ = true; qDebug() << "TcpServer::Start threadID:"<< QThread::currentThreadId(); return true;}
2、关闭Server
之前在公司封装asio库的时候,同事间交流感觉,这种网络库的封装,其实安全的关服比启动更加困难。毕竟涉及到多线程读写的问题。
所以在写这个之前我觉得关服可能也需要特别在意,包括在写上一章的的时候,我也说了一些关服的时候的问题,甚至当时还取巧直接写。但后来写本章的代码的时候,询问了下同事Qt的事件循环机制(原谅我不够理解),仔细一想其实压根没什么复杂的事情,用信号槽不久完事儿了,摊手= =。
上一篇里我提到,可能要用DoubleList(以后有时间单独更新一篇吧,其实就是双缓冲队列,逻辑上交换,写锁,读不锁)。现在看来比我想象的要简单许多。
那么就开始上代码吧:
2.1、修改TcpSession
既然要保证关服的顺序(参考上一章结尾),那TcpSession断开连接和写数据的操作,都需要在所属线程排队执行,才能确保没问题。
头文件:
#ifndef TCPSESSION_H#define TCPSESSION_H#include <QTcpSocket>#include <functional>#include "TcpThread.h"class TcpSession : public QTcpSocket{ Q_OBJECTsignals: void SignalRead(const QByteArray &, int); void SignalDisConnected(void *); void SignalDoWrite(const char *, qint64); void SignalDoDisConnect();public: TcpSession(TcpThread *thread); ~TcpSession(); //断开连接 void Disconnect(); void Write(const char *data, qint64 len);public: //断开连接回调,将来可能要用 std::function<void(void*)> OnDisConnected = nullptr;private slots: //开始读数据 void SlotStartRead(); //断开连接回调 void SlotDisConnected(); //写数据 void SlotDoWrite(const char *data, qint64 len); //断开连接 void SlotDoDisconnect();private: TcpThread *Thread_ = nullptr; QByteArray Buffer_ = nullptr;};#endif // TCPSESSION_H
cpp文件:
#include "TcpSession.h"TcpSession::TcpSession(TcpThread *thread){ this->Thread_ = thread; connect(this, &TcpSession::readyRead, this, &TcpSession::SlotStartRead); connect(this, &TcpSession::disconnected, this, &TcpSession::SlotDisConnected); connect(this, &TcpSession::SignalDoDisConnect, this, &TcpSession::SlotDoDisconnect); connect(this, &TcpSession::SignalDoWrite, this, &TcpSession::SlotDoWrite);}TcpSession::~TcpSession(){ disconnect(this, &TcpSession::readyRead, this, &TcpSession::SlotStartRead); disconnect(this, &TcpSession::disconnected, this, &TcpSession::SlotDisConnected); disconnect(this, &TcpSession::SignalDoDisConnect, this, &TcpSession::SlotDoDisconnect); disconnect(this, &TcpSession::SignalDoWrite, this, &TcpSession::SlotDoWrite);}void TcpSession::Disconnect(){ qDebug() << "TcpSession::Disconnect threadID:"<< QThread::currentThreadId(); emit this->SignalDoDisConnect();}void TcpSession::Write(const char *data, qint64 len){ emit this->SignalDoWrite(data, len);}void TcpSession::SlotStartRead(){ qDebug() << "TcpSession::SlotStartRead threadID:"<< QThread::currentThreadId(); Buffer_ = this->readAll(); emit this->SignalRead(Buffer_.toStdString().c_str(), Buffer_.length());}void TcpSession::SlotDisConnected(){ if(Thread_) --Thread_->SessionCount; //通知会话断开连接 if(OnDisConnected) OnDisConnected(this); emit this->SignalDisConnected(this);}void TcpSession::SlotDoWrite(const char *data, qint64 len){ qDebug() << "TcpSession::SlotDoWrite threadID:"<< QThread::currentThreadId(); this->write(data, len);}void TcpSession::SlotDoDisconnect(){ qDebug() << "TcpSession::SlotDoDisconnect threadID:"<< QThread::currentThreadId(); this->disconnectFromHost();}
Connection默认是auto,这里会丢到所属线程,不了解的可以补补Connection的第五个参数。
2.2 线程池关闭:
void SessionThreads::Stop(){ if(!IsRunning_) return; { std::lock_guard<std::mutex> locker(this->Lock_); std::unordered_map<void*, std::shared_ptr<TcpSession>>::iterator itor = SessionList_.begin(); //关闭连接 for(itor = SessionList_.begin(); itor != SessionList_.end(); ++itor) { std::shared_ptr<TcpSession> session = itor->second; if(session.get()) { disconnect(session.get(), &TcpSession::SignalDisConnected, this, &SessionThreads::SlotSessionDisConnected); session.get()->Disconnect(); } } } for(TcpThread *thread : this->ThreadList_) { thread->exit(); thread->wait(); } for(TcpThread *thread : this->ThreadList_) delete thread; this->ThreadList_.clear(); this->SessionList_.clear(); IsRunning_ = false;}
代码比较简单,就是按照顺序,先断开连接,然后等线程推出循环。最后清空TcpSession列表。
5、总结
到这里NetApi的Server端的已经完成,TcpSession也可直接用于客户端,当然,你得自建立一个线程,传入进去。对于上层来说,还是有点麻烦,所以打算Client也来一个类似Server一样的ClientManager:这样的好处是,可以客户端也有个Manager了,比较一个客户端连N个Server的情况还是比较常见。
所以下一章把Client也搞下。
这里把到这个阶段的代码上传下,Client只是个测试端,还没怎么搞,比较简单。Server端的界面也很简单,代码都还没整理,包括些debug信息还在,最后再统一整理吧:
Server:
Client:
代码地址:
TcpServerDemo
有什么问题或建议,可以留言或者联系QQ:1281581259 ,谢谢。
2017.11.30更新:
可以直接下载下面的,更完善。
基于QtcpServer的网络库
- Qt一步步搭建TcpServer3——关闭与启动
- Qt一步步搭建TcpServer0——序
- Qt一步步搭建TcpServer4——Client的封装与网络库的使用
- Qt一步步搭建TcpServer1——封装QTcpServer,QTcpSocket
- Qt一步步搭建TcpServer2——线程池
- 一步步搭建Ubuntu环境——修改启动选项
- 一步步搭建Ubuntu环境——关闭 Ubuntu 终端提示音
- 一步步搭建Ubuntu环境——修改Ubuntu窗口最小化、最大化、关闭按钮位置
- CentOS下是SVN服务的搭建、启动与关闭
- AG阅读总结5.1——实例启动与关闭
- AG阅读总结5.2——实例启动与关闭
- 启动与关闭Linux
- 启动与关闭服务器
- 启动与关闭
- 线程启动与关闭
- oracle启动与关闭
- solrcloud 启动与关闭
- ORACLE 启动与关闭
- UVA 11827 Maximum GCD gcd
- c语言初步经典题16---将一个文件的内容复制到另一个文件里面
- Hudson持续集成环境搭建
- 2017,共享单车的盛世危局
- Unexpected exception parsing XML tomcat启动时报错原因解析
- Qt一步步搭建TcpServer3——关闭与启动
- websocket示例
- 算法系列——Roman to Integer
- jQuery 方法
- Permissions
- Eclipse安装Spring-tool-suite
- 获取百度地
- 1.Unicode 简介
- JVM参数配置大全