里用Qt來(lái)簡(jiǎn)單設(shè)計(jì)實(shí)現(xiàn)一個(gè)場(chǎng)景,即:
(1)兩端:服務(wù)器QtServer和客戶端QtClient
(2)功能:服務(wù)端連接客戶端,兩者能夠互相發(fā)送消息,傳送文件,并且顯示文件傳送進(jìn)度。
環(huán)境:VS20013 + Qt5.11.2 + Qt設(shè)計(jì)師
先看效果:

一、基本概念
客戶端與服務(wù)器的基本概念不說(shuō)了,關(guān)于TCP通信的三次握手等等,在《計(jì)算機(jī)網(wǎng)絡(luò)》里都有詳細(xì)介紹。這里說(shuō)下兩者是如何建立起通信連接的。
(1)IP地址:首先服務(wù)器和每一個(gè)客戶端都有一個(gè)地址,即IP地址。(底層的MAC地址,不關(guān)心,因?yàn)門CP通信以及IP,是七層架構(gòu)里面的網(wǎng)絡(luò)層、傳輸層了,底層透明)。對(duì)于服務(wù)器來(lái)說(shuō),客戶端的數(shù)量及地址是未知的,除非建立了連接。但是對(duì)于客戶端來(lái)說(shuō),必須知道服務(wù)器的地址,因?yàn)閮烧咧g的連接是由客戶端主動(dòng)發(fā)起的。
(1)端口號(hào):軟件層面的端口號(hào),指的是 “應(yīng)用層的各種協(xié)議進(jìn)程與運(yùn)輸實(shí)體進(jìn)行層間交互的一種地址”。簡(jiǎn)而言之,每一個(gè)TCP連接都是一個(gè)進(jìn)程,操作系統(tǒng)需要為每個(gè)進(jìn)程分配一個(gè)協(xié)議端口(即每一個(gè)客戶端與服務(wù)端的連接,不是兩臺(tái)主機(jī)的連接,而是兩個(gè)端口的連接)。但一臺(tái)主機(jī)通常會(huì)有很多服務(wù),很多進(jìn)程,單靠一個(gè)IP地址不能標(biāo)識(shí)某個(gè)具體的進(jìn)程或者連接。所以用端口號(hào)來(lái)標(biāo)識(shí)訪問(wèn)的目標(biāo)服務(wù)器以及服務(wù)器的目標(biāo)服務(wù)類型。端口號(hào)也有分類,但這不是本文的重點(diǎn),詳見教材。
(3)TCP連接:總的來(lái)說(shuō),TCP的連接管理分為單個(gè)階段:建立連接 -> 數(shù)據(jù)傳送 -> 連接釋放。在(2)里說(shuō)到,每個(gè)TCP連接的是具體IP地址的主機(jī)的兩個(gè)端口,即TCP連接的兩個(gè)端點(diǎn)由IP地址和端口號(hào)組成,這即是套接字(socket)的概念:套接字socket = IP + 端口號(hào)。
因此,我們要通過(guò)通過(guò)套接字來(lái)建立服務(wù)端與客戶端的通信連接。
二、Qt相關(guān)類
QTcpSocket:提供套接字
QTcpServer:提供基于TCP的服務(wù)端,看官方文檔的解釋如下:
This class makes it possible to accept incoming TCP connections. You can specify the port or have QTcpServer pick one automatically. You can listen on a specific address or on all the machine’s addresses.
這個(gè)解釋里面提到兩點(diǎn):
(1)指定端口:即開通哪一個(gè)端口用于建立TCP連接;
(2)監(jiān)聽:監(jiān)聽(1)中指定的端口是否有連接的請(qǐng)求。
三、寫服務(wù)器和客戶端的具體流程
(1)服務(wù)器:
創(chuàng)建并初始化 QTcpServer 對(duì)象;
啟動(dòng)服務(wù)器監(jiān)聽,通過(guò)調(diào)用成員函數(shù) listen(QHostAddress::Any, 端口號(hào));
連接 QTcpServer 對(duì)象的 newConnection 信號(hào)槽,當(dāng)有客戶端鏈接時(shí),客戶端會(huì)發(fā)送 newConnection 信號(hào)給服務(wù)器,觸發(fā)槽函數(shù)接受鏈接(得到一個(gè)與客戶端通信的套接字 QTcpSocket);
QTcpsocket 對(duì)象調(diào)用成員函數(shù) write,發(fā)送數(shù)據(jù)給客戶端;
當(dāng)客戶端有數(shù)據(jù)發(fā)送來(lái),QTcpSocket 對(duì)象就會(huì)發(fā)送 readyRead 信號(hào),關(guān)聯(lián)槽函數(shù)讀取數(shù)據(jù);
連接 QTcpsocket 對(duì)象的 disconnected 信號(hào)槽,當(dāng)客戶端對(duì)象調(diào)用成員函數(shù) close,會(huì)觸發(fā) QTcpsocket 對(duì)象的 disconnected 信號(hào),進(jìn)而觸發(fā)槽函數(shù)進(jìn)行相應(yīng)處理。
(2)客戶端:
創(chuàng)建并初始化 QTcpSocket 對(duì)象;
QTcpSocket 調(diào)用 connectToHost(QHostAddress("IP"), 端口號(hào)),連接服務(wù)器IP和端口號(hào);
QTcpsocket 對(duì)象調(diào)用成員函數(shù) write,發(fā)送數(shù)據(jù)給服務(wù)器;
連接QTcpsocket 對(duì)象的 connected() 信號(hào)槽,當(dāng)客服端成功連接到服務(wù)器后觸發(fā) connected() 信號(hào);
連接QTcpsocket 對(duì)象的 readyread() 信號(hào)槽,當(dāng)客戶端接收到服務(wù)端發(fā)來(lái)數(shù)據(jù)時(shí)觸發(fā) readyread() 信號(hào);
連接 QTcpsocket 對(duì)象的 disconnected 信號(hào)槽,當(dāng)客戶端對(duì)象調(diào)用成員函數(shù) close,會(huì)觸發(fā) QTcpsocket 對(duì)象的 disconnected 信號(hào),進(jìn)而觸發(fā)槽函數(shù)進(jìn)行相應(yīng)處理。
四、UI設(shè)計(jì)
客戶端:

服務(wù)端:

五、服務(wù)端實(shí)現(xiàn)
我們先要在工程文件中加入network
QT += core gui network
下面我們來(lái)看看服務(wù)器程序步驟:
1、初始化 QTcpServer 對(duì)象
TCP_server = new QTcpServer();
2、啟動(dòng)服務(wù)器監(jiān)聽
TCP_server->listen(QHostAddress::Any,9988);//9988為端口號(hào)
3、連接 QTcpServer 對(duì)象的 newConnection 信號(hào)槽,當(dāng)有客戶端鏈接時(shí),客戶端會(huì)發(fā)送 newConnection 信號(hào)給服務(wù)器,觸發(fā)槽函數(shù)接受鏈接(得到一個(gè)與客戶端通信的套接字 QTcpSocket)
connect(TCP_server, SIGNAL(newConnection()), this, SLOT(slot_newconnect()));
// 在與 newConnection 信號(hào)連接的槽函數(shù)中,獲取與相應(yīng)客戶端通信的套接字
TCP_connectSocket = mServer->nextPendingConnection();
4、QTcpsocket 對(duì)象調(diào)用成員函數(shù) write,發(fā)送數(shù)據(jù)給客戶端
TCP_connectSocket->write(msg.toUtf8());
5、當(dāng)客戶端有數(shù)據(jù)發(fā)送來(lái),QTcpSocket 對(duì)象就會(huì)發(fā)送 readyRead 信號(hào),關(guān)聯(lián)槽函數(shù)讀取數(shù)據(jù)
connect(mSocket,SIGNAL(readyRead()),this,SLOT(slot_recvmessage()));
6、連接 QTcpsocket 對(duì)象的 disconnected 信號(hào)槽,當(dāng)客戶端對(duì)象調(diào)用成員函數(shù) close,會(huì)觸發(fā) QTcpsocket 對(duì)象的 disconnected 信號(hào),進(jìn)而觸發(fā)槽函數(shù)進(jìn)行相應(yīng)處理
connect(mSocket,SIGNAL(disconnected()),this,SLOT(slot_disconnect()));
TCP_Server.h
#ifndef TCP_SERVER_H
#define TCP_SERVER_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
namespace Ui {
class TCP_Server;
}
class TCP_Server : public QWidget
{
Q_OBJECT
public:
explicit TCP_Server(QWidget *parent = nullptr);
~TCP_Server();
private slots:
void slot_newconnect(); //建立新連接的槽
void slot_sendmessage(); //發(fā)送消息的槽
void slot_recvmessage(); //接收消息的槽
void slot_disconnect(); //取消連接的槽
private:
Ui::m_tcpServer *ui;
QTcpServer *TCP_server; //QTcpServer服務(wù)器
QTcpSocket *TCP_connectSocket; //與客戶端連接套接字
};
#endif // TCP_SERVER_H
TCP_Server.cpp
#include "tcp_server.h"
#include "ui_tcp_server.h"
#include <QMessageBox>
#include <QDateTime>
TCP_Server::TCP_Server(QWidget *parent) :
QWidget(parent),
ui(new Ui::TCP_Server)
{
ui->setupUi(this);
//初始化
TCP_server = new QTcpServer();
TCP_connectSocket = nullptr;
connect(ui->pushButton_send,SIGNAL(clicked()),this,SLOT(slot_sendmessage()));
//調(diào)用listen函數(shù)監(jiān)聽同時(shí)綁定IP和端口號(hào)
if(TCP_server->listen(QHostAddress::LocalHost,10000)) //判斷l(xiāng)isten是否成功,成功則繼續(xù)執(zhí)行,連接新接收信號(hào)槽
{
this->connect(TCP_server,SIGNAL(newConnection()),this,SLOT(slot_newconnect())); //將服務(wù)器的新連接信號(hào)連接到接收新連接的槽
}
else
{
QMessageBox::critical(this,"錯(cuò)誤","IP綁定錯(cuò)誤,請(qǐng)關(guān)閉其它服務(wù)端或更改綁定端口號(hào)");
}
}
TCP_Server::~TCP_Server()
{
delete ui;
}
//建立新連接的槽
void TCP_Server::slot_newconnect()
{
if(TCP_server->hasPendingConnections()) //查詢是否有新連接
{
TCP_connectSocket = TCP_server->nextPendingConnection(); //獲取與真實(shí)客戶端相連的客戶端套接字
ui->textBrowser->append("client login!"); //若有新連接,則提示
this->connect(TCP_connectSocket,SIGNAL(readyRead()),this,SLOT(slot_recvmessage())); //連接客戶端的套接字的有新消息信號(hào)到接收消息的槽
this->connect(TCP_connectSocket,SIGNAL(disconnected()),this,SLOT(slot_disconnect())); //連接客戶端的套接字取消連接信號(hào)到取消連接槽
}
}
//發(fā)送消息的槽
void TCP_Server::slot_sendmessage()
{
QString sendMessage = ui->lineEdit->text(); //獲取單行文本框內(nèi)要發(fā)送的內(nèi)容
if(TCP_connectSocket != nullptr && !sendMessage.isEmpty()) //確保有客戶端連接,并且發(fā)送內(nèi)容不為空
{
TCP_connectSocket->write(sendMessage.toLatin1()); //發(fā)送消息到客戶端
QString localDispalyMessage = "send to client: " + sendMessage \
+ QDateTime::currentDateTime().toString(" yyyy-M-dd hh:mm:ss") + tr("\n");
ui->textBrowser->append(localDispalyMessage); //將要發(fā)送的內(nèi)容顯示在listwidget
}
ui->lineEdit->clear();
}
//接收消息的槽
void TCP_Server::slot_recvmessage()
{
if(TCP_connectSocket != nullptr) //與客戶端連接的socket,不是nullptr,則說(shuō)明有客戶端存在
{
QByteArray array = TCP_connectSocket->readAll(); //接收消息
QHostAddress clientaddr = TCP_connectSocket->peerAddress(); //獲得IP
int port = TCP_connectSocket->peerPort(); //獲得端口號(hào)
QDateTime datetime = QDateTime::currentDateTime();
QString sendMessage = tr("recv from :") + clientaddr.toString() + tr(" : ") \
+ QString::number(port) + tr(" ") + datetime.toString("yyyy-M-dd hh:mm:ss") + tr("\n");
sendMessage += array;
ui->textBrowser->append(sendMessage); //將接收到的內(nèi)容加入到listwidget
}
}
//取消連接的槽
void TCP_Server::slot_disconnect()
{
if(TCP_connectSocket != nullptr)
{
ui->textBrowser->append("client logout!");
TCP_connectSocket->close(); //關(guān)閉客戶端
TCP_connectSocket->deleteLater();
}
}
六、客戶端實(shí)現(xiàn)
下面我們來(lái)看看客戶端程序步驟:
1、初始化 QTcpSocket 對(duì)象
TCP_server = new QTcpSocket();
2、QTcpSocket 調(diào)用 connectToHost(QHostAddress("IP"), 端口號(hào)),連接服務(wù)器IP和端口號(hào)
TCP_sendMesSocket->connectToHost("127.0.0.1",10000);
3、QTcpsocket 對(duì)象調(diào)用成員函數(shù) write,發(fā)送數(shù)據(jù)給服務(wù)器
//取發(fā)送信息編輯框內(nèi)容
QString msg = ui->sendEdit->toPlainText();
TCP_sendMesSocket->write(msg.toUtf8());//轉(zhuǎn)編碼
4、連接QTcpsocket 對(duì)象的 connected() 信號(hào)槽,當(dāng)客服端成功連接到服務(wù)器后觸發(fā) connected() 信號(hào)
connect(TCP_sendMesSocket,SIGNAL(connected()),this,SLOT(slot_connected()));
5、連接QTcpsocket 對(duì)象的 readyread() 信號(hào)槽,當(dāng)客戶端接收到服務(wù)端發(fā)來(lái)數(shù)據(jù)時(shí)觸發(fā) readyread() 信號(hào)
connect(TCP_sendMesSocket,SIGNAL(readyRead()),this,SLOT(slot_recvmessage()));
6、連接 QTcpsocket 對(duì)象的 disconnected 信號(hào)槽,當(dāng)客戶端對(duì)象調(diào)用成員函數(shù) close,會(huì)觸發(fā) QTcpsocket 對(duì)象的 disconnected 信號(hào),進(jìn)而觸發(fā)槽函數(shù)進(jìn)行相應(yīng)處理
connect(TCP_sendMesSocket, SIGNAL(disconnected()), this, SLOT(slot_disconnect()));
TCP_Client.h文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-505282.html
#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H
#include <QWidget>
#include <QTcpSocket>
namespace Ui {
class TCP_Client;
}
class TCP_Client : public QWidget
{
Q_OBJECT
public:
explicit TCP_Client(QWidget *parent = nullptr);
~TCP_Client();
//與按鈕交互,故函數(shù)都設(shè)置為槽函數(shù)
private slots:
void slot_connected(); //處理成功連接到服務(wù)器的槽
void slot_sendmessage(); //發(fā)送消息到服務(wù)器的槽
void slot_recvmessage(); //接收來(lái)自服務(wù)器的消息的槽
void slot_disconnect(); //取消與服務(wù)器連接的槽
private:
Ui::TCP_Client *ui;
bool isconnetion; //判斷是否連接到服務(wù)器的標(biāo)志位
QTcpSocket *TCP_sendMesSocket; //發(fā)送消息套接字
};
#endif // TCP_CLIENT_H
TCP_Client.cpp文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-505282.html
#include "tcp_client.h"
#include "ui_tcp_client.h"
#include <QHostAddress>
#include <QDateTime>
#include <QMessageBox>
TCP_Client::TCP_Client(QWidget *parent) :
QWidget(parent),
ui(new Ui::TCP_Client)
{
ui->setupUi(this);
/*** 初始化TCP ***/
this->setWindowTitle("TCP客戶端");
this->isconnetion = false;
//初始化sendMesSocket
this->TCP_sendMesSocket = new QTcpSocket();
//終止之前的連接,重置套接字
TCP_sendMesSocket->abort();
//給定IP和端口號(hào),連接服務(wù)器
this->TCP_sendMesSocket->connectToHost("127.0.0.1",10000); //QHostAddress::LocalHost等于127.0.0.1,所以兩者都可以互相替換
//成功連接服務(wù)器的connected()信號(hào)連接到slot_connected() (注意:不是connect()信號(hào))
connect(TCP_sendMesSocket,SIGNAL(connected()),this,SLOT(slot_connected()));
//發(fā)送按鈕的clicked()信號(hào)連接到slot_sendmessage()
connect(ui->pushButton_send,SIGNAL(clicked()),this,SLOT(slot_sendmessage()));
//有新數(shù)據(jù)到達(dá)時(shí)的readyread()信號(hào)連接到slot_recvmessage()
connect(TCP_sendMesSocket,SIGNAL(readyRead()),this,SLOT(slot_recvmessage()));
//與服務(wù)器斷開連接的disconnected()信號(hào)連接到slot_disconnect()
connect(TCP_sendMesSocket,SIGNAL(disconnected()),this,SLOT(slot_disconnect()));
}
TCP_Client::~TCP_Client()
{
delete ui;
}
//處理成功連接到服務(wù)器的槽
void TCP_Client::slot_connected()
{
this->isconnetion = true;
ui->textBrowser->append(tr("與服務(wù)器連接成功:") + QDateTime::currentDateTime().toString("yyyy-M-dd hh:mm:ss"));
}
//發(fā)送消息到服務(wù)器的槽
void TCP_Client::slot_sendmessage()
{
if(this->isconnetion)
{
QString sendMessage = ui->lineEdit->text(); //從單行文本框獲得要發(fā)送消息
if(!sendMessage.isEmpty())
{
//發(fā)送消息到服務(wù)器
this->TCP_sendMesSocket->write(sendMessage.toLatin1());
//本地顯示發(fā)送的消息
QString localDispalyMessage = tr("send to server: ") + sendMessage \
+ QDateTime::currentDateTime().toString(" yyyy-M-dd hh:mm:ss") + tr("\n");
ui->textBrowser->append(localDispalyMessage);
}
else
QMessageBox::warning(this,"錯(cuò)誤","消息不能為空!",QMessageBox::Ok);
}
else
QMessageBox::warning(this,"錯(cuò)誤","未連接到服務(wù)器!",QMessageBox::Ok);
ui->lineEdit->clear();
}
//接收來(lái)自服務(wù)器的消息的槽
void TCP_Client::slot_recvmessage()
{
//接收來(lái)自服務(wù)器的消息
QByteArray byteArray = this->TCP_sendMesSocket->readAll();
QString recvMessage = tr("recv from server: ") + byteArray + QDateTime::currentDateTime().toString(" yyyy-M-dd hh:mm:ss") + tr("\n");
ui->textBrowser->append(recvMessage);
}
//取消與服務(wù)器連接的槽
void TCP_Client::slot_disconnect()
{
QMessageBox::warning(this,"警告","與服務(wù)器的連接中斷",QMessageBox::Ok);
//關(guān)閉并隨后刪除socket
TCP_sendMesSocket->close();
TCP_sendMesSocket->deleteLater();
}
到了這里,關(guān)于Qt實(shí)現(xiàn)客戶端與服務(wù)器消息發(fā)送的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!