基于QT的TCP通信服務的實現(xiàn)
一、結構
1.1 套接字
應用層通過傳輸層進行數據通信時,TCP和UDP會遇到同時為多個應用程序進程提供并發(fā)服務的問題。多個TCP連接或多個應用程序進程可能需要 通過同一個TCP協(xié)議端口傳輸數據。為了區(qū)別不同的應用程序進程和連接,許多計算機操作系統(tǒng)為應用程序與TCP/IP協(xié)議交互提供了稱為套接字 (Socket)的接口,區(qū)分不同應用程序進程間的網絡通信和連接。
實際上套接字做的事情就是為我們通信的兩端做一個連接
1.2 socket通信流程
對于TCP而言,socket通信的流程大概如下:

1.3 QTcpsocket
對于客戶端我們就使用的這個QTcpsocket類去請求服務器端,我們先看官方給的文檔可以知道:

使用該類需要#include <QTcpSocket>頭文件,并且該類是繼承QAbstractSocket類的,而且我們發(fā)現(xiàn)對于這個類沒有新增很多的函數,那么我們就應該去看它的父類,果不其然,父類中有很多的函數,我們后面進行TCP通信其實也主要是用到父類的一些函數,所以看一下文檔還是有必要的,對于每一個函數,你都能點進去看參數、以及描述

雖然QAbstractSocket有這么多的函數,但是我們實際上使用的函數就那么幾個,我們后面一一介紹,我們現(xiàn)在先來說說QT客戶端創(chuàng)建網絡連接的流程:
1.我們需要new一個QTcpSocket的對象,當然初始化只需要將當前的obj傳入即可,也就是this,然后給這個對象的readyRead創(chuàng)建一個凹槽做一些收到信號后的處理(比如將收到的數據顯示在某個地方)。
2.通過connectToHost函數去連接服務器,函數中傳入ip和port (ip要強轉為QHostAddress類)
3.通過調用waitForConnected函數,來判斷服務器是否連接超時,一般設置1000,表示的是1s未連接就超時,通過官方的文檔我們能知道如果返回的是true表示的是建立了連接,否則表示建立失敗或未建立連接
注意的是這里,只有使用waitForConnected()后,QTcpSocket才真正嘗試連接服務器,并返回是否連接的結果。
4.當我們的客戶端接收到readyRead的信號,我們就可以通過readAll()函數讀取服務器返回的信息,同樣的我們也可以通過write()函數向服務器發(fā)送信息
注意的是這里服務端讀到的數據是一個QByteArray類型的,我們寫入的數據可以是Qstring類型的,當然也可以是QByteArray
那么這就是客戶端的通信流程了
1.4 QTcpServer
對于服務器端我們需要用到QTcpServer類,同樣在官網的文檔我們能得到這個類的一些基本信息:

服務端的流程:
1.首先創(chuàng)建一個QTcpServer類,并初始化,然后給這個對象的 QTcpServer::newConnection() 建立一個凹槽,用于處理與客戶端建立連接后要做的一些事情,例如繼續(xù)為QTcpSocket::readyRead創(chuàng)建一個凹槽進行數據讀取操作、為QTcpSocket::disconnected創(chuàng)建凹槽用于對服務端失聯(lián)后的操作……
2.通過listen(QHostAddress::Any,port)函數監(jiān)聽所有的ip請求
3.當有新的客戶端連接服務器的時候,會自動觸發(fā)newConnection()信號函數,然后我們可以通過通過QTcpSocket * nextPendingConnection()成員函數來獲取當前連接上的新的客戶端類.然后再對QTcpSocket來進行信號槽綁定(這里可以寫一個客戶端的池但是我這里為了方便就只寫了一個客戶端連接的情況)
4.對于數據的讀取的話,由于我們這里只寫了一個客戶端的情況,那么就可以直接給這個QTcpSocket對象綁定和客戶端相同的事件就好
二、設計UI
我們直接使用QT Creator自帶的繪制工具,簡單繪制一下就好,界面不重要,重要是控件的objectName 經量設置合理一點,下面是我的設置:
2.1 客戶端UI

2.2 服務器端UI

三、核心代碼
對于客戶端來說:mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTcpSocket>
#include <QLabel>
#include <QHostAddress>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
setWindowTitle(QString("客戶端"));
ui->setupUi(this);
ui->port_2->setText("8899");
ui->ip->setText("127.0.0.1");
//剛開始 客戶端的 [斷開服務] 按鈕不可用
ui->disconnect->setDisabled(true);
//創(chuàng)建一個監(jiān)聽器服務對象
//Tcpserver
m_tcp=new QTcpSocket(this);
//客戶端 被動的接收服務器信號
connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
QByteArray array=m_tcp->readAll();
ui->record->append("服務端說:"+array);
});
//客戶端 斷開
connect(m_tcp,&QTcpSocket::disconnected,this,[=](){
m_status->setPixmap(QPixmap(":/a/tmp/disconnect.png").scaled(20,20));
ui->record->append("斷開鏈接服務器");
ui->connect->setDisabled(false);
ui->disconnect->setDisabled(true);
});
//操作狀態(tài)欄圖標
connect(m_tcp,&QTcpSocket::connected,this,[=](){
m_status->setPixmap(QPixmap(":/a/tmp/connect.png").scaled(20,20));
ui->record->append("已經鏈接成功服務器");
//操作按鈕互斥,鏈接成功了,自然鏈接按鈕不能用只有斷開按鈕可以用
ui->connect->setDisabled(true);
ui->disconnect->setDisabled(false);
});
//增加一點動畫效果 狀態(tài)欄的顏色變化
m_status =new QLabel;
//狀態(tài)欄的圖片添加
ui->statusbar->addWidget(new QLabel("鏈接狀態(tài):"));
ui->statusbar->addWidget(m_status);
//裝到菜單狀態(tài)欄
}
MainWindow::~MainWindow()
{
delete ui;
}
//點擊監(jiān)聽服務,自然而然去啟動監(jiān)聽服務
void MainWindow::on_send_clicked()
{
//把發(fā)出信息框的數據拿到,通過socket套接字發(fā)送出去
QString string=ui->sendmsg->toPlainText();
m_tcp->write(string.toUtf8());
ui->record->append("客戶端說:"+string);
ui->sendmsg->clear();
}
void MainWindow::on_connect_clicked()
{
//獲取 IP 端口 才能鏈接
QString ip=ui->ip->text();
unsigned short port=ui->port_2->text().toUShort();
qDebug("click_on_connect state = %d\n",m_tcp->state());
m_tcp->connectToHost(QHostAddress(ip),port);
if(m_tcp->waitForConnected(1000)) {
qDebug("connected !\n");
ui->record->clear();
}
else
qDebug("connect out time limit !\n");
}
void MainWindow::on_disconnect_clicked()
{
qDebug("loc1 state = %d\n",m_tcp->state());
m_tcp->disconnectFromHost();
qDebug("loc2 state = %d\n",m_tcp->state());
if(m_tcp->state() == QAbstractSocket::UnconnectedState
|| m_tcp->waitForDisconnected(1000))
qDebug("Disconnected!\n");
else
qDebug("Disconnect fail!\n");
m_tcp->close();
ui->connect->setDisabled(false);
ui->disconnect->setDisabled(false);
}
對于服務器來說:mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QLabel>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->port_2->setText("8899");
//創(chuàng)建一個監(jiān)聽器服務對象
//Tcpserver
m_s=new QTcpServer(this);
m_tcp = new QTcpSocket;
//啟動監(jiān)聽 通過點擊監(jiān)聽按鈕實現(xiàn),并且在按鈕轉到的槽函數實現(xiàn)監(jiān)聽
//上述完成監(jiān)聽,就等待用戶/客戶端的鏈接
connect(m_s,&QTcpServer::newConnection,this,[=](){
//狀態(tài)欄變色
m_status->setPixmap(QPixmap(":/a/tmp/connect.png").scaled(20,20));
//程序到此步驟證明有用戶鏈接,啟用socket通信傳輸并解析數據
//實例此次通信對象 nextPendingConnection得到一個可供通信的套接字對象
m_tcp=m_s->nextPendingConnection();
QString client_ip = m_tcp->peerAddress().toString().split("::ffff:")[1];
quint16 client_port = m_tcp->peerPort();
ui->record->append(tr("%1:%2 connected!\n").arg(client_ip).arg(client_port));
//進行對象處理,檢測傳輸的數據,利用connect對tcp套接字操作
connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
QByteArray array = m_tcp->readAll();
ui->record->append("客戶端說:"+array);
});
//客戶端斷開操作
connect(m_tcp,&QTcpSocket::disconnected,this,[=](){
QString client_ip = m_tcp->peerAddress().toString().split("::ffff:")[1];
quint16 client_port = m_tcp->peerPort();
ui->record->append(tr("%1:%2 Disconnected!\n").arg(client_ip).arg(client_port));
m_tcp->disconnectFromHost();
if(m_tcp->state() == QAbstractSocket::UnconnectedState
|| m_tcp->waitForDisconnected(1000))
qDebug("Disconnected!\n");
else
qDebug("Disconnect fail!\n");
m_status->setPixmap(QPixmap(":/a/tmp/disconnect.png").scaled(20,20));
});
});
//增加一點動畫效果 狀態(tài)欄的顏色變化
m_status =new QLabel;
//狀態(tài)欄的圖片添加
//m_status->setPixmap(QPixmap(":/a/tmp/connect.png").scaled(20,20));
ui->statusbar->addWidget(new QLabel("鏈接狀態(tài):"));
ui->statusbar->addWidget(m_status);
//裝到菜單狀態(tài)欄
}
MainWindow::~MainWindow()
{
delete ui;
}
//點擊監(jiān)聽服務,自然而然去啟動監(jiān)聽服務
void MainWindow::on_setlisten_clicked()
{ setWindowTitle("服務器");
//得到窗口 lineedit窗口的端口號
unsigned short port=ui->port_2->text().toShort();
//進行監(jiān)聽 ip 端口
m_s->listen(QHostAddress::Any,port);
ui->setlisten->setDisabled(true);
}
void MainWindow::on_send_clicked()
{
//把發(fā)出信息框的數據拿到,通過socket套接字發(fā)送出去
QString string=ui->sendmsg->toPlainText();
m_tcp->write(string.toUtf8());
ui->record->append("服務端說:"+string);
ui->sendmsg->clear();
}
四、效果圖


到此這篇關于基于QT的TCP通信服務的實現(xiàn)的文章就介紹到這了,更多相關QT TCP通信內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
visual?studio?2022?編譯出來的文件被刪除并監(jiān)視目錄中的文件變更(示例詳解)
這篇文章主要介紹了visual?studio?2022?編譯出來的文件被刪除?并監(jiān)視目錄中的文件變更,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08
C++類中的常數據成員與靜態(tài)數據成員之間的區(qū)別
常數據成員是指在類中定義的不能修改其值的一些數據成員,類似于我們以前學過的常變量,雖然是變量,也有自己的地址,但是一經賦初值,便不能再被修改2013-10-10
C++下如何將TensorFlow模型封裝成DLL供C#調用
這篇文章主要介紹了C++下如何將TensorFlow模型封裝成DLL供C#調用問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11

