简介:
QTcpServer 监听套接字 只有服务端的监听套接字才使用到
QTcpSocket是Qt框架中的一个类,用于通过TCP协议进行网络通信。它提供了一套简洁而强大的API,可以用于建立TCP连接、发送和接收数据、处理连接状态以及错误处理等、
TCP和Websocket协议的区别
- 层次结构: WebSocket 是应用层协议,而 TCP 是传输层协议。
- 协议特点: TCP 是一种面向连接的协议,使用三次握手建立连接,提供可靠的数据传输。而 WebSocket 是一种无状态的协议,使用 HTTP 协议建立连接,可以进行双向通信,WebSocket 的数据传输比 TCP 更加轻量级。
- 数据格式: TCP 传输的数据需要自定义数据格式,而 WebSocket 可以支持多种数据格式,如 JSON、XML、二进制等。WebSocket 数据格式化可以更好的支持 Web 应用开发。
- 连接方式: TCP 连接的是物理地址和端口号,而 WebSocket 连接的是 URL 地址和端口号。
- 发送方:TCP套接字非阻塞操作出现部分发送的情况。Websocket从发送的数据来看,不再是一系列字节,而是按照一个完整的"消息体"发送出去的,这个"消息体"无法进一步再分割,要么全部发送成功,要么压根就不发送,不存在像TCP套接字非阻塞操作那样出现部分发送的情况。换言之,Web Socket里对套接字的操作是非阻塞操作。
- 接收方:在TCP套接字的场景下,接收方从TCP套接字读取的字节数,并不一定等于发送方调用send所发送的字节数。WebSocket的接收方从套接字读取数据,根本不是像TCP 套接字那样直接用recv/read来读取, 而是采取事件驱动机制。即应用程序注册一个事件处理函数,当web socket的发送方发送的数据在接收方应用从内核缓冲区拷贝到应用程序层已经处于可用状态时 ,应用程序注册的事件处理函数以回调(callback)的方式被调用。
要使用 Qt 的 QTcpServer QTcpSocket模块,先在 pro 文件中加上 network.
QT += core gui network
服务端
流程
- 创建服务器:new QTcpServer
- 绑定,开始监听:listen
m_SocketServer->listen(QHostAddress::Any, mPort);//端口号
- 有新的连接,触发这个信号:QTcpServer::newConnection
- 在处理newConnection信号的槽函数中,获得新的QTcpSocket客户端:QTcpServer::nextPendingConnection
- 接收到信息时候,触发信号:QIODevice::readyRead,在readyRead对应的槽函数,调用readAll、read、readLine获取客户端发送来的数据。
- 发送数据,调用函数QIODevice::write
- 客户端断开连接,触发信号:disconnected,在处理disconnected信号的槽函数中,单个QTcpSocket客户端调用deleteLater,进行释放资源。
- 主动关闭服务端,所有QTcpSocket客户端都调用close,然后服务端对象调用QWebSocketServer::close。
客户端
客户端直接使用QTcpSocket的函数进行操作即可
流程
- 创建客户端:new QTcpSocket
- 通过调用connectToHost函数连接服务端
- 连接服务端成功后,会触发connected信号。从而获知连接成功
- 发送数据,调用函数write
- 接收到信息时候,触发信号:readyRead,在readyRead对应的槽函数,调用readAll、read、readLine获取客户端发送来的数据。
- 客户端关闭时,要主动close函数,让服务端知道该客户端已断开连接。
- 断开连接成功,会触发信号disconnected,在该信号的槽函数处理断开连接的相应工作。
代码:
服务端:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_listen_clicked();
void newConnection_Slot();
void readyRead_Slot();
void DisConnection_slot();
void on_pushButton_send_clicked();
void on_pushButton_close_clicked();
private:
Ui::Widget *ui;
QTcpServer *tcpserver;
QList<QTcpSocket*> m_tcpsocketList;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
tcpserver = new QTcpServer(this);
//若有新的客户端连接的话,会触发信号newConnection_Slot()
connect(tcpserver,SIGNAL(newConnection()),this,SLOT(newConnection_Slot()));
ui->lineEdit_IP->setText("any");
ui->lineEdit_Port->setText("34567");
ui->pushButton_send->setEnabled(false);
ui->pushButton_close->setEnabled(false);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_listen_clicked()
{
tcpserver->listen(QHostAddress::Any,ui->lineEdit_Port->text().toUInt());//监听端口号
ui->pushButton_listen->setEnabled(false);
}
void Widget::newConnection_Slot()
{
QTcpSocket *tcpsocket = tcpserver->nextPendingConnection(); //获取已经连接的客户端的SOCKET套接字
connect(tcpsocket,SIGNAL(readyRead()),this,SLOT(readyRead_Slot()));//若客户端有消息进来的话,会触发信号readyRead_Slot();
connect(tcpsocket, SIGNAL(disconnected()), this, SLOT(DisConnection_slot()));//断开连接的话会触发disconnected();
m_tcpsocketList.append(tcpsocket);
ui->pushButton_send->setEnabled(true);
ui->pushButton_close->setEnabled(true);
}
void Widget::readyRead_Slot()
{
QTcpSocket *tcpsocket = qobject_cast<QTcpSocket*>(sender());
QByteArray arry = tcpsocket->readAll();
ui->textEdit_recv->setText(QString::fromUtf8(arry));
}
void Widget::DisConnection_slot()
{
QTcpSocket *tcpsocket = qobject_cast<QTcpSocket*>(sender());
m_tcpsocketList.removeAll(tcpsocket);
tcpsocket->deleteLater();
}
void Widget::on_pushButton_send_clicked()
{
QString msg = ui->textEdit_send->toPlainText();
QByteArray arry = msg.toUtf8();
for(auto socket : m_tcpsocketList)
{
socket ->write(arry);
}
}
void Widget::on_pushButton_close_clicked()
{
for(auto socket : m_tcpsocketList)
{
socket ->close();
}
tcpserver->close();
ui->pushButton_listen->setEnabled(true);
ui->pushButton_send->setEnabled(false);
ui->pushButton_close->setEnabled(false);
}
客户端
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_connect_clicked();
void on_pushButton_send_clicked();
void connected_SLOT();
void readyRead_Slot();
void DisConnection_slot();
void on_pushButton_disconnect_clicked();
private:
Ui::Widget *ui;
QTcpSocket *tcpsocket;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
tcpsocket=new QTcpSocket(this);
ui->lineEdit_IP->setText("127.0.0.1");
ui->lineEdit_Port->setText("34567");
ui->label_status->setText("尚未连接服务器");
ui->pushButton_connect->setEnabled(true);
ui->pushButton_send->setEnabled(false);
ui->pushButton_disconnect->setEnabled(false);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_connect_clicked()
{
QHostAddress add(ui->lineEdit_IP->text());
tcpsocket->connectToHost(add,ui->lineEdit_Port->text().toInt());//转为无符号,连接服务器端口
connect(tcpsocket,SIGNAL(connected()),this,SLOT(connected_SLOT()));
}
void Widget::connected_SLOT()
{
ui->label_status->setText("连接上了服务器");
ui->pushButton_connect->setEnabled(false);
ui->pushButton_send->setEnabled(true);
ui->pushButton_disconnect->setEnabled(true);
connect(tcpsocket,SIGNAL(readyRead()),this,SLOT(readyRead_Slot()));
connect(tcpsocket, SIGNAL(disconnected()), this, SLOT(DisConnection_slot()));
}
void Widget::readyRead_Slot()
{
QTcpSocket *tcpsocket = qobject_cast<QTcpSocket*>(sender());
QByteArray arry = tcpsocket->readAll();
QString msg = QString::fromUtf8(arry);
ui->textEdit_recv->setText(msg);
}
void Widget::DisConnection_slot()
{
QTcpSocket *tcpsocket = qobject_cast<QTcpSocket*>(sender());
ui->label_status->setText("断开连接服务器");
ui->pushButton_connect->setEnabled(true);
ui->pushButton_send->setEnabled(false);
ui->pushButton_disconnect->setEnabled(false);
}
void Widget::on_pushButton_send_clicked()
{
QString msg = ui->textEdit_send->toPlainText();
QByteArray arry = msg.toUtf8();
tcpsocket ->write(arry);
}
void Widget::on_pushButton_disconnect_clicked()
{
tcpsocket->close();
}
QTcpServer多线程
每个客户端连接的tcpSocket分别分配一个专门的线程来处理。
核心思想:继承并重写QTcpServer的incomingConnection函数去自己实现tcpsocket连接的建立和分配。
incomingConnection函数说明:
当QTcpServer有一个新的连接时这个虚函数被调用。该socketDescriptor参数是用于接受连接的本地套接字描述符。
该函数会创建一个QTcpSocket,并设置套接字描述符为socketDescriptor,然后存储QTcpSocket在挂起连接的内部清单。最后newConnection()被发射。
重新实现这个函数来改变服务器的行为,当一个连接可用。
注意:如果你想处理在另一个线程一个新的QTcpSocket对象传入连接,您必须将socketDescriptor传递给其他线程,并创建了QTcpSocket对象存在并使用其setSocketDescriptor()方法。
同时:
接收函数:readAll、发送函数:write、关闭连接函数:close 都需要在子线程实现。
具体代码见:https://download.csdn.net/download/baidu_16370559/89709246?spm=1001.2014.3001.5501