1. TCP协议数据传输
重点:在 .pro 项目文件中要加入 network 模块
cpp
QT += core gui network
-
QTcpServer 类: 服务器套接字 (该类只有在服务器端使用)
-
listen 方法, 用于设置端口
-
newConnection 信号,当新的客户端链接到服务器时触发
-
QTcpSocket nextPendingConnection 方法,获取新链接到服务器的对象,返回值 QTcpSocket 类型
-
-
QTcpSocket 类: 链接套接字
-
peerAddress 方法,获取新链接的地址
-
peerPort 方法,获取新链接的端口号
-
socket 介绍:
-
Socket是一种通信协议,是应用层和传输层之间的接口,用于在网络上实现进程之间的通信。Socket是指两个不同计算机之间的通信链路,包括IP地址和端口号。
-
通过Socket,应用程序可以连接到其他计算机或网络设备,发送或接收数据,并可以监听或等待其他计算机或网络设备的请求。所以,开发网络应用程序离不开socket
-
Socket可以分为TCP Socket和UDP Socket两种类型
第一步: 创建服务器对象,监听端口号
第二步: 创建客户端,链接到服务器
第三步: 当有新的客户端链接时,触发 TcpServer 的 newConnection 信号
第四步: 成功链接到服务器,触发 socket 的connected 信号
2.1 创建服务器端
cpp
Server::Server(QWidget *parent) : QWidget(parent), ui(new Ui::Server)
{
ui->setupUi(this);
this->setWindowTitle("服务器");
// 指定父对象,可以自动回收
this->tcpServer = new QTcpServer(this);
// 指定监听的端口
this->tcpServer->listen(QHostAddress::Any, 8888);
// 新链接信号 和 槽
connect(tcpServer, &QTcpServer::newConnection, [=](){
// 获取最新的链接客户端
this->serSocket = tcpServer->nextPendingConnection();
// 获取客户端的 ip 和 端口号
quint16 port = this->serSocket->peerPort();
QString ip = this->serSocket->peerAddress().toString();
// 将链接信息显示到显示区
QString str = QString("链接信息: %1:%2").arg(ip).arg(port);
ui->textEdit->setText(str);
});
}
2.2 创建客户端
cpp
#include <QTcpSocket>
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("客户端");
// 创建套接字
tcpSocket = new QTcpSocket(this);
// 链接到服务器按钮来处理链接
connect(ui->connectBtn, &QPushButton::clicked, [=](){
// 获取 ip 和 端口号
QString ip = ui->ipLine->text();
qint16 port = ui->portLine->text().toInt();
// 链接到服务器
tcpSocket->connectToHost(ip, port);
});
// 当成功链接到服务器后会自动触发 connected 信号
connect(tcpSocket, &QTcpSocket::connected, [=](){
// 在显示区提示成功信息
ui->showEdit->setText("成功链接到服务器");
});
}
2.3客户端向服务器发送信息
核心方法:
-
QTcpSocket::write(QByteArray content); 向 socket 中写入内容并发送
-
QByteArray QTcpSocket::readAll(); 读取 socket 接收到的内容
1.客户端向服务器发送信息
cpp
// 点击发送按钮
connect(ui->sendBtn, &QPushButton::clicked, [=](){
// 获取文本框中的内容
QString content = ui->sendLine->text();
// 将内容写入,并进行发送
tcpSocket->write(content.toUtf8().data());
});
2.服务器接收
注意事项: 服务器接收需要写在建立链接的信号槽内,因为要应用到 tcpSocket
cpp
connect(tcpServer, &QTcpServer::newConnection, [=](){
// 建立与客户端的链接套接字
tcpSocket = tcpServer->nextPendingConnection();
// 获取ip和端口
quint16 port = tcpSocket->peerPort();
QString ip = tcpSocket->peerAddress().toString();
QString str = QString("链接信息: %1:%2").arg(ip).arg(port);
ui->readEdit->setText(str);
// 链接成功之后
// 当服务器接收到客户端的数据后会触发 readyRead 信号
connect(tcpSocket, &QTcpSocket::readyRead, [=](){
// 从通信套接字中取出内容
QByteArray c = tcpSocket->readAll();
// 将内容追加到 TextEdit 中
ui->readEdit->append(c);
});
});
2.4 服务器向客户端发送信息
逻辑同上
1)服务器
-
点击发送按钮时获取用户输入内容
-
调用 tcpSocket 中的 write 方法将数据写入并发送
cpp
// 点击发送按钮时获取
connect(ui->btnSend, &QPushButton::clicked, [=](){
QString content = ui->writeEdit->toPlainText();
tcpSocket->write(content.toUtf8().data());
});
2)客户端
调用 tcpSocket 中 readAll 方法获取套接字中的数据显示到屏幕
cpp
connect(tcpSocket, &QTcpSocket::readyRead, [=](){
QByteArray content = tcpSocket->readAll();
ui->showEdit->append(content);
});
2.4 断开链接
-
tcpSocket->disconnectFromHost() // 断开与服务器的链接
-
tcpSocket->close() // 关闭套接字
cpp
connect(ui->disconnectBtn, &QPushButton::clicked, [=](){
tcpSocket->disconnectFromHost();
tcpSocket->close();
});
步骤小结:
- 建立 客户端与服务器的链接
① 使用 QTCPServer 创建了一个 server对象,再使用 server 对象监听端口
cpp
QTcpServer *server = new QTcpServer(this);
server->listen(QHostAddress::Any, 8888);
② 客户端通过 socket 建立与服务器端的链接
cpp
QTcpSocket *c_socket = new QTcpSocket(this);
c_socket->connectToHost(ip, port);
③ 服务器通过 QTcpServer::newConnection 信号来监听是否有新的客户端链接进来
cpp
connect(server, &QTcpServer::newConntion, [=](){
// 将链接信息写入到 屏幕
socket = server->nextPendingConnection();
QString ip = socket->peerAddress().toString();
int port = socket->peerPort();
});
④ 链接成功之后,也会触发客户端 socket::connected 信号
cpp
connect(socket, &QTcpSocket::connected, [](){
// 将链接成功信息写入到屏幕
....
});
2.5 tcp通信完整代码
客户端:
cpp
// client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <QWidget>
#include <QTcpSocket>
namespace Ui {
class Client;
}
class Client : public QWidget
{
Q_OBJECT
public:
explicit Client(QWidget *parent = nullptr);
~Client();
private:
Ui::Client *ui;
QTcpSocket *clientSocket;
};
#endif // CLIENT_H
// client.c
#include "client.h"
#include "ui_client.h"
Client::Client(QWidget *parent) :
QWidget(parent),
ui(new Ui::Client)
{
ui->setupUi(this);
this->setWindowTitle("客户端");
this->clientSocket = new QTcpSocket(this);
// 2. 点击"链接服务器"按钮,链接到端口号为8888的服务器
connect(ui->connBtn, &QPushButton::clicked, [this](){
// 获取用户填写的ip和端口号
QString ip = ui->ipLine->text();
int port = ui->portLine->text().toInt();
// 链接到服务器
this->clientSocket->connectToHost(ip, port);
});
// 4. 当客户端成功链接到服务器后,会触发 QTcpSocket::connected 信号
connect(this->clientSocket, &QTcpSocket::connected, [this](){
ui->textEdit->setText("成功链接到服务器");
});
// 5. 点击"发送"按钮,得到用户编写的信息,通过socket发送给客户端
// 发送数据就是将 数据写入socket
connect(ui->sendBtn, &QPushButton::clicked, [this](){
QString msg = ui->msgLine->text();
this->clientSocket->write(msg.toUtf8());
ui->msgLine->clear();
});
// 接收服务端发送的数据
connect(this->clientSocket, &QTcpSocket::readyRead, [this](){
QByteArray buf = this->clientSocket->readAll();
ui->textEdit->append(buf);
});
connect(ui->disconnBtn, &QPushButton::clicked, [this](){
this->clientSocket->disconnectFromHost();
});
}
Client::~Client()
{
delete ui;
}
服务端:
cpp
// server.h
#ifndef SERVER_H
#define SERVER_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
namespace Ui {
class Server;
}
class Server : public QWidget
{
Q_OBJECT
public:
explicit Server(QWidget *parent = nullptr);
~Server();
private:
Ui::Server *ui;
QTcpServer *server;
QTcpSocket *serverSocket;
};
#endif // SERVER_H
//server.c
#include "server.h"
#include "ui_server.h"
#include <QDebug>
#include "client.h"
Server::Server(QWidget *parent) :
QWidget(parent),
ui(new Ui::Server)
{
ui->setupUi(this);
this->setWindowTitle("服务端");
this->move(150, 100);
auto *c = new Client;
c->show();
c->move(700, 100);
// 1. 创建服务器端
this->server = new QTcpServer(this);
this->server->listen(QHostAddress::Any, 8888);
// 3. 当有客户端链接当前服务器时,就会触发 QTcpServer::newConnection 信号
connect(this->server, &QTcpServer::newConnection, [this](){
// nextPendingConnection: 用来获取连入服务器的客户端的socket对象信息
this->serverSocket = this->server->nextPendingConnection();
// 获取连入的客户端的ip地址和端口号
QString ip = this->serverSocket->peerAddress().toString();
quint16 port = this->serverSocket->peerPort();
ui->screen->setText(QString("%1:%2 -- 已成链接").arg(ip).arg(port));
//6. 每当客户端发送数据过来的时候就会触发 QTcpSocket::readyRead 信号
// 接收数据就是从socket中读取
connect(this->serverSocket, &QTcpSocket::readyRead, [this](){
QByteArray buf = this->serverSocket->readAll();
ui->screen->append(buf);
});
});
// 点击"发送"按钮,得到用户输入的信息,写入socket
connect(ui->sendBtn, &QPushButton::clicked, [this](){
QString msg = ui->msgEdit->toPlainText();
this->serverSocket->write(msg.toUtf8());
ui->msgEdit->clear();
});
// 点击"断开链接",断开服务端与客户端的链接
connect(ui->disconnBtn, &QPushButton::clicked, [this](){
this->serverSocket->disconnectFromHost();
});
}
Server::~Server()
{
delete ui;
}
主函数:
cpp
#include "server.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Server w;
w.show();
return a.exec();
}
2. UDP协议发送数据
QUdpSocket:
-
bind: 绑定端口号
-
writeDatagram: 写数据 (发送数据)
-
readDatagram: 读数据
-
readyRead (信号):接收到对方发送的数据时,自动触发 readyRead 信号
cpp
client::client(QWidget *parent) :
QWidget(parent),
ui(new Ui::client)
{
ui->setupUi(this);
this->setWindowTitle("端口号: 9999");
updSocket = new QUdpSocket(this);
// 绑定端口号
updSocket->bind(9999);
// 接收到对方发送的数据时,自动触发 readyRead 信号
connect(updSocket, &QUdpSocket::readyRead, this, &client::dealMsg);
// 发送数据
connect(ui->sendBtn, &QPushButton::clicked, [=](){
// 获取对方 ip 和 端口
QString ip = ui->ipLine->text();
qint16 port = ui->portLine->text().toInt();
// 获取发送内容
QString str = ui->sendLine->text();
// 发送
updSocket->writeDatagram(str.toUtf8(), QHostAddress(ip), port);
});
}
void client::dealMsg()
{
// 读取对方发送的内容
char buf[1024] = {0};
// 对方地址
QHostAddress ip;
// 对方端口
quint16 port;
// readDatagram 用来读取对方发送的数据
// 参数1: 保存对方发送的数据
// 参数2: 指定最多能保存多少数据
// 参数3: 保存对方的ip地址
// 参数4: 保存对方的端口号
// 返回值: 一共读取了多长的数据
qint64 len = updSocket->readDatagram(buf, 1024, &ip, &port);
if (len > 0)
{
QString str = QString("[%1:%2] -> %3").arg(ip.toString()).arg(port).arg(buf);
qDebug() << str;
ui->screenEdit->setText(str);
}
}
2.1 UDP通信完整代码
c1:
cpp
#ifndef C1_H
#define C1_H
#include <QMainWindow>
#include <QUdpSocket>
namespace Ui {
class C1;
}
class C1 : public QMainWindow
{
Q_OBJECT
public:
explicit C1(QWidget *parent = nullptr);
~C1();
private:
Ui::C1 *ui;
QUdpSocket *udp_c1;
};
#endif // C1_H
//c1.c
#include "c1.h"
#include "ui_c1.h"
#include "c2.h"
C1::C1(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::C1)
{
ui->setupUi(this);
this->setWindowTitle("8888端口");
this->move(200, 100);
C2 *c2 = new C2;
c2->show();
c2->move(700, 100);
// 1. 实例化udpSocket对象,并绑定端口号
this->udp_c1 = new QUdpSocket(this);
this->udp_c1->bind(8888);
// 2. 点击"发送"按钮时,得到对方的ip和port、发送的数据
// udp协议不需要建立链接,只要有对方的ip和port就能发数据
connect(ui->pushButton, &QPushButton::clicked, [this](){
QString ip = ui->ipLine->text();
QString port = ui->portLine->text();
QString msg = ui->msgLine->text();
this->udp_c1->writeDatagram(msg.toUtf8(), QHostAddress(ip), port.toInt());
});
connect(this->udp_c1, &QUdpSocket::readyRead, [this](){
char c[1024] = {0};
QHostAddress ip;
quint16 port;
this->udp_c1->readDatagram(c, 1024, &ip, &port);
ui->textEdit->append(QString("%1:%2 --> %3").arg(ip.toString()).arg(port).arg(c));
});
}
C1::~C1()
{
delete ui;
}
c2:
cpp
#ifndef C2_H
#define C2_H
#include <QWidget>
#include <QUdpSocket>
namespace Ui {
class C2;
}
class C2 : public QWidget
{
Q_OBJECT
public:
explicit C2(QWidget *parent = nullptr);
~C2();
private:
Ui::C2 *ui;
QUdpSocket *udp_c2;
};
#endif // C2_H
//c2.h
#include "c2.h"
#include "ui_c2.h"
C2::C2(QWidget *parent) :
QWidget(parent),
ui(new Ui::C2)
{
ui->setupUi(this);
this->setWindowTitle("9999端口");
this->udp_c2 = new QUdpSocket(this);
this->udp_c2->bind(9999);
// 一旦接收到信息就会触发 QUdpSocket::readyRead 信号
connect(this->udp_c2, &QUdpSocket::readyRead, [this](){
char c[1024] = {0};
QHostAddress ip;
quint16 port;
this->udp_c2->readDatagram(c, 1024, &ip, &port);
ui->textEdit->append(QString("%1:%2 --> %3").arg(ip.toString()).arg(port).arg(c));
});
connect(ui->pushButton, &QPushButton::clicked, [this](){
QString ip = ui->ipLine->text();
QString port = ui->portLine->text();
QString msg = ui->msgLine->text();
this->udp_c2->writeDatagram(msg.toUtf8(), QHostAddress(ip), port.toInt());
});
}
C2::~C2()
{
delete ui;
}
主函数:
cpp
include "c1.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
C1 w;
w.show();
return a.exec();
}