天天看點

QT5.7 tcp網絡通信 基于TCP的網絡聊天室程式

  傳輸控制協定(Transmission Control Protocol,TCP)是一種可靠、面向連接配接、面向資料流的傳輸協定,許多高層應用協定(包括HTTP、FTP等)都是以它為基礎的,TCP協定非常适合資料的連續傳輸。

QT5.7 tcp網絡通信 基于TCP的網絡聊天室程式

TCP協定工作原理

如圖10.9所示,TCP協定能夠為應用程式提供可靠的通信連接配接,使一台計算機發出的位元組流無差錯地送達網絡上的其他計算機。是以,對可靠性要求高的資料通信系統往往使用TCP協定傳輸資料,但在正式收發資料前通信雙方必須首先建立連接配接。

QT5.7 tcp網絡通信 基于TCP的網絡聊天室程式

TCP程式設計模型

下面介紹一下基于TCP協定的經典程式設計模型,程式編寫的流程如圖10.10所示。

QT5.7 tcp網絡通信 基于TCP的網絡聊天室程式

首先啟動伺服器,一段時間後啟動用戶端,它與此伺服器經過三次握手後建立連接配接。此後的一段時間内,用戶端向伺服器發送一個請求,伺服器處理這個請求,并為用戶端發回一個響應。這個過程一直持續下去,直到用戶端為伺服器發一個檔案結束符,并關閉用戶端連接配接,接着伺服器也關閉伺服器端的連接配接,結束運作或等待一個新的用戶端連接配接。

Qt 中通過QTcpSocket類和QTcpServer類實作TCP協定的程式設計。下面介紹如何實作一個基于TCP協定的網絡聊天室應用,它同樣也由用戶端和伺服器兩部分組成。

TCP伺服器程式設計執行個體

以下内容是伺服器端的程式設計,建立工程TcpServer.pro。

(1)頭檔案“tcpserver.h”中聲明了需要的各種控件,TcpServer繼承自QDialog,實作了伺服器端的對話框顯示與控制。其具體代碼如下:

#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <QDialog>
#include <QDialog>
#include <QListWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include "server.h"
class tcpserver : public QDialog
{
    Q_OBJECT

public:
    tcpserver(QWidget *parent = 0);
    ~tcpserver();

private:
    QListWidget *ContentListWidget;
    QLabel *PortLabel;
    QLineEdit *PortLineEdit;
    QPushButton *CreateBtn;
    QGridLayout *mainLayout;
    int port;
    Server *server;
public slots:
    void slotCreateServer();
    void updateServer(QString,int);
};

#endif // TCPSERVER_H      

(2)在源檔案“tcpserver.cpp”中,TcpServer類的構造函數主要實作窗體各控件的建立、布局等,其具體代碼如下:

#include "tcpserver.h"
#pragma execution_character_set("utf-8")   //中文輸入
tcpserver::tcpserver(QWidget *parent)
    : QDialog(parent)
{
    setWindowTitle((tr("TCP Server")));

    ContentListWidget=new QListWidget;

    PortLabel=new QLabel(tr("端口:"));
    PortLineEdit=new QLineEdit;

    CreateBtn=new QPushButton(tr("建立聊天室:"));

    mainLayout=new QGridLayout(this);
    mainLayout->addWidget(ContentListWidget,0,0,1,2);
    mainLayout->addWidget(PortLabel,1,0);
    mainLayout->addWidget(PortLineEdit,1,1);
    mainLayout->addWidget(CreateBtn,2,0,1,2);

    port=8010;
    PortLineEdit->setText(QString::number(port));

    connect(CreateBtn,SIGNAL(clicked()),this,SLOT(slotCreateServer()));
}

tcpserver::~tcpserver()
{
}

void tcpserver::slotCreateServer()
{
    server=new Server(this,port);   //建立一個server對象
    //将server對象的updateServer()信号與相應的槽函數進行連接配接
    connect(server,SIGNAL(updateServer(QString,int)),this,SLOT(updateServer(QString,int)));
    CreateBtn->setEnabled(false);
}

void tcpserver::updateServer(QString msg, int length)
{
    ContentListWidget->addItem(msg.left(length));
}      
QT5.7 tcp網絡通信 基于TCP的網絡聊天室程式

以上完成了伺服器的界面設計,下面将詳細完成聊天室的伺服器端功能。

(1)在工程檔案“TcpServer.pro”中添加如下語句:

QT+=network

(2)在工程“TcpServer.pro”中添加C++類檔案“tcpclientsocketh”及

“tcpclientsocket.cpp”,TopClientSocket繼承自QTcpSocket,建立一個TCP套接字,以便在伺服器端實作與用戶端程式的通信。

頭檔案“tcpclientsocket.h”的具體代碼如下:

#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H
#pragma execution_character_set("utf-8")   //中文輸入
#include <QTcpSocket>
#include <QObject>

class TcpClientSocket : public QTcpSocket
{
    Q_OBJECT    //添加宏(Q_OBJECT)是為了實作信号與槽的通信
public:
    TcpClientSocket(QObject *parent=0);
signals:
    void updateClients(QString,int);
    void disconnected(int);

protected slots:
    void dataReceived();
    void slotDisconnected();
};

#endif // TCPCLIENTSOCKET_H      

(3)在源檔案“tcpclientsocket.cpp”中,的具體代碼如下:

#include "tcpclientsocket.h"
#pragma execution_character_set("utf-8")   //中文輸入


TcpClientSocket::TcpClientSocket(QObject *parent)
{
    //readyRead()是QIODevice的signal,由QTcpSocket繼承而來。QIODevice是所有輸入\輸出裝置
    //的一個抽象類,其中定義了基本的接口,在QT中,QTcpSocket也被看做一個QIODevice,readyRead()信号在有資料到來時發出
    connect(this,SIGNAL(readyRead()),this,SLOT(dataReceived()));
    //disconnected()信号在有資料到來時發出
    connect(this,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));
}

void TcpClientSocket::dataReceived()
{
    while(bytesAvailable()>0)       //從套接字中将有效資料取出,然後發出updateClients()信号.
    {                               //updateClients()信号是通知伺服器向聊天室内的所有成員廣播資訊
        int length=bytesAvailable();
        char buf[1024];
        read(buf,length);

        QString msg=buf;
        emit updateClients(msg,length);
    }
}

void TcpClientSocket::slotDisconnected()
{
    emit disconnected(this->socketDescriptor());
}      

(4)在工程“TcpServer.pro”中添加C++類檔案“server.h”及“server.cpp”,Server繼承自QTcpServer,實作一個TCP協定的伺服器。利用QTcpServer,開發者可以監聽到指定端口的TCP連接配接。其具體代碼如下:

#ifndef SERVER_H
#define SERVER_H

#include <QTcpServer>
#include <QObject>
#include "tcpclientsocket.h"


class Server : public QTcpServer
{
    Q_OBJECT   //添加宏(Q_OBJECT)是為了實作信号與槽
public:
    Server(QObject *parent=0,int port=0);
    QList<TcpClientSocket *> TcpClientSocketList;  //用來儲存與每一個用戶端連接配接的TcpClientSocket

signals:
    void updateServer(QString,int);
public slots:
    void updateClients(QString,int);
    void slotDisconnected(int);
protected:
    void incomingConnection(int socketDescriptor);
};

#endif // SERVER_H      
#include "server.h"
#pragma execution_character_set("utf-8")   //中文輸入

//QHostAddress::LocaHost 表示IPV4的本機位址127.0.0.1;
//QHostAddress::LocaHostIPv6b表示IPv6的任意位址
//QHostAddress::Broadcast表示廣播位址255.255.255.255
//QHostAddress::Any表示IPv4的任意位址0.0.0.0
//QHostAddress::AnyIPv6表示IPv6的任意位址
Server::Server(QObject *parent,int port)        //QObject *parent/QWidget *parent,當指定了parent後,Qt就會介入,在合适的時候調用對應的delete操作。
    :QTcpServer(parent)
{
    listen(QHostAddress::Any,port);     //在指定的端口對任意位址進行監聽
}


void Server::incomingConnection(int socketDescriptor)
{
    //建立一個新的TcpClientSocket與用戶端通信
    TcpClientSocket *tcpClientSocket=new TcpClientSocket(this);
    //連接配接TcpClientSocket的updateClients信号
    connect(tcpClientSocket,SIGNAL(updateClients(QString,int)),this,SLOT(updateClients(QString,int)));
    //連接配接TcpClientSocket的disconnected信号
    connect(tcpClientSocket,SIGNAL(disconnected(int)),this,SLOT(slotDisconnected(int)));
    //将新建立的TcpClientSocket的套接字描述符指定為參數sockedDescriptor
    tcpClientSocket->setSocketDescriptor(socketDescriptor);
    //将tcpClientSocket加入用戶端套接字清單以便管理
    TcpClientSocketList.append(tcpClientSocket);
}
//updateClients()函數将任意用戶端發來的資訊進行廣播,保證聊天室所有客戶均能看到其他人的發言
void Server::updateClients(QString msg, int length)
{
    emit updateServer(msg,length);  //發出updateServer信号,用來通知伺服器對話框更新相應的顯示狀态
    for(int i=0;i<TcpClientSocketList.count();i++)      //實作資訊的廣播,tcpClientSockedList中儲存
    {                                                   //了所有與伺服器相連的TcpClientSocket對象
        QTcpSocket *item=TcpClientSocketList.at(i);
        if(item->write(msg.toLatin1(),length)!=length)
        {
            continue;
        }
    }
}
//slotDisconnected()函數實作從tcpClientSocketList清單中将斷開連接配接的
//TcpClientSocketList對象删除的功能
void Server::slotDisconnected(int descriptor)
{
    for(int i=0;i<TcpClientSocketList.count();i++)
    {
        QTcpSocket *item=TcpClientSocketList.at(i);
        if(item->socketDescriptor()==descriptor)
        {
            TcpClientSocketList.removeAt(i);
            return;
        }
    }
    return;
}      
QT5.7 tcp網絡通信 基于TCP的網絡聊天室程式

此時運作伺服器端工程“TcpServer.pro”編譯通過。單擊“建立聊天室”

便開通了一個TCP聊天室的伺服器,如圖10.12所示。

QT5.7 tcp網絡通信 基于TCP的網絡聊天室程式