Qt网络编程——TCP
- 1. 概念
- 2. 服务器
- 3. 客户端
- 4. TCP服务器和客户端互传文件
- 5. 资源下载
1. 概念
- TCP(Transmission Control Protocol, 传输控制协议) 是一个用于数据传输的底层网络协议。
- TCP 是一个面向数据流和连接的可靠的传输协议。
2. 服务器
- tcpserver.pro
复制代码
1
2QT += network
- server.h
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#ifndef SERVER_H #define SERVER_H #include <QDialog> class QTcpServer; namespace Ui { class Server; } class Server : public QDialog { Q_OBJECT public: explicit Server(QWidget *parent = 0); ~Server(); private: Ui::Server *ui; QTcpServer * tcpServer; private slots: void sendMessage(); }; #endif // SERVER_H
- server.cpp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50#include "server.h" #include "ui_server.h" #include <QtNetwork> #include <QDebug> Server::Server(QWidget *parent) : QDialog(parent), ui(new Ui::Server) { ui->setupUi(this); tcpServer = new QTcpServer(this); //监听连接 //使用了IPv4的本地主机地址,等价于 QHostAddress("127.0.0.1") if(!tcpServer->listen(QHostAddress::LocalHost,6666)) { qDebug()<<tcpServer->errorString(); close(); } connect(tcpServer,&QTcpServer::newConnection,this,&Server::sendMessage); } Server::~Server() { delete ui; } void Server::sendMessage() { //用于暂存要发送的数据 QByteArray block; QDataStream out(&block,QIODevice::WriteOnly); //设置数据流的版本,客户端和服务器端使用的版本要相同 out.setVersion(QDataStream::Qt_5_6); out<<(quint16)0; out<<tr("HelloTCP!"); //跳转到数据块开头,然后将获取的大小信息填写到前面空留的两个字节处 out.device()->seek(0); out<<(quint16)(block.size()-sizeof(quint16)); //获取已经建立的连接的套接字 QTcpSocket * clientConnection = tcpServer->nextPendingConnection(); connect(clientConnection,&QTcpSocket::disconnected, clientConnection,&QTcpSocket::deleteLater); //发送数据 clientConnection->write(block); clientConnection->disconnectFromHost(); ui->label->setText(tr("发送数据成功")); }
3. 客户端
- tcpclient.pro
复制代码
1
2QT += network
- client.h
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37#ifndef CLIENT_H #define CLIENT_H #include <QDialog> #include <QAbstractSocket> class QTcpSocket; namespace Ui { class Client; } class Client : public QDialog { Q_OBJECT public: explicit Client(QWidget *parent = 0); ~Client(); private: Ui::Client *ui; QTcpSocket * tcpSocket; QString message; //存放数据的大小信息 quint16 blockSize; private slots: void newConnect(); void readMessage(); void displayError(QAbstractSocket::SocketError); void on_pushButtonConnect_clicked(); }; #endif // CLIENT_H
- client.cpp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65#include "client.h" #include "ui_client.h" #include <QtNetwork> Client::Client(QWidget *parent) : QDialog(parent), ui(new Ui::Client) { ui->setupUi(this); tcpSocket = new QTcpSocket(this); //这里关联了两个信号到自定义槽上,当有可读的数据时,会发射readyRead()信号 //当发生错误时,会发射error()信号 connect(tcpSocket,&QTcpSocket::readyRead,this,&Client::readMessage); connect(tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(displayError(QAbstractSocket::SocketError))); } Client::~Client() { delete ui; } void Client::newConnect() { blockSize = 0; tcpSocket->abort(); tcpSocket->connectToHost(ui->lineEditHost->text(), ui->lineEditPort->text().toInt()); } void Client::readMessage() { QDataStream in(tcpSocket); //设置数据流版本,这里要和服务器端相同 in.setVersion(QDataStream::Qt_5_6); //如果是刚开始接收数据 if(blockSize == 0) { //判断接受的数据是否大于两字节,也就是文件的大小信息所占的空间 //如果是则保存到blockSize变量中,否则直接返回,继续接收数据 if(tcpSocket->bytesAvailable()<(int)sizeof(quint16)) return; in>>blockSize; } //如果没有得到全部的数据,则返回,继续接收数据 if(tcpSocket->bytesAvailable()<blockSize) return; //将接受到的数据放到变量中 in>>message; //显示接收到的数据 ui->labelMessage->setText(message); } void Client::displayError(QAbstractSocket::SocketError) { qDebug()<<tcpSocket->errorString(); } void Client::on_pushButtonConnect_clicked() { newConnect(); }
4. TCP服务器和客户端互传文件
- client.h
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46#ifndef CLIENT_H #define CLIENT_H #include <QDialog> #include <QAbstractSocket> class QTcpSocket; class QFile; namespace Ui { class Client; } class Client : public QDialog { Q_OBJECT public: explicit Client(QWidget *parent = 0); ~Client(); private: Ui::Client *ui; QTcpSocket *tcpClient; QFile *localFile; // 要发送的文件 qint64 totalBytes; // 发送数据的总大小 qint64 bytesWritten; // 已经发送数据大小 qint64 bytesToWrite; // 剩余数据大小 qint64 payloadSize; // 每次发送数据的大小 QString fileName; // 保存文件路径 QByteArray outBlock; // 数据缓冲区,即存放每次要发送的数据块 private slots: void openFile(); void send(); void startTransfer(); void updateClientProgress(qint64); void displayError(QAbstractSocket::SocketError); void on_openButton_clicked(); void on_sendButton_clicked(); }; #endif // CLIENT_H
- client.cpp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138#include "client.h" #include "ui_client.h" #include <QtNetwork> #include <QFileDialog> Client::Client(QWidget *parent) : QDialog(parent), ui(new Ui::Client) { ui->setupUi(this); payloadSize = 64*1024; // 64KB totalBytes = 0; bytesWritten = 0; bytesToWrite = 0; tcpClient = new QTcpSocket(this); // 当连接服务器成功时,发出connected()信号,开始传送文件 connect(tcpClient, SIGNAL(connected()), this, SLOT(startTransfer())); connect(tcpClient, SIGNAL(bytesWritten(qint64)), this, SLOT(updateClientProgress(qint64))); connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError))); ui->sendButton->setEnabled(false); } Client::~Client() { delete ui; } void Client::openFile() { fileName = QFileDialog::getOpenFileName(this); if (!fileName.isEmpty()) { ui->sendButton->setEnabled(true); ui->clientStatusLabel->setText(tr("打开文件 %1 成功!").arg(fileName)); } } void Client::send() { ui->sendButton->setEnabled(false); // 初始化已发送字节为0 bytesWritten = 0; ui->clientStatusLabel->setText(tr("连接中…")); tcpClient->connectToHost(ui->hostLineEdit->text(), ui->portLineEdit->text().toInt()); } void Client::startTransfer() { localFile = new QFile(fileName); if (!localFile->open(QFile::ReadOnly)) { qDebug() << "client: open file error!"; return; } // 获取文件大小 totalBytes = localFile->size(); QDataStream sendOut(&outBlock, QIODevice::WriteOnly); sendOut.setVersion(QDataStream::Qt_4_0); QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1); // 保留总大小信息空间、文件名大小信息空间,然后输入文件名 sendOut << qint64(0) << qint64(0) << currentFileName; // 这里的总大小是总大小信息、文件名大小信息、文件名和实际文件大小的总和 totalBytes += outBlock.size(); sendOut.device()->seek(0); // 返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间 sendOut << totalBytes << qint64((outBlock.size() - sizeof(qint64)*2)); // 发送完文件头结构后剩余数据的大小 bytesToWrite = totalBytes - tcpClient->write(outBlock); ui->clientStatusLabel->setText(tr("已连接")); outBlock.resize(0); } void Client::updateClientProgress(qint64 numBytes) { // 已经发送数据的大小 bytesWritten += (int)numBytes; // 如果已经发送了数据 if (bytesToWrite > 0) { // 每次发送payloadSize大小的数据,这里设置为64KB,如果剩余的数据不足64KB, // 就发送剩余数据的大小 outBlock = localFile->read(qMin(bytesToWrite, payloadSize)); // 发送完一次数据后还剩余数据的大小 bytesToWrite -= (int)tcpClient->write(outBlock); // 清空发送缓冲区 outBlock.resize(0); } else { // 如果没有发送任何数据,则关闭文件 localFile->close(); } // 更新进度条 ui->clientProgressBar->setMaximum(totalBytes); ui->clientProgressBar->setValue(bytesWritten); // 如果发送完毕 if(bytesWritten == totalBytes) { ui->clientStatusLabel->setText(tr("传送文件 %1 成功").arg(fileName)); localFile->close(); tcpClient->close(); } } void Client::displayError(QAbstractSocket::SocketError) { qDebug() << tcpClient->errorString(); tcpClient->close(); ui->clientProgressBar->reset(); ui->clientStatusLabel->setText(tr("客户端就绪")); ui->sendButton->setEnabled(true); } // 打开按钮 void Client::on_openButton_clicked() { ui->clientProgressBar->reset(); ui->clientStatusLabel->setText(tr("状态:等待打开文件!")); openFile(); } // 发送按钮 void Client::on_sendButton_clicked() { send(); }
- server.h
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46#ifndef SERVER_H #define SERVER_H #include <QDialog> #include <QAbstractSocket> #include <QTcpServer> class QTcpSocket; class QFile; namespace Ui { class Server; } class Server : public QDialog { Q_OBJECT public: explicit Server(QWidget *parent = 0); ~Server(); private: Ui::Server *ui; QTcpServer tcpServer; QTcpSocket *tcpServerConnection; qint64 totalBytes; // 存放总大小信息 qint64 bytesReceived; // 已收到数据的大小 qint64 fileNameSize; // 文件名的大小信息 QString fileName; // 存放文件名 QFile *localFile; // 本地文件 QByteArray inBlock; // 数据缓冲区 private slots: void start(); void acceptConnection(); void updateServerProgress(); void displayError(QAbstractSocket::SocketError socketError); void on_startButton_clicked(); }; #endif // SERVER_H
- server.cpp
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113#include "server.h" #include "ui_server.h" #include <QtNetwork> Server::Server(QWidget *parent) : QDialog(parent), ui(new Ui::Server) { ui->setupUi(this); connect(&tcpServer, SIGNAL(newConnection()), this, SLOT(acceptConnection())); } Server::~Server() { delete ui; } void Server::start() { if (!tcpServer.listen(QHostAddress::LocalHost, 6666)) { qDebug() << tcpServer.errorString(); close(); return; } ui->startButton->setEnabled(false); totalBytes = 0; bytesReceived = 0; fileNameSize = 0; ui->serverStatusLabel->setText(tr("监听")); ui->serverProgressBar->reset(); } void Server::acceptConnection() { tcpServerConnection = tcpServer.nextPendingConnection(); connect(tcpServerConnection, SIGNAL(readyRead()), this, SLOT(updateServerProgress())); connect(tcpServerConnection, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError))); ui->serverStatusLabel->setText(tr("接受连接")); // 关闭服务器,不再进行监听 tcpServer.close(); } void Server::updateServerProgress() { QDataStream in(tcpServerConnection); in.setVersion(QDataStream::Qt_4_0); // 如果接收到的数据小于16个字节,保存到来的文件头结构 if (bytesReceived <= sizeof(qint64)*2) { if((tcpServerConnection->bytesAvailable() >= sizeof(qint64)*2) && (fileNameSize == 0)) { // 接收数据总大小信息和文件名大小信息 in >> totalBytes >> fileNameSize; bytesReceived += sizeof(qint64) * 2; } if((tcpServerConnection->bytesAvailable() >= fileNameSize) && (fileNameSize != 0)) { // 接收文件名,并建立文件 in >> fileName; ui->serverStatusLabel->setText(tr("接收文件 %1 …") .arg(fileName)); bytesReceived += fileNameSize; localFile = new QFile(fileName); if (!localFile->open(QFile::WriteOnly)) { qDebug() << "server: open file error!"; return; } } else { return; } } // 如果接收的数据小于总数据,那么写入文件 if (bytesReceived < totalBytes) { bytesReceived += tcpServerConnection->bytesAvailable(); inBlock = tcpServerConnection->readAll(); localFile->write(inBlock); inBlock.resize(0); } ui->serverProgressBar->setMaximum(totalBytes); ui->serverProgressBar->setValue(bytesReceived); // 接收数据完成时 if (bytesReceived == totalBytes) { tcpServerConnection->close(); localFile->close(); ui->startButton->setEnabled(true); ui->serverStatusLabel->setText(tr("接收文件 %1 成功!") .arg(fileName)); } } void Server::displayError(QAbstractSocket::SocketError socketError) { qDebug() << tcpServerConnection->errorString(); tcpServerConnection->close(); ui->serverProgressBar->reset(); ui->serverStatusLabel->setText(tr("服务端就绪")); ui->startButton->setEnabled(true); } // 开始监听按钮 void Server::on_startButton_clicked() { start(); }
5. 资源下载
https://download.csdn.net/download/weixin_38566632/85187690
最后
以上就是平常钢笔最近收集整理的关于Qt网络编程——TCP1. 概念2. 服务器3. 客户端4. TCP服务器和客户端互传文件5. 资源下载的全部内容,更多相关Qt网络编程——TCP1.内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复