HPSocket -- 文檔和下載地址 :https://www.oschina.net/p/hp-socket
HPSocket -- Github :https://github.com/ldcsaa/HP-Socket
源碼文檔案例可在交流群下載:QQ-75375912
* 本篇講解的是TcpServer的使用 *
環境Ubuntu 18.04.3 編輯工具IDE QT5.13.2
在群里可以下載相關源碼目錄如下
我們需要的是Linux目錄下的東西 將里面的東西都復制到Linux系統內
鑒於我們是簡單利用TCP 開發Server 我們只需要
include,lib不過良心的作者為我們做了個install.sh和compile.sh
安裝如下:
include和lib會安裝在 /usr/local/include 和 /usr/local/lib64 兩個目錄下
打開Qt Creator 新建一個項目 Qt Widgets Application 命名為HpSocketDemo
更改Pro文件如下:
新建類MyTcpListener 包含頭文件hpsocket/HPSocket.h 繼承CTcpServerListener
將MyTcpListener變成單例類,再加上自己實現的事件回調類ISocketView 代碼如下:
#ifndef ISOCKETVIEW_H #define ISOCKETVIEW_H //此處僅當教程使用 具體回傳參數由各位根據需求來定 class ISocketView { public: virtual void onPrepareListen()=0; virtual void onAccept()=0; virtual void onHandShake() = 0; virtual void onReceive()=0; virtual void onSend()=0; virtual void onShutdown()=0; virtual void onClose()=0; }; #endif // ISOCKETVIEW_H
Server執行順序如下:
執行startServer() 導致 OnPrepareListen觸發 可進行發送數據的行為
外部Client接入連接 導致onAccept觸發 可進行接受數據的行為
外部Client發送數據 導致onReceive觸發
外部Client斷開連接導致onClose觸發
執行stopServer() 導致onClose onShutdown 觸發
#ifndef MYTCPLISTENER_H #define MYTCPLISTENER_H #include "hpsocket/HPSocket.h" #include "ISocketView.h" #include <QMutex> #include <QMutexLocker> //這個類的需求請自行添加 class MySocketBuffer { public: static MySocketBuffer* newBuffer(){ return new MySocketBuffer();} MySocketBuffer(){} }; class MyTcpListener:CTcpServerListener { public: static MyTcpListener* getInstance(); CTcpPackServerPtr mServer; //1.開啟服務器 bool startServer(); //2.關閉服務器 bool stopServer(); void registerView(ISocketView* view); void unRegisterView(); public: //監聽成功時觸發 EnHandleResult OnPrepareListen(ITcpServer *pSender, SOCKET soListen); //接受到連接時觸發 EnHandleResult OnAccept(ITcpServer *pSender, CONNID dwConnID, UINT_PTR soClient); //握手成功時觸發 EnHandleResult OnHandShake(ITcpServer *pSender, CONNID dwConnID); //收到數據時觸發 EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength); //發送數據成功觸發 EnHandleResult OnSend(ITcpServer *pSender, CONNID dwConnID, const BYTE *pData, int iLength); //服務器關閉時觸發 EnHandleResult OnShutdown(ITcpServer *pSender); //關閉某個連接時觸發 EnHandleResult OnClose(ITcpServer* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode); private: ISocketView *mView; private: //單例實現 MyTcpListener(); MyTcpListener(const MyTcpListener&); MyTcpListener& operator=(const MyTcpListener&); virtual ~MyTcpListener(); class CGarbo // { public: CGarbo(){} ~CGarbo() { if (MyTcpListener::mServerListener) { delete MyTcpListener::mServerListener; } } }; static CGarbo mCGarbo; private: static MyTcpListener* mServerListener; static QMutex mMutex; }; #endif // MYTCPLISTENER_H
#include "MyTcpListener.h" MyTcpListener* MyTcpListener::mServerListener=nullptr; QMutex MyTcpListener::mMutex; MyTcpListener::MyTcpListener() :mServer(this)//要想監聽生效 這個必須傳入監聽者對象 ,mView(nullptr) { } MyTcpListener::~MyTcpListener() { } MyTcpListener *MyTcpListener::getInstance() { MyTcpListener* tmp = mServerListener; if (tmp == nullptr) { QMutexLocker locker(&mMutex); tmp = mServerListener; if (tmp == nullptr) { tmp = new MyTcpListener(); mServerListener = tmp; } } return mServerListener; } bool MyTcpListener::startServer() { //mServer ->SetSocketBufferSize(2048); return mServer->Start("0,0,0,0",12000); } bool MyTcpListener::stopServer() { return mServer->Stop(); } void MyTcpListener::registerView(ISocketView *view) { mView = view; } void MyTcpListener::unRegisterView() { mView=nullptr; } EnHandleResult MyTcpListener::OnPrepareListen(ITcpServer *pSender, SOCKET soListen) { //回調觸發 注意不能直接改UI Qt建議采用信號槽 if(mView!=nullptr) { mView->onPrepareListen(); } //獲取監聽的ip port信息 TCHAR lpszAddress[30]; int iAddressLen; USHORT unPort; pSender->GetListenAddress(lpszAddress,iAddressLen,unPort); return HR_OK; } EnHandleResult MyTcpListener::OnAccept(ITcpServer *pSender, CONNID dwConnID, UINT_PTR soClient) { //回調觸發 注意不能直接改UI Qt建議采用信號槽 if(mView!=nullptr) { mView->onAccept(); } //為每一個新接入的連接 附加一個對象 因為我們是根據ID來區分連接的 pSender->SetConnectionExtra(dwConnID,MySocketBuffer::newBuffer()); return HR_OK; } EnHandleResult MyTcpListener::OnHandShake(ITcpServer *pSender, CONNID dwConnID) { //回調觸發 注意不能直接改UI Qt建議采用信號槽 if(mView!=nullptr) { mView->onHandShake(); } return HR_OK; } EnHandleResult MyTcpListener::OnReceive(ITcpServer *pSender, CONNID dwConnID, const BYTE *pData, int iLength) { //特別注意這個回調觸發的方式 當同一個ID觸發時是按照順序觸發 //如果是不同ID觸發則是線程觸發 所以在這里不能做阻塞處理 //特別是不能加鎖做同步處理 //如果想高速處理數據 請采用線程池方式 //回調觸發 注意不能直接改UI Qt建議采用信號槽 if(mView!=nullptr) { mView->onReceive(); } //這里就可以采用Accept時附加的對象做處理,比如定義方法 數據緩存等 MySocketBuffer * pSocketBuffer=nullptr; pSender->GetConnectionExtra(dwConnID,(PVOID*)pSocketBuffer); if(pSocketBuffer!=nullptr) { //可在此對pSocketBuffer做操作 } return HR_OK; } EnHandleResult MyTcpListener::OnSend(ITcpServer *pSender, CONNID dwConnID, const BYTE *pData, int iLength) { //回調觸發 注意不能直接改UI Qt建議采用信號槽 if(mView!=nullptr) { mView->onSend(); } return HR_OK; } EnHandleResult MyTcpListener::OnShutdown(ITcpServer *pSender) { //回調觸發 注意不能直接改UI Qt建議采用信號槽 if(mView!=nullptr) { mView->onShutdown(); } return HR_OK; } EnHandleResult MyTcpListener::OnClose(ITcpServer *pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode) { //回調觸發 注意不能直接改UI Qt建議采用信號槽 if(mView!=nullptr) { mView->onClose(); } //當一個連接關閉時 我們需要自己將onAccept 時 new出來的對象delete掉 MySocketBuffer * pSocketBuffer=nullptr; pSender->GetConnectionExtra(dwConnID,(PVOID*)pSocketBuffer); if(pSocketBuffer!=nullptr){ delete pSocketBuffer;} return HR_OK; }
一個簡單的TCP服務器就搭建好了