Введение в сетевое взаимодействие в QT

Введение в класс QTcpSocket

Класс QTcpSocket предоставляет сокет TCP. TCP (протокол управления передачей) — это надежный, ориентированный на поток и ориентированный на соединение транспортный протокол. Он особенно подходит для непрерывной передачи данных. QTcpSocket — это подкласс QAbstractSocket, позволяющий устанавливать TCP-соединения и передавать потоки данных. Подробности смотрите в документации QAbstractSocket. (Примечание: сокеты TCP не могут быть открыты в режиме QIODevice::Unbuffered.)

заголовок:#include
qmake: QT += network
Наследуется: QAbstractSocket
Наследуется: QSctpSocket и QSslSocket

Публичные функции: (есть много наследований, ниже будут представлены свои собственные и те, что в QAbstractSocket)

QTcpSocket(QObject *parent = nullptr)
//创建状态为UnconnectedState的QTcpSocket对象。
virtual  ~QTcpSocket()
//销毁套接字,必要时关闭连接。
QAbstractSocket(QAbstractSocket::SocketType socketType, QObject *parent)
//创建类型为SocketType的新抽象套接字。
virtual  ~QAbstractSocket()
//销毁套接字
void  abort()
//中止当前连接并重置套接字。与disconnectFromHost()不同,此函数会立即关闭套接字,丢弃写缓冲区中的任何挂起数据。
bool  bind(const QHostAddress &address, quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform)
//使用绑定模式绑定到端口端口上的地址。
//对于UDP套接字,绑定后,每当UDP数据报到达指定的地址和端口时,就会发出信号QUdpSocket::readyRead()。因此,此函数对于写入UDP服务器很有用。
//对于TCP套接字,此函数可用于指定用于传出连接的接口,这在多个网络接口的情况下很有用。
//默认情况下,套接字是使用DefaultForPlatform绑定模式绑定的。如果未指定端口,则会选择一个随机端口。
//成功后,函数返回true,套接字进入BoundState;否则返回false。此功能在Qt 5.0中引入。
bool  bind(quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform)
//这是一个重载函数。
//绑定到QHostAddress:任何端口上的端口,使用BindMode模式。
//默认情况下,套接字是使用DefaultForPlatform绑定模式绑定的。如果未指定端口,则会选择一个随机端口。
virtual void connectToHost(const QString &hostName, quint16 port, QIODevice::OpenMode openMode = ReadWrite, QAbstractSocket::NetworkLayerProtocolprotocol = AnyIPProtocol)
//尝试连接到给定端口上的hostName。协议参数可用于指定要使用的网络协议(例如IPv4或IPv6)。
//套接字在给定的openMode中打开,首先进入HostLookupState,然后执行hostName的主机名查找。如果查找成功,将发出hostFound(),并且QAbstractSocket将进入ConnectingState。然后,它尝试连接到查找返回的一个或多个地址。最后,如果建立了连接,QAbstractSocket将进入ConnectedState并发出connected()。
//在任何时候,套接字都可以发出error()来发出发生错误的信号。
//hostName可以是字符串形式的IP地址(例如“43.195.83.32”),也可以是主机名(例如“example.com”)。只有在需要时,x才会进行查找。端口按本机字节顺序排列。
virtual void connectToHost(const QHostAddress &address, quint16 port, QIODevice::OpenMode openMode = ReadWrite)
//这是一个重载函数。尝试连接到端口端口上的地址。
virtual void disconnectFromHost()
//试图关闭套接字。如果有挂起的数据等待写入,QAbstractSocket将进入ClosingState并等待,直到所有数据都已写入。最终,它将进入UnconnectedState并发出disconnected()信号。
QAbstractSocket::SocketError error() const
//返回上次发生的错误类型。
bool flush()
//此函数在不阻塞的情况下,尽可能多地从内部写入缓冲区写入底层网络套接字。如果写入了任何数据,此函数将返回true;否则返回false。
//如果需要QAbstractSocket立即开始发送缓冲数据,请调用此函数。成功写入的字节数取决于操作系统。在大多数情况下,您不需要调用此函数,因为一旦控制返回到事件循环,QAbstractSocket将自动开始发送数据。如果没有事件循环,请改为调用waitForBytesWritten()。
bool isValid() const
//如果套接字有效并且可以使用,则返回true;否则返回false。
//注意:在进行读取和写入之前,套接字的状态必须为ConnectedState。
QHostAddress  localAddress() const
//返回本地套接字的主机地址(如果可用);否则返回QHostAddress::Null。
//这通常是主机的主IP地址,但对于连接到本地主机,可以是QHostAddress::LocalHost(127.0.0.1)。
quint16 localPort() const
//返回本地套接字的主机端口号(按本机字节顺序)(如果可用);否则返回0。
QAbstractSocket::PauseModes pauseMode() const
//返回此套接字的暂停模式。此功能在Qt 5.0中引入。
QHostAddress peerAddress() const
//如果套接字处于ConnectedState,则返回连接的对等方的地址;否则返回QHostAddress::Null。
QString peerName() const
//返回由connectToHost()指定的对等方的名称,如果尚未调用connectToHost(),则返回空的QString。
quint16 peerPort() const
//如果套接字处于ConnectedState,则返回已连接对等端的端口;否则返回0。
QNetworkProxy proxy() const
//返回此套接字的网络代理。默认情况下,使用QNetworkProxy::DefaultProxy,这意味着此套接字将查询应用程序的默认代理设置。此功能在Qt 4.1中引入。
qint64 readBufferSize() const
//返回内部读取缓冲区的大小。这限制了客户端在调用read()或readAll()之前可以接收的数据量。
//读取缓冲区大小为0(默认值)意味着缓冲区没有大小限制,确保不会丢失任何数据。
virtual void resume()
//在套接字上继续数据传输。只有在套接字设置为在收到通知时暂停并且收到通知后,才应使用此方法。当前支持的唯一通知是QSslSocket::sslErrors()。如果套接字未暂停,则调用此方法会导致未定义的行为。此功能在Qt 5.0中引入。
void setPauseMode(QAbstractSocket::PauseModes pauseMode)
//控制是否在收到通知时暂停。pauseMode参数指定套接字应该暂停的条件。当前支持的唯一通知是QSslSocket::sslErrors()。如果设置为PauseOnslErrors,套接字上的数据传输将暂停,并且需要通过调用resume()再次显式启用。默认情况下,此选项设置为PauseNever。在连接到服务器之前必须调用此选项,否则将导致未定义的行为。此功能在Qt 5.0中引入。
void setProxy(const QNetworkProxy &networkProxy)
//将此套接字的显式网络代理设置为networkProxy。
virtual void setReadBufferSize(qint64 size)
//将QAbstractSocket的内部读取缓冲区的大小设置为size字节。
//如果缓冲区大小被限制为某个特定的大小,那么QAbstractSocket不会缓冲超过这个大小的数据。例外情况下,缓冲区大小为0意味着读取缓冲区是不受限制的,并且缓冲所有传入数据。这是默认设置。
//如果您只在特定时间点读取数据(例如,在实时流应用程序中),或者如果您想保护套接字不接收过多数据,这可能最终导致应用程序内存不足,则此选项非常有用。
//只有QTcpSocket使用QAbstractSocket的内部缓冲区;QUdpSocket根本不使用任何缓冲,而是依赖于操作系统提供的隐式缓冲。因此,在QUdpSocket上调用此函数没有任何效果。
virtual bool setSocketDescriptor(qintptr socketDescriptor, QAbstractSocket::SocketState socketState = ConnectedState, QIODevice::OpenMode openMode = ReadWrite)
//使用本机套接字描述符socketDescriptor初始化QAbstractSocket。如果socketDescriptor被接受为有效的套接字描述符,则返回true;否则返回false。套接字以openMode指定的模式打开,并进入socketState指定的套接字状态。读取和写入缓冲区被清除,丢弃任何挂起的数据。
//注意:不可能使用相同的本机套接字描述符初始化两个抽象套接字。
virtual void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
//将给定option设置为由value描述的值。
//注意:在Windows运行时上,连接套接字之前必须设置QAbstractSocket::KeepAliveOption。
//此功能在Qt 4.6中引入。
virtual qintptr socketDescriptor() const
//如果QAbstractSocket对象的本地套接字描述符可用,则返回该描述符;否则返回-1。
//如果套接字使用QNetworkProxy,则返回的描述符可能无法用于本机套接字函数。
//当QAbstractSocket处于UnconnectedState时,套接字描述符不可用。
virtual QVariant socketOption(QAbstractSocket::SocketOption option)
//返回option选项的值。此功能在Qt 4.6中引入。
QAbstractSocket::SocketType socketType() const
//返回套接字类型(TCP、UDP或其他)。
QAbstractSocket::SocketState state() const
//返回套接字的状态。
virtual bool waitForConnected(int msecs = 30000)
//等待,直到套接字连接,最长可达毫秒。如果已建立连接,则此函数将返回true;否则返回false。在返回false的情况下,可以调用error()来确定错误的原因。
virtual bool waitForDisconnected(int msecs = 30000)
//等待,直到套接字断开连接,最长可达毫秒。如果连接已断开,此函数将返回true;否则返回false。在返回false的情况下,可以调用error()来确定错误的原因。

Статические общедоступные члены:
QMetaObject
содержит так называемые метаданные QObject, то есть некоторую информацию описания информации QObject: помимо информации о типе, он также содержит информацию о сигналах и слотах, уникальную для QT.

Класс QTcpSocket использует

// 定义套接字
QTcpServer *socket = new QTcpSocket();

//取消已有的连接
socket->abort();

//连接服务器
socket->connectToHost(IP, Port);

//QTcpSocket类里读取和发送数据用的函数都在socket缓冲区中保存
//读取socket缓冲区数据
QString buffer = socket->readAll();
//写入socket缓冲区里数据
socket->write("将数据写入socket缓存区");
//可以以以下两个信号来添加槽函数处理操作
//连接成功和连接断开会触发 connected() 和 disconnected() 信号:
//当socket接收缓冲区有新数据到来时,会发出readRead()信号

//断开连接
socket->disconnectFromHost();

//关闭套接字
socket.close();

Введение в класс QTcpServer
Класс QTcpServer предоставляет сервер на основе TCP. Этот класс может принимать входящие соединения TCP. Вы можете указать порт или позволить QTcpServer выбрать порт автоматически. Вы можете прослушивать определенный адрес или адреса всех машин. Вызовите listen(), чтобы сервер прослушивал входящие соединения. Каждый раз, когда клиент подключается к серверу, генерируется сигнал newConnection(). Вызовите nextPendingConnection(), чтобы принять ожидающее соединение как подключенный QTcpSocket. Эта функция возвращает указатель на QTcpSocket в QAbstractSocket::ConnectedState, который вы можете использовать для связи с клиентом. При возникновении ошибки serverError() вернет тип ошибки, а errorString() можно вызвать, чтобы получить удобочитаемое описание того, что произошло. При прослушивании подключений адрес и порт, который прослушивает сервер, доступны как serverAddress() и serverPort(). Вызов close() заставит QTcpServer перестать прослушивать входящие соединения. Хотя QTcpServer в первую очередь предназначен для использования с циклом обработки событий, его также можно использовать без него. В этом случае вы должны использовать функцию waitForNewConnection(), которая блокируется до тех пор, пока соединение не станет доступным или не истечет время ожидания.

Заголовок:#include
qmake: QT += сеть
Наследует: QObject
Унаследовано от: QSctpServer

Публичные функции

QTcpServer(QObject *parent = nullptr)
//构造一个QTcpServer对象。
virtual ~QTcpServer()
//销毁QTcpServer对象。
void close()
//关闭服务器。服务器将不再侦听传入连接。
QString errorString() const
//返回上次发生的错误的可读说明。
virtual bool hasPendingConnections() const
//如果服务器有挂起的连接,则返回true;否则返回false。
bool isListening() const
//如果服务器当前正在侦听传入连接,则返回true;否则返回false。
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
//告诉服务器侦听地址地址和端口端口上的传入连接。如果端口为0,则自动选择端口。如果地址为QHostAddress::Any,则服务器将侦听所有网络接口。成功时返回true;否则返回false。
int maxPendingConnections() const
//返回挂起的可接受连接的最大数目。默认值为30。
virtual QTcpSocket *nextPendingConnection()
//将下一个挂起的连接作为已连接的QTcpSocket对象返回。
//套接字是作为服务器的子级创建的,这意味着当QTcpServer对象被销毁时,它会被自动删除。在处理完对象后显式删除该对象仍然是一个好主意,以避免浪费内存。
//如果在没有挂起的连接时调用此函数,则返回0。
//注意:返回的QTcpSocket对象不能从另一个线程使用。如果要使用来自另一个线程的传入连接,则需要重写incomingConnection()。
void pauseAccepting()
//暂停接受新连接。排队的连接将保留在队列中。
QNetworkProxy proxy() const
//返回此套接字的网络代理。默认情况下,使用QNetworkProxy::DefaultProxy。
void resumeAccepting()
//继续接受新连接。
QHostAddress serverAddress() const
//如果服务器正在侦听连接,则返回服务器的地址;否则返回QHostAddress::Null。
QAbstractSocket::SocketError serverError() const
//返回上次发生的错误的错误代码。
quint16 serverPort() const
//如果服务器正在侦听连接,则返回服务器的端口;否则返回0。
void setMaxPendingConnections(int numConnections)
//将挂起的可接受连接的最大数量设置为numConnections。在调用nextPendingConnection()之前,QTcpServer将接受不超过numConnections的传入连接。默认情况下,限制为30个挂起的连接。
//在服务器达到其挂起连接的最大数量后,客户端仍然可以连接(即,QTcpSocket仍然可以发出connected()信号)。QTcpServer将停止接受新连接,但操作系统可能仍会将它们保留在队列中。
void setProxy(const QNetworkProxy &networkProxy)
//将此套接字的显式网络代理设置为networkProxy。
bool setSocketDescriptor(qintptr socketDescriptor)
//设置此服务器在侦听到socketDescriptor的传入连接时应使用的套接字描述符。如果套接字设置成功,则返回true;否则返回false。
//假定套接字处于侦听状态。
qintptr socketDescriptor() const
//返回服务器用于侦听传入指令的本机套接字描述符,如果服务器未在侦听,则返回-1。
//如果服务器正在使用QNetworkProxy,则返回的描述符可能无法用于本机套接字函数。
bool waitForNewConnection(int msec = 0, bool *timedOut = nullptr)
//等待时间最多为毫秒,或者直到有传入连接可用为止。如果连接可用,则返回true;否则返回false。如果操作超时且timedOut不为0,则*timedOut将设置为true。
//这是一个阻塞函数调用。它在单线程GUI应用程序中的使用是不利的,因为整个应用程序将停止响应,直到函数返回。waitForNewConnection()在没有可用的事件循环时非常有用。
//非阻塞的替代方法是连接到newConnection()信号。
//如果毫秒为-1,则此功能不会超时。

此外还有31 public functions inherited from QObject 

Сигналы

void acceptError(QAbstractSocket::SocketError socketError)
//当接受新连接导致错误时,会发出此信号。socketError参数描述了发生的错误类型。
void newConnection()
//每当有新的连接可用时,就会发出此信号。

此外还有2 signals inherited from QObject 

Класс QTcpServer использует

QTcpServer *server = new QTcpServer();

//监听指定的地址和端口
server->listen(QHostAddress::Any, port)

//获取已经连接的客户端套接字
socket = server->nextPendingConnection(); 

//关闭倾听服务
server->close();

Небольшое упражнение:
вставьте сюда описание изображения

//client.h
#ifndef CLIENT_H
#define CLIENT_H
#pragma execution_character_set("utf-8")
#include <QMainWindow>
#include <QTcpSocket>
#include <QKeyEvent>
#include <QWidget>

namespace Ui {
    
    
	class client;
}

class Client : public QMainWindow
{
    
    
	Q_OBJECT

public:
	explicit Client(QWidget *parent = nullptr);
	~Client();

private slots:
	void on_connect_button_clicked(bool checked);

	void on_send_button_clicked();

	void readyRead_SLOT();

	void connected_SLOT();

private:
	Ui::client *ui;
	QTcpSocket *socket;
};

#endif // CLIENT_H
//server.h
#ifndef SERVER_H
#define SERVER_H
#pragma execution_character_set("utf-8")
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QString>

QT_BEGIN_NAMESPACE
namespace Ui {
    
     class Server; }
QT_END_NAMESPACE

class Server : public QMainWindow
{
    
    
	Q_OBJECT

public:
	Server(QWidget *parent = nullptr);
	~Server();

private slots:
	void on_send_button_clicked();

	void on_startorstop_Listen_clicked(bool checked);

	void readyRead_SLOT();
	
	void newConnection_SLOT();

private:
	Ui::Server *ui;
	QTcpSocket *socket;
	QTcpServer *server;
};
#endif // SERVER_H
//client.cpp
#include "client.h"
#include "ui_client.h"
#include "stdio.h"
#include "QString"
#include <QTextCodec>
#pragma execution_character_set("utf-8")
Client::Client(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::client)
{
    
    
	
	ui->setupUi(this);
	//设置clicked(bool checked)点击反转状态打开
	ui->connect_button->setCheckable(true);

	socket = new QTcpSocket();

	//信号:客户端申请连接成功 槽函数:允许写入数据
	connect(socket, SIGNAL(connected()), this, SLOT(connected_SLOT()));
}

Client::~Client()
{
    
    
	delete ui;
}

//信号:客户端申请连接成功 槽函数:允许写入数据
void Client::connected_SLOT()
{
    
    
	connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead_SLOT()));//如果socket中有缓存消息,触发槽函数
}

//接收消息并显示到接收框
void Client::readyRead_SLOT()
{
    
    
	
	
	QString buffer;
	qDebug() << "Client Received!";
	//读取缓冲区数据
	buffer = socket->readAll();
	buffer = "Server: " + buffer;
	if (!buffer.isEmpty())
	{
    
    
		qDebug() << buffer;
		//刷新显示
		ui->receiver->appendPlainText(buffer);
	}
}

//连接和断开按键
void Client::on_connect_button_clicked(bool checked)
{
    
    
	if (checked)
	{
    
    
		QString IP = ui->ipnum->text();
		int Port = ui->portnum->text().toUInt();
		//取消已有的连接
		socket->abort();
		//连接服务器
		socket->connectToHost(IP, Port);
		//如果等待超过1000ms
		if (!socket->waitForConnected(1000))
		{
    
    
			qDebug() << "Connect failed, please try again later!";
			//连接失败,再次点击则重新连接,将checked恢复为true
			ui->connect_button->toggle();
			return;
		}
		qDebug() << "Connect Successfully! Connect with IP:" << IP << "; port:" << Port;
		//修改按键文字
		ui->connect_button->setText("断开连接");
		//发送键使能
		ui->send_button->setEnabled(true);
	}
	else
	{
    
    
		qDebug() << "Disconnect!";
		//断开连接
		socket->disconnectFromHost();
		//修改按键文字&发送键静默
		ui->connect_button->setText("连接");
		ui->send_button->setEnabled(false);
	}
}

//发送消息,写入socket缓存区
void Client::on_send_button_clicked()
{
    
    
	//qDebug() << "Client Send: " << ui->sender->toPlainText().toLocal8Bit();
	将输入框的内容写入socket缓冲区
	//socket->write(ui->sender->toPlainText().toLocal8Bit());
	qDebug() << "Client Send: " << ui->sender->toPlainText().toLatin1();
	ui->receiver->appendPlainText("client: " + ui->sender->toPlainText());
	//将输入框的内容写入socket缓冲区
	socket->write(ui->sender->toPlainText().toLatin1());
	//刷新socket缓冲区
	socket->flush();
	ui->sender->setPlainText("");
}
//server.cpp
#include "server.h"
#include "ui_server.h"
#include <QTextCodec>
Server::Server(QWidget *parent)
	: QMainWindow(parent)
	, ui(new Ui::Server)
{
    
    
	ui->setupUi(this);
	//设置clicked(bool checked)点击反转状态打开
	ui->startorstop_Listen->setCheckable(true);

	socket = new QTcpSocket();
	server = new QTcpServer();

	//信号:新的客户端连接建立 槽函数:获取客户端套接字,允许写入数据
	connect(server, SIGNAL(newConnection()), this, SLOT(newConnection_SLOT()));
}

Server::~Server()
{
    
    
	delete ui;
}

//信号:新的客户端连接建立 槽函数:获取客户端套接字,允许写入数据
void Server::newConnection_SLOT()
{
    
    
	socket = server->nextPendingConnection(); //获取已经连接的客户端套接字
	connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead_SLOT()));//如果socket中有缓存消息,触发槽函数
}

//接收消息并显示到接收框
void Server::readyRead_SLOT()
{
    
    

	qDebug() << "Server Received!";
	QString buffer;
	//读取缓冲区数据
	buffer = socket->readAll();
	buffer = "Client:  " + buffer;
	if (!buffer.isEmpty())
	{
    
    
		//刷新显示
		ui->receiver->appendPlainText(buffer);
	}
}


//开始监听和停止监听按键
void Server::on_startorstop_Listen_clicked(bool checked)
{
    
    
	if (checked)
	{
    
    
		int port = ui->portnum->text().toUInt();
		//如果未监听到
		if (!server->listen(QHostAddress::Any, port))
		{
    
    
			qDebug() << server->errorString();
			//连接失败,再次点击则重新连接,将checked恢复为true
			ui->startorstop_Listen->toggle();
			return;
		}
		qDebug() << "Listen Successfully! Message from port:" << port;
		//修改按钮文字
		ui->startorstop_Listen->setText("停止监听");
		//发送键使能
		ui->send_button->setEnabled(true);
	}
	else
	{
    
    
		qDebug() << "Stop Listening!";
		//如果已经连接则断开连接
		if (socket->state() == QAbstractSocket::ConnectedState)
		{
    
    
			//断开连接
			socket->disconnectFromHost();
		}
		//关闭倾听服务
		server->close();
		//修改按钮文字&发送键静默
		ui->startorstop_Listen->setText("开始监听");
		ui->send_button->setEnabled(false);
	}
}

//发送消息,写入socket缓存区
void Server::on_send_button_clicked()
{
    
    
	//qDebug() << "Client Send: " << ui->sender->toPlainText().toLocal8Bit();
	将输入框的内容写入socket缓冲区
	//socket->write(ui->sender->toPlainText().toLocal8Bit());
	qDebug() << "Server Send: " << ui->sender->toPlainText().toLatin1();
	ui->receiver->appendPlainText("server: " + ui->sender->toPlainText());
	//将输入框的内容写入socket缓冲区
	socket->write(ui->sender->toPlainText().toLatin1());
	//刷新socket缓冲区
	socket->flush();
	ui->sender->setPlainText("");
}
//main()
#include "server.h"
#include "client.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    
    
	QApplication a(argc, argv);
	Server w1;
	Client w2;

	//Client窗口通过鼠标单机获得聚焦
	w2.setFocusPolicy(Qt::ClickFocus);
	//将客户端和服务端窗口移动到屏幕合适位置
	w1.move(320, 340);
	w2.move(960, 340);

	//打开客户端和服务端窗口
	w1.show();
	w2.show();

	return a.exec();
}

Supongo que te gusta

Origin blog.csdn.net/AAAA202012/article/details/130288297
Recomendado
Clasificación