目录
第1章 概述
1.1 概述
1.2 QT socket通信的本质
1.3 QUdpSocket相关的信号
1.4 QTcpSocket相关的信号
第2章 UDP通信示例
服务端代码:
客户端代码:
第3章 TCP通信代码示例
服务器端代码:
客户端代码:
在Qt中,通过套接字(socket)实现网络通信主要使用的是QTcpSocket
和QUdpSocket
类。QTcpSocket
用于基于TCP协议的通信,而QUdpSocket
用于基于UDP协议的通信。
在Qt中,使用TCP/IP协议进行网络通信可以使用QTcpSocket
和QTcpServer
类。QTcpServer
用于创建服务器,监听连接请求并接受客户端连接,而QTcpSocket
用于创建客户端,并与服务器建立连接。
QUdpSocket和QTcpSocket这两个类,是QT库提供的,应用程序,无论是服务器还是客户端,socket通信本质上就是创建应用程序的类和类对象,并且在socket库提供的QUdpSocket和QTcpSocket类和类对象之间进行对象间通信,当socket对象中有数据时,自动通过信号+槽机制,自动回调应用程序注册到socket对象中的槽函数。这就是QT socket通信的本质。
QUdpSocket相关的信号
QUdpSocket
类在Qt中用于进行UDP网络通信。下面是QUdpSocket
中一些常用的信号:
readyRead()
:当有新的数据可读取时触发该信号。可以通过调用readDatagram()
方法读取数据。
stateChanged(QAbstractSocket::SocketState state)
:当QUdpSocket
的状态发生变化时触发该信号。状态可以是QAbstractSocket::UnconnectedState
(未连接状态)、QAbstractSocket::HostLookupState
(主机查找状态)、QAbstractSocket::ConnectingState
(连接中状态)、QAbstractSocket::ConnectedState
(已连接状态)等。
errorOccurred(QAbstractSocket::SocketError socketError)
:当QUdpSocket
发生错误时触发该信号。通过socketError
参数可以获取到具体的错误类型,例如QAbstractSocket::ConnectionRefusedError
(连接被拒绝错误)、QAbstractSocket::RemoteHostClosedError
(远程主机关闭连接错误)等。
这些信号可以通过连接到相关的槽函数来实现相应的逻辑处理。使用示例如下:
// 声明QUdpSocket对象
QUdpSocket *udpSocket;
// 连接信号和槽函数
connect(udpSocket, &QUdpSocket::readyRead, this, &MyClass::readReady);
connect(udpSocket, &QUdpSocket::stateChanged, this, &MyClass::socketStateChanged);
connect(udpSocket, &QUdpSocket::errorOccurred, this, &MyClass::socketErrorOccurred);
// 槽函数实现
void MyClass::readReady()
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress senderAddress;
quint16 senderPort;
udpSocket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
// 处理读取到的数据
}
void MyClass::socketStateChanged(QAbstractSocket::SocketState state)
{
// 处理状态变化
}
void MyClass::socketErrorOccurred(QAbstractSocket::SocketError socketError)
{
// 处理错误
}
通过连接这些信号和相应的槽函数,可以实现对QUdpSocket
对象的事件响应和处理。
QTcpSocket相关的信号
在Qt中,使用QTcpSocket类进行TCP网络通信。下面是QTcpSocket类的一些常见信号:
connected()
:当TCP套接字成功连接到远程主机时发出的信号。
disconnected()
:当TCP套接字与远程主机断开连接时发出的信号。
readyRead()
:当有新的数据可供读取时发出的信号。可以通过调用read()
方法读取数据。
bytesWritten(qint64 bytes)
:当已成功写入一定字节数的数据后发出的信号。可以通过bytes参数获取已写入的字节数。
hostFound()
:当QTcpSocket已成功查找到远程主机时发出的信号。
stateChanged(QAbstractSocket::SocketState state)
:当QTcpSocket的状态发生变化时发出的信号。状态可以是QAbstractSocket::UnconnectedState
(未连接状态)、QAbstractSocket::HostLookupState
(主机查找状态)、QAbstractSocket::ConnectingState
(连接中状态)、QAbstractSocket::ConnectedState
(已连接状态)等。
errorOccurred(QAbstractSocket::SocketError socketError)
:当QTcpSocket发生错误时发出的信号。通过socketError
参数可以获取到具体的错误类型,例如QAbstractSocket::ConnectionRefusedError
(连接被拒绝错误)、QAbstractSocket::RemoteHostClosedError
(远程主机关闭连接错误)等。
这些信号可以通过连接到相关的槽函数来实现相应的逻辑处理。使用示例如下:
// 声明QTcpSocket对象
QTcpSocket *tcpSocket;
// 连接信号和槽函数
connect(tcpSocket, &QTcpSocket::connected, this, &MyClass::socketConnected);
connect(tcpSocket, &QTcpSocket::disconnected, this, &MyClass::socketDisconnected);
connect(tcpSocket, &QTcpSocket::readyRead, this, &MyClass::socketReadyRead);
connect(tcpSocket, &QTcpSocket::bytesWritten, this, &MyClass::bytesWritten);
connect(tcpSocket, &QTcpSocket::hostFound, this, &MyClass::hostFound);
connect(tcpSocket, &QTcpSocket::stateChanged, this, &MyClass::socketStateChanged);
connect(tcpSocket, &QTcpSocket::errorOccurred, this, &MyClass::socketErrorOccurred);
// 槽函数实现
void MyClass::socketConnected()
{
// 处理连接成功事件
}
void MyClass::socketDisconnected()
{
// 处理断开连接事件
}
void MyClass::socketReadyRead()
{
// 处理有数据可读事件
}
void MyClass::bytesWritten(qint64 bytes)
{
// 处理数据写入事件
}
void MyClass::hostFound()
{
// 处理主机查找事件
}
void MyClass::socketStateChanged(QAbstractSocket::SocketState state)
{
// 处理状态变化
}
void MyClass::socketErrorOccurred(QAbstractSocket::SocketError socketError)
{
// 处理错误
}
通过连接这些信号和相应的槽函数,可以实现对QTcpSocket对象的事件响应和处理。
下面是一个简单的示例代码,展示了如何使用Qt进行UDP通信:
// Server.h
#ifndef SERVER_H
#define SERVER_H
#include
#include
class Server : public QObject
{
Q_OBJECT
public:
explicit Server(QObject *parent = nullptr);
public slots:
void readyRead();
private:
QUdpSocket *udpSocket;
};
// Server.cpp
#include "Server.h"
Server::Server(QObject *parent) : QObject(parent)
{
//创建udp socket对象
udpSocket = new QUdpSocket(this);
//由于是服务器,需要绑定socket的IP地址和端口号
udpSocket->bind(QHostAddress::Any, 12345);
//在socket对象和Server应用程序之间建立信号与回调函数机制
//当Server中有数据时,socket自动发送信号给Server应用程序,自动调用Server注册到Socket中的回调函数
connect(udpSocket, &QUdpSocket::readyRead, this, &Server::readyRead);
}
// Server的应用程序
void Server::readyRead()
{
while (udpSocket->hasPendingDatagrams()) {
//为读取/接收数据准备缓冲区
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
//为获取发送方信息准备对象
QHostAddress senderAddress;
quint16 senderPort;
//应用程序从socket中读取数据
udpSocket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
qDebug() << "Received data:" << datagram << "from" << senderAddress.toString() << ":" << senderPort;
// 在这里可以对接收到的数据进行处理
// 回复客户端: 即向socket写数据
udpSocket->writeDatagram("Server response", senderAddress, senderPort);
}
}
// Client.h
#ifndef CLIENT_H
#define CLIENT_H
#include
#include
class Client : public QObject
{
Q_OBJECT
public:
explicit Client(QObject *parent = nullptr);
public slots:
void readyRead();
private:
QUdpSocket *udpSocket;
};
// Client.cpp
#include "Client.h"
Client::Client(QObject *parent) : QObject(parent)
{
//创建udp socket对象
udpSocket = new QUdpSocket(this);
//当udpSocket中有数据时,TCP/IP协议栈自动会给该socket发送事件
//该socket对象自动会发送readyRead的信号,请求应用程序读取数据
//应用程序只需要把自己对象的槽函数挂接到新创建的socket对象的readyRead事件上即可
//有socket中有数据时,创建的socket对象自动会发送readyRead事件,这是QT的网络通信协作栈保证的
connect(udpSocket, &QUdpSocket::readyRead, this, &Client::readyRead);
}
//readyRead是应用程序挂接到socket上,读取socket数据的函数
//本质上是一个回调函数而已
void Client::readyRead()
{
//client检查socket中是否有数据
//如果有数据,则一直读数据,然后数据读完
//如果没有数据,则直接退出
while (udpSocket->hasPendingDatagrams())
{
//准备好数据接收buffer
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
//获取数据发送端的IP地址和端口号
QHostAddress senderAddress;
quint16 senderPort;
//从upd socket读取数据,并获得数据发送端的信息
udpSocket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
qDebug() << "Received data:" << datagram << "from" << senderAddress.toString() << ":" << senderPort;
// 在这里可以对接收到的数据进行处理
// .............................
// .............................
}
}
特别说明:
TCP的socket与UDP通信相似,都是应用程序类对象向socket类对象注册回调函数,当socket对象发生某种事件,socket对象通过先前注册的回调函数(信号与槽),自动调用相应的应用程序处理socket相关信号!!!
下面是一个简单的示例代码,展示了如何使用Qt进行TCP/IP通信:
// Server.h
#ifndef SERVER_H
#define SERVER_H
#include
#include
#include
class Server : public QObject
{
Q_OBJECT
public:
explicit Server(QObject *parent = nullptr);
public slots:
void newConnection();
void readyRead();
void disconnected();
private:
QTcpServer *tcpServer;
QTcpSocket *clientSocket;
};
// Server.cpp
#include "Server.h"
Server::Server(QObject *parent) : QObject(parent)
{
tcpServer = new QTcpServer(this);
if (!tcpServer->listen(QHostAddress::Any, 12345)) {
qDebug() << "Server could not start!";
} else {
qDebug() << "Server started!";
}
connect(tcpServer, &QTcpServer::newConnection, this, &Server::newConnection);
}
void Server::newConnection()
{
clientSocket = tcpServer->nextPendingConnection();
connect(clientSocket, &QTcpSocket::readyRead, this, &Server::readyRead);
connect(clientSocket, &QTcpSocket::disconnected, this, &Server::disconnected);
qDebug() << "Client connected!";
}
void Server::readyRead()
{
QByteArray data = clientSocket->readAll();
qDebug() << "Received data:" << data;
// 在这里可以对接收到的数据进行处理
// 回复客户端
clientSocket->write("Server response");
clientSocket->flush();
}
void Server::disconnected()
{
qDebug() << "Client disconnected!";
clientSocket->deleteLater();
}
// Client.h
#ifndef CLIENT_H
#define CLIENT_H
#include
#include
class Client : public QObject
{
Q_OBJECT
public:
explicit Client(QObject *parent = nullptr);
public slots:
void connected();
void disconnected();
void readyRead();
private:
QTcpSocket *tcpSocket;
};
// Client.cpp
#include "Client.h"
Client::Client(QObject *parent) : QObject(parent)
{
tcpSocket = new QTcpSocket(this);
connect(tcpSocket, &QTcpSocket::connected, this, &Client::connected);
connect(tcpSocket, &QTcpSocket::disconnected, this, &Client::disconnected);
connect(tcpSocket, &QTcpSocket::readyRead, this, &Client::readyRead);
tcpSocket->connectToHost("127.0.0.1", 12345);
}
void Client::connected()
{
qDebug() << "Connected to Server!";
tcpSocket->write("Hello from Client!");
tcpSocket->flush();
}
void Client::disconnected()
{
qDebug() << "Disconnected from Server!";
}
void Client::readyRead()
{
QByteArray data = tcpSocket->readAll();
qDebug() << "Received data:" << data;
// 在这里可以对接收到的数据进行处理
}
在上面的示例中,服务器监听在IP地址为"Any"(0.0.0.0)的所有网络接口上,端口号为12345的地址上。客户端连接到"127.0.0.1"(本地回环地址)上的12345端口。
当客户端连接到服务器后,服务器会打印"Client connected!"。当服务器接收到客户端发来的数据后,会在控制台打印该数据,然后回复客户端。客户端在接收到服务器的回复后,会在控制台打印该数据。
请确保在客户端连接之前启动服务器,以便能够接收到客户机的连接请求。
这只是一个简单的示例,你可以根据需要进行修改和扩展。请注意,在实际使用中,应该对错误和异