qtQtcphotoshoperver服务端_qt websocket
0.前言
本文主要讲解 Qt TCP 相关接口的基本应用,一些实践相关的后面会单独写。
TCP 协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP 通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输。
TCP 通过三次握手来建立可靠的连接。
TCP 四次挥手断开连接。TCP连接是双向的,在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C 语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
1.准备工作
首先,要使用 Qt 的网络模块需要在 pro 中加上 network(如果是 VS IDE 就在模块选择里勾选上 network):
QT = network
引入相关类的头文件:
另外, Qt 在 windows 下使用的 select 模型,在 linux 下新版本的改为了 poll 模型(具体版本待查)。
Qt TCP 的操作流程:
2.认识QTcpSocket的接口
QTcpSocket 是 QAbstractSocket 的子类,用于建立 TCP 连接并传输数据流。
对于 QTcpServer 服务端,可通过 nextPendingConnection() 接口获取到建立了 TCP 连接的 QTcpSocket 对象。
对于客户端,创建好 QTcpSocket 对象后,调用 connectToHost() 连接到服务端:
-
void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, QIODevice::OpenMode openMode = ReadWrite, QAbstractSocket::NetworkLayerProtocol protocol = AnyIPProtocol)
-
void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, QIODevice::OpenMode openMode = ReadWrite)
连接成功和连接断开会触发 connected() 和 disconnected() 信号:
-
void QAbstractSocket::connected()
-
void QAbstractSocket::disconnected()
连接成功之后,可以调用 QIODevice 继承来的 read,write 等接口:
-
qint64 QIODevice::read(char *data, qint64 maxSize)
-
QByteArray QIODevice::read(qint64 maxSize)
-
QByteArray QIODevice::readAll()
-
qint64 QIODevice::write(const char *data, qint64 maxSize)
-
qint64 QIODevice::write(const char *data)
-
qint64 QIODevice::write(const QByteArray &byteArray)
当有新的数据到来,会触发 readyRead() 信号,此时在槽函数中进行读取即可:
void QIODevice::readyRead()
操作完之后,调用相关接口关闭 TCP 连接:
-
void QAbstractSocket::disconnectFromHost()
-
void QAbstractSocket::close()
-
void QAbstractSocket::abort()
其中, abort 调用了 close, close 调用了 disconnectFromHost。 abort 立即关闭套接字,并丢弃写缓冲区中的所有待处理数据。close 关闭套接字的 IO,以及套接字的连接。
3.认识QTcpServer的接口
QTcpServer 类提供基于 TCP 的服务器。
首先,调用 listen() 监听指定的地址和端口:
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
当有新的 TCP 连接,会触发 newConnection() 信号,此时可以调用 nextPendingConnection() 以将挂起的连接接受为已连接的 QTcpSocket,通过该对象可以与客户端通信。
QTcpSocket *QTcpServer::nextPendingConnection()
注意,返回的 QTcpSocket 对象不能在另一个线程使用,如果需要在别的线程管理这个 socket 连接,需要重写 Server 的 incomingConnection() ,将 sokcet 描述符传递给别的线程并创建 QTcpSocket:
void QTcpServer::incomingConnection(qintptr socketDescriptor)
最后,调用 close() 停止监听:
void QTcpServer::close()
4.Qt Tcp的简单示例
完整代码链接(分为SimpleTcpServer和SimpleTcpClient两个子项目):
运行效果:
服务端主要实现代码:
-
-
-
-
-
-
-
-
QT_BEGIN_NAMESPACE
-
namespace Ui { class Widget; }
-
QT_END_NAMESPACE
-
-
//simple Tcp 服务端
-
class Widget : public QWidget
-
{
-
Q_OBJECT
-
-
public:
-
Widget(QWidget *parent = nullptr);
-
~Widget();
-
-
private:
-
//初始化server操作
-
void initServer();
-
//close server
-
void closeServer();
-
//更新当前状态
-
void updateState();
-
-
private:
-
Ui::Widget *ui;
-
//server用于监听端口,获取新的tcp连接的描述符
-
QTcpServer *server;
-
//存储已连接的socket对象
-
QList<QTcpSocket*> clientList;
-
};
-
-
#include "widget.h"
-
#include "ui_widget.h"
-
#include <QHostAddress>
-
Widget::Widget(QWidget *parent)
-
: QWidget(parent)
-
, ui(new Ui::Widget)
-
{
-
ui->setupUi(this);
-
setWindowTitle("Server");
-
initServer();
-
}
-
Widget::~Widget()
-
{
-
//关闭server
-
closeServer();
-
delete ui;
-
}
-
void Widget::initServer()
-
{
-
//创建Server对象
-
server = new QTcpServer(this);
-
//点击监听按钮,开始监听
-
connect(ui->btnListen,&QPushButton::clicked,[this]{
-
//判断当前是否已开启,是则close,否则listen
-
if(server->isListening()){
-
//server->close();
-
closeServer();
-
//关闭server后恢复界面状态
-
ui->btnListen->setText("Listen");
-
ui->editAddress->setEnabled(true);
-
ui->editPort->setEnabled(true);
-
}else{
-
//从界面上读取ip和端口
-
//可以使用 QHostAddress::Any 监听所有地址的对应端口
-
const QString address_text=ui->editAddress->text();
-
const QHostAddress address=(address_text=="Any")
-
?QHostAddress::Any
-
:QHostAddress(address_text);
-
const unsigned short port=ui->editPort->text().toUShort();
-
//开始监听,并判断是否成功
-
if(server->listen(address,port)){
-
//连接成功就修改界面按钮提示,以及地址栏不可编辑
-
ui->btnListen->setText("Close");
-
ui->editAddress->setEnabled(false);
-
ui->editPort->setEnabled(false);
-
}
-
}
-
updateState();
-
});
-
//监听到新的客户端连接请求
-
connect(server,&QTcpServer::newConnection,this,[this]{
-
//如果有新的连接就取出
-
while(server->hasPendingConnections())
-
{
-
//nextPendingConnection返回下一个挂起的连接作为已连接的QTcpSocket对象
-
//套接字是作为服务器的子级创建的,这意味着销毁QTcpServer对象时会自动删除该套接字。
-
//最好在完成处理后显式删除该对象,以避免浪费内存。
-
//返回的QTcpSocket对象不能从另一个线程使用,如有需要可重写incomingConnection().
-
QTcpSocket *socket=server->nextPendingConnection();
-
clientList.append(socket);
-
ui->textRecv->append(QString("[%1:%2] Soket Connected")
-
.arg(socket->peerAddress().toString())
-
.arg(socket->peerPort()));
-
//关联相关操作的信号槽
-
//收到数据,触发readyRead
-
connect(socket,&QTcpSocket::readyRead,[this,socket]{
-
//没有可读的数据就返回
-
if(socket->bytesAvailable()<=0)
-
return;
-
//注意收发两端文本要使用对应的编解码
-
const QString recv_text=QString::fromUtf8(socket->readAll());
-
ui->textRecv->append(QString("[%1:%2]")
-
.arg(socket->peerAddress().toString())
-
.arg(socket->peerPort()));
-
ui->textRecv->append(recv_text);
-
});
-
//error信号在5.15换了名字
-
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
-
//错误信息
-
connect(socket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
-
[this,socket](QAbstractSocket::SocketError){
-
ui->textRecv->append(QString("[%1:%2] Soket Error:%3")
-
.arg(socket->peerAddress().toString())
-
.arg(socket->peerPort())
-
.arg(socket->errorString()));
-
});
-
#else
-
//错误信息
-
connect(socket,&QAbstractSocket::errorOccurred,[this,socket](QAbstractSocket::SocketError){
-
ui->textRecv->append(QString("[%1:%2] Soket Error:%3")
-
.arg(socket->peerAddress().toString())
-
.arg(socket->peerPort())
-
.arg(socket->errorString()));
-
});
-
#endif
-
//连接断开,销毁socket对象,这是为了开关server时socket正确释放
-
connect(socket,&QTcpSocket::disconnected,[this,socket]{
-
socket->deleteLater();
-
clientList.removeOne(socket);
-
ui->textRecv->append(QString("[%1:%2] Soket Disonnected")
-
.arg(socket->peerAddress().toString())
-
.arg(socket->peerPort()));
-
updateState();
-
});
-
}
-
updateState();
-
});
-
//server向client发送内容
-
connect(ui->btnSend,&QPushButton::clicked,[this]{
-
//判断是否开启了server
-
if(!server->isListening())
-
return;
-
//将发送区文本发送给客户端
-
const QByteArray send_data=ui->textSend->toPlainText().toUtf8();
-
//数据为空就返回
-
if(send_data.isEmpty())
-
return;
-
for(QTcpSocket *socket:clientList)
-
{
-
socket->write(send_data);
-
//socket->waitForBytesWritten();
-
}
-
});
-
//server的错误信息
-
//如果发生错误,则serverError()返回错误的类型,
-
//并且可以调用errorString()以获取对所发生事件的易于理解的描述
-
connect(server,&QTcpServer::acceptError,[this](QAbstractSocket::SocketError){
-
ui->textRecv->append("Server Error:" server->errorString());
-
});
-
}
-
void Widget::closeServer()
-
{
-
//停止服务
-
server->close();
-
for(QTcpSocket * socket:clientList)
-
{
-
//断开与客户端的连接
-
socket->disconnectFromHost();
-
if(socket->state()!=QAbstractSocket::UnconnectedState){
-
socket->abort();
-
}
-
}
-
}
-
void Widget::updateState()
-
{
-
//将当前server地址和端口、客户端连接数写在标题栏
-
if(server->isListening()){
-
setWindowTitle(QString("Server[%1:%2] connections:%3")
-
.arg(server->serverAddress().toString())
-
.arg(server->serverPort())
-
.arg(clientList.count()));
-
}else{
-
setWindowTitle("Server");
-
}
-
}
客户端主要实现代码:
-
-
-
-
-
QT_BEGIN_NAMESPACE
-
namespace Ui { class Widget; }
-
QT_END_NAMESPACE
-
//simple Tcp 客户端
-
class Widget : public QWidget
-
{
-
Q_OBJECT
-
public:
-
Widget(QWidget *parent = nullptr);
-
~Widget();
-
private:
-
//初始化client操作
-
void initClient();
-
//更新当前状态
-
void updateState();
-
private:
-
Ui::Widget *ui;
-
//socket对象
-
QTcpSocket *client;
-
};
-
-
#include "widget.h"
-
#include "ui_widget.h"
-
#include <QHostAddress>
-
Widget::Widget(QWidget *parent)
-
: QWidget(parent)
-
, ui(new Ui::Widget)
-
{
-
ui->setupUi(this);
-
setWindowTitle("Client");
-
initClient();
-
}
-
Widget::~Widget()
-
{
-
//析构关闭连接
-
//client->disconnectFromHost();
-
//if(client->state()!=QAbstractSocket::UnconnectedState){
-
// client->waitForDisconnected();
-
//}
-
//关闭套接字的I/O设备,并调用disconnectFromHost()关闭套接字的连接。
-
//client->close();
-
//中止当前连接并重置套接字。与disconnectFromHost()不同,
-
//此函数立即关闭套接字,并丢弃写缓冲区中的所有待处理数据。
-
client->abort();
-
delete ui;
-
}
-
void Widget::initClient()
-
{
-
//创建client对象
-
client = new QTcpSocket(this);
-
//点击连接,根据ui设置的服务器地址进行连接
-
connect(ui->btnConnect,&QPushButton::clicked,[this]{
-
//判断当前是否已连接,连接了就断开
-
if(client->state()==QAbstractSocket::ConnectedState){
-
//如果使用disconnectFromHost()不会重置套接字,isValid还是会为true
-
client->abort();
-
}else if(client->state()==QAbstractSocket::UnconnectedState){
-
//从界面上读取ip和端口
-
const QHostAddress address=QHostAddress(ui->editAddress->text());
-
const unsigned short port=ui->editPort->text().toUShort();
-
//连接服务器
-
client->connectToHost(address,port);
-
}else{
-
ui->textRecv->append("It is not ConnectedState or UnconnectedState");
-
}
-
});
-
//连接状态
-
connect(client,&QTcpSocket::connected,[this]{
-
//已连接就设置为不可编辑
-
ui->btnConnect->setText("Disconnect");
-
ui->editAddress->setEnabled(false);
-
ui->editPort->setEnabled(false);
-
updateState();
-
});
-
connect(client,&QTcpSocket::disconnected,[this]{
-
//断开连接还原状态
-
ui->btnConnect->setText("Connect");
-
ui->editAddress->setEnabled(true);
-
ui->editPort->setEnabled(true);
-
updateState();
-
});
-
//发送数据
-
connect(ui->btnSend,&QPushButton::clicked,[this]{
-
//判断是可操作,isValid表示准备好读写
-
if(!client->isValid())
-
return;
-
//将发送区文本发送给客户端
-
const QByteArray send_data=ui->textSend->toPlainText().toUtf8();
-
//数据为空就返回
-
if(send_data.isEmpty())
-
return;
-
client->write(send_data);
-
//client->waitForBytesWritten();
-
});
-
//收到数据,触发readyRead
-
connect(client,&QTcpSocket::readyRead,[this]{
-
//没有可读的数据就返回
-
if(client->bytesAvailable()<=0)
-
return;
-
//注意收发两端文本要使用对应的编解码
-
const QString recv_text=QString::fromUtf8(client->readAll());
-
ui->textRecv->append(QString("[%1:%2]")
-
.arg(client->peerAddress().toString())
-
.arg(client->peerPort()));
-
ui->textRecv->append(recv_text);
-
});
-
//error信号在5.15换了名字
-
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
-
//错误信息
-
connect(client, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error),
-
[this](QAbstractSocket::SocketError){
-
ui->textRecv->append("Socket Error:" client->errorString());
-
});
-
#else
-
//错误信息
-
connect(client,&QAbstractSocket::errorOccurred,[this](QAbstractSocket::SocketError){
-
ui->textRecv->append("Socket Error:" client->errorString());
-
});
-
#endif
-
}
-
void Widget::updateState()
-
{
-
//将当前client地址和端口写在标题栏
-
if(client->state()==QAbstractSocket::ConnectedState){
-
setWindowTitle(QString("Client[%1:%2]")
-
.arg(client->localAddress().toString())
-
.arg(client->localPort()));
-
}else{
-
setWindowTitle("Client");
-
}
-
}
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C 语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
这篇文章转载于:学新通
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通
- 本文地址: https://www.swvq.com/boutique/detail/tanhehfgfh
- 联系方式: luke.wu●vfv.cc
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
photoshop怎么在一张图上加图
PHP中文网 06-24 -
photoshop像素不50怎么改
PHP中文网 06-22 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
photoshop蒙版画笔没反应怎么办
PHP中文网 06-24 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
抖音国际版要用什么加速器能流畅刷Tiktok的加速器
TK小达人 08-02