1. 程式人生 > >Qt:Qt實現Winsock網路程式設計—TCP服務端和客戶端通訊(多執行緒)

Qt:Qt實現Winsock網路程式設計—TCP服務端和客戶端通訊(多執行緒)

Qt實現Winsock網路程式設計—TCP服務端和客戶端通訊(多執行緒)

前言

感覺Winsock網路程式設計的api其實和Linux下網路程式設計的api非常像,其實和其他程式語言的網路程式設計都差不太多。博主用Qt實現的,當然不想用黑視窗唄,有介面可以看到,由於GUI程式設計的話 一般UI程序不能阻塞,肯定需要多線來實現,在等待使用者連線的時候accept,和客戶端通訊 等待訊息的時候recv這些都是阻塞的 都需要在後臺程序中。博主使用的是Qt5.9.7

介面效果

先看下效果再說
在這裡插入圖片描述
在這裡插入圖片描述

Winsock面向連線的介面

Winsock初始化和釋放

WSAStartup、WSACleanup

服務端:

socket() -> bind() -> listen() -> accept() -> send() / recv() -> closesocket()

客戶端:

socket() -> connect() -> send() / recv() -> closesocket()

這些函式都在msdn上有,這裡就不過多的闡述

程式碼展示

服務端 將與客戶度建立連線的 過程放到單獨的執行緒中進行處理了,這個執行緒 做的事就是等待客戶端連線,客戶端連線後 單獨再開一個執行緒專門與客戶度進行通訊,這樣就不會導致介面無響應了。這裡執行緒和執行緒間的通訊是使用的Qt中的訊號槽機制。

處理客戶端連線請求程式碼

void SocketThread::run()
{
        sockaddr_in clientAddr;
        int size = sizeof(clientAddr);
        while(!isInterruptionRequested()){
            //每次接受新客戶端將之前的地址資訊清0
            memset(&clientAddr,0,sizeof(clientAddr));
            //等待新客戶端連線 阻塞函式,結束執行緒 使用requestInterruption打斷執行緒並沒有用,只能使用terminate 終止執行緒
            SOCKET client = ::accept(mListen,(sockaddr*)&clientAddr,&size);
            char* clientIp = inet_ntoa(clientAddr.sin_addr);
            int clientPort = ntohs(clientAddr.sin_port);
            QTime time = QTime::currentTime();
            QString str = time.toString("hh:mm:ss");
            QString msg = QString("%1 [%2:%3] connect success").arg(str).arg(clientIp).arg(clientPort);
            //新客戶端連線,通知 UI 更新介面
            emit isMsg(msg);
            //開啟新執行緒和客戶端進行通訊
            MsgThread* msgThread = new MsgThread(client,clientAddr,parent);
            msgThread->start();

            connect(msgThread,&MsgThread::isMsg,this,[=](QString msg){
                //轉發訊息給 UI程序,UI進行介面更新
                emit isMsg(msg);
            });
            connect(this,&SocketThread::isClose,this,[=](){
                msgThread->terminate();
                msgThread->quit();
                delete msgThread;
            });
        }
}

服務端和客戶端通訊的程式碼

void MsgThread::run(){
        //inet_addr點分十進位制轉網路ip地址 ,inet_ntoa網路轉點分十進位制
        char* clientIp = inet_ntoa(mAddr.sin_addr);
        int clientPort = ntohs(mAddr.sin_port);
        while(true){
            memset(resp,0,1024);
            char buf[1024] = {0};
            //阻塞等待 接受資訊
            int ret = recv(mClient,buf,1024,0);
            QTime time = QTime::currentTime();
            QString str = time.toString("hh:mm:ss");
            if(ret == 0){//連線斷開
                emit isMsg(QString("%1 [%2:%3] is closed!!!").arg(str).arg(clientIp).arg(clientPort));
                break;
            }
            QString msg = QString("%1 [%2:%3]:%4").arg(str).arg(clientIp).arg(clientPort).arg(buf);
            //接受到訊息,通知UI 介面更新
            emit isMsg(msg);

            //給使用者進行響應訊息,小寫變大寫
            strcpy(resp,QString(buf).toUpper().toUtf8().data());
            qDebug() << "給客戶端傳送訊息:" << resp;
            send(mClient,resp,strlen(resp)+1,0);
        }
}

完整程式碼

完整專案程式碼可以點這裡進行下載,或者github下載最新程式碼