1. 程式人生 > >Qt下的Tcp協議練習——服務端

Qt下的Tcp協議練習——服務端

1.Tcp協議的原理

Tcp協議是一種可靠、面向連線、面向資料流的傳輸協議,適合資料的連續傳輸。

Tcp協議能夠確保一臺計算機發出的資料無差錯的傳輸給網路上的其他計算機,但是在傳輸資料前,必須建立連線(三次握手)。

2.傳輸過程

首先,服務端和客戶端通過三次握手建立連線。

然後,客戶端向服務端傳送一個請求,服務端處理這個請求,並向客戶端返回一個響應,這個過程會一直持續。

直到客戶端為服務端傳送一個檔案結束符,並關閉客戶端連線。

最後,服務端也關閉連線,結束執行。

3.例項

一個基於Tcp協議的聊天室Demo

建立TcpServer類,在建構函式中定義各個控制元件並完成初始化和佈局

    //初始化物件
    ContentListWidget = new QListWidget;
    PortLabel = new QLabel(tr("Port:"));
    PortLineEdit = new QLineEdit;
    CreateBtn = new QPushButton(tr("Create talkRoom"));
    mainLayout = new QGridLayout(this);

    //控制元件佈局
    mainLayout->addWidget(PortLabel,0,0);
    mainLayout->addWidget(PortLineEdit,0,1);
    mainLayout->addWidget(ContentListWidget,1,0,1,2);
    mainLayout->addWidget(CreateBtn,2,0,1,2);

畫面效果

建立TcpClientSocket類,用來接收各個客戶端傳送的資料,在建構函式中完成以下程式碼

    //在Qt中,QIODevice是所有I/O裝置的一個抽象類,QTcpSocket也被當做一個QIODevice
    //當接收到資料報的時候,發出訊號readyRead()
    connect(this,SIGNAL(readyRead()),this,SLOT(dataReceived()));

    //在連線斷開的時候,發出訊號disconnected()
    connect(this,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));

下面是上述兩個槽函式的實現

dataReceived():

   while(bytesAvailable()>0){//判斷等待讀取的傳入位元組數是否大於0
                              //(也就是判斷是否有沒讀取的有效資料)

        //儲存有效資料的長度
        int lenght = bytesAvailable();
        char buf[1024];

        //寫入到buf中,並轉換成QString型別
        read(buf,lenght);
        QString msg = buf;

        //傳送訊號,向客戶端廣播訊息
        emit updateClients(msg,lenght);
    }

slotDisconnected(): 

    //socketDescriptor()的返回值是socket的描述符,如果沒有連線則返回-1
    emit disconnected(this->socketDescriptor());

建立Server類,用來監聽指定埠的Tcp連線,在建構函式中完成以下程式碼

    //QHostAddress定義的技術特殊IP地址
    //Null:空地址
    //LocalHost:IPv4的本機地址
    //LocalHostIPv6:IPv6的本機地址
    //Broadcast:廣播地址
    //Any:IPv4的任意地址
    //AnyIPv6:IPv6的任意地址
    //監聽指定IP地址
    listen(QHostAddress::Any,port);

實現下面三個函式

過載 incomingConnection(qintptr socketDescriptor):

這裡格外注意:因為QT5.0版本之前incomingConnection函式的引數型別是int,但是5.0以後變成了qintptr,如果這兒寫錯了,即使是有新連線加入也不會觸發incomingConnection​​​​​​​函式。

    //建立一個新的TcpClientSocket與客戶端通訊
    TcpClientSocket *tcpClientSocket = new TcpClientSocket(this);

    //連線TcpClientSOcket的訊號
    connect(tcpClientSocket,SIGNAL(updateClients(QString,int)),this,SLOT(updateClients(QString,int)));
    connect(tcpClientSocket,SIGNAL(disconnected(int)),this,SLOT(slotDisconnected(int)));

    //將新建立的套接字的描述符指定為傳入的socketDescriptor
    tcpClientSocket->setSocketDescriptor(socketDescriptor);

    //將套接字加入套接字列表
    tcpClientSocketList.append(tcpClientSocket);

updateClients(QString msg, int length): 

    //傳送訊號,更新服務端視窗資訊
    emit updateServer(msg,length);

    //向套接字列表中的每一項傳送廣播
    for(int i = 0;i<tcpClientSocketList.count();i++){
        QTcpSocket *item = tcpClientSocketList.at(i);
        if(item->write(msg.toLatin1(),length)!=length){
            continue;
        }
    }

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;

在TcpServer類中寫入下列程式碼,

    //設定埠號
    port = 80;
    //將埠號顯示到控制元件上
    PortLineEdit->setText(QString::number(port));
    //點選按鈕建立服務
    connect(CreateBtn,SIGNAL(clicked(bool)),this,SLOT(slotCreateServer()));

實現下面的槽函式。

slotCreateServer():

    //建立一個Server物件
    server = new Server(this,port);
    //將Server類中的訊號和TcpServer中的槽函式連線起來
    connect(server,SIGNAL(updateServer(QString,int)),this,SLOT(updateServer(QString,int)));
    //將按鈕設定為不可點選狀態
    CreateBtn->setEnabled(false);

 updateServer(QString msg, int length):

//將資訊顯示到控制元件上
ContentListWidget->addItem(msg.left(length));

強調一個重點,因為專案中使用了網路相關的功能,所以要在工程檔案中新增下面的程式碼

QT       += network

以上就是這個簡略的服務端的實現和主要程式碼,因為目前還是處在一個模仿和學習的階段,所以主要還是借鑑一些書上的案例和網上的Demo,後續會根據自己的掌握程度做一些更加能夠體現自身理解的練習專案。