最近没事想着研究一下QT 的服务器,了解了一下QT中相关的接口,突然发现incomingConnection这个方式好简单呀,弄好后测试了一下(没有数据那种,只是简单的收到确认和回复(打开33个client客户端),不保证大批量的数据处理的效率)
首先要弄一个数据接收已经处理的socket,只要readyRead和disconnected就行,一个用来接收数据,一个用来销毁连接
// 头文件
#ifndef TCPSOCKET_H
#define TCPSOCKET_H
#include
class TcpSocket : public QTcpSocket
{
Q_OBJECT
public:
TcpSocket(QObject *parent = nullptr);
signals:
void emit_updateClients(QString, int);
void emit_disConnected(qintptr);
//private slots:
// void onDataRecv();
// void onDisconnected();
};
#endif // TCPSOCKET_H
// cpp
#include "tcpsocket.h"
TcpSocket::TcpSocket(QObject *parent)
: QTcpSocket (parent)
{
connect(this, &QTcpSocket::readyRead, [this](){
while (this->bytesAvailable() > 0)
{
int length = this->bytesAvailable();
QByteArray data;
data.resize(length);
this->read(data.data(), length); // 接受sock数据
QString strReadData = QString::number(this->socketDescriptor()) + " " + data;
emit emit_updateClients(strReadData, strReadData.length());
// for循环是为了模拟,大批量数据处理
for(int index = 0; index < 1000000; ++index)
{
}
QString strMsg = "Recv Success";
this->write(strMsg.toStdString().c_str(), strMsg.length()); // 给client客户端返回接收情况
}
});
// 调用销毁信号,但是看实际情况,调用这个的时候就销毁了,会导致获取到的描述符为-1, 但是不会影响到其他地方
connect(this, &QTcpSocket::disconnected, [this](){
qDebug() << this->socketDescriptor();
emit emit_disConnected(this->socketDescriptor()); // 获取socket描述符
});
}
接下来是服务器部分,真的真的超级简单,我都惊了,直接上代码,部分地方会上解释
// 头文件
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include
#include
#include "TcpServer/tcpsocket.h"
class TcpServer : public QTcpServer
{
Q_OBJECT
public:
TcpServer(QObject *parent = nullptr, int port = 3366);
QStringList m_strListData;
signals:
void emit_UpdateServer(QString, int);
protected:
QList list_tcpclient; // 用来方便管理各个客户端
// 用来连接客户端,这个函数需要注意,在QT 5后面的版本参数部分类型要改为 **qintptr** 否则会出现连接上了,但是没有执行函数的情况
virtual void incomingConnection(qintptr socketDescriptor);
};
#endif // TCPSERVER_H
// cpp
#include "tcpserver.h"
#include
TcpServer::TcpServer(QObject *parent, int port)
: QTcpServer (parent)
{
/****************
* QHostAddress
* Null 空地址
* LockHost IPv4 127.0.0.1 本地回环地址
* LockHostIPv6 IPv6 本地回环地址
* Broadcast 广播地址 255.255.255.255
* Any IPv4 0.0.0.0 任意地址
* AnyIPv6 IPv6 任意地址
******************/
this->listen(QHostAddress::Any, port);
if(this->isListening())
{
qDebug() << "isListening";
}
}
void TcpServer::incomingConnection(qintptr socketDescriptor)
{
TcpSocket *socket = new TcpSocket();
// 设置本 socket 唯一标识符
socket->setSocketDescriptor(socketDescriptor);
qDebug() << socketDescriptor << " " << socket->socketDescriptor();
// 添加到list中方便管理
list_tcpclient.append(socket);
// 本 socket 接受到的信息
connect(socket, &TcpSocket::emit_updateClients, [this](QString strInfo, int length){
// 这个信息要进行什么操作,这里是为了将收到的数据展示
m_strListData.append(strInfo);
if(m_strListData.size() >= 200)
{
m_strListData.clear();
}
Q_UNUSED(length);
});
// 在list中移除,销毁实际是在 TcpSocket 中触发销毁的时候就已经销毁了
connect(socket, &TcpSocket::emit_disConnected, [this](int isocketDescriptor){
for(int index = 0; index < list_tcpclient.size(); ++index)
{
QTcpSocket *item = list_tcpclient.at(index);
qDebug() << list_tcpclient.at(index)->socketDescriptor();
qDebug() << item << " socketDescriptor " << item->socketDescriptor() << " " << isocketDescriptor;
if(item->socketDescriptor() == isocketDescriptor)
{
list_tcpclient.removeAt(index);
return ;
}
qDebug() << "list size " << list_tcpclient.size();
}
});
}
UI界面主要就是简单的展示数据,其实上面也看不到,数据有点快
// 主要代码
tcpserver = new TcpServer;
connect(timer, &QTimer::timeout, [this](){
ui->textEdit->clear();
for(int index = 0; index < tcpserver->m_strListData.size(); ++index)
ui->textEdit->append(tcpserver->m_strListData.at(index));
});
timer->start(100);
下面介绍一下incomingConnection
原文解释:
This virtual function is called by QTcpServer when a new connection is available. The socketDescriptor argument is the native socket descriptor for the accepted connection.
The base implementation creates a QTcpSocket, sets the socket descriptor and then stores the QTcpSocket in an internal list of pending connections. Finally newConnection() is emitted.
Reimplement this function to alter the server’s behavior when a connection is available.
If this server is using QNetworkProxy then the socketDescriptor may not be usable with native socket functions, and should only be used with QTcpSocket::setSocketDescriptor().
Note: If another socket is created in the reimplementation of this method, it needs to be added to the Pending Connections mechanism by calling addPendingConnection().
Note: If you want to handle an incoming connection as a new QTcpSocket object in another thread you have to pass the socketDescriptor to the other thread and create the QTcpSocket object there and use its setSocketDescriptor() method.
译文:
当有新的连接可用时,QTcpServer将调用这个虚拟函数。socketDescriptor参数是接受连接的本地套接字描述符。
基本实现创建一个QTcpSocket,设置套接字描述符,然后将QTcpSocket存储在一个挂起连接的内部列表中。最后发出newConnection()。
重新实现此函数,以在连接可用时更改服务器的行为。
如果此服务器使用QNetworkProxy,则socketDescriptor可能不能用于本机套接字函数,只能用于QTcpSocket::setSocketDescriptor()。
注意:如果在此方法的重新实现中创建了另一个套接字,则需要通过调用addPendingConnection()将其添加到挂起连接机制。
注意:如果您想要在另一个线程中处理一个新的QTcpSocket对象,那么您必须将socketDescriptor传递给另一个线程,并在那里创建QTcpSocket对象,然后使用它的setSocketDescriptor()方法。
作用说白了,就是,有此函数,然后你有自己的QTcpSocket,那么将这个socket描述符赋值到你的socket中,那么你就可以用这个描述符了
工程代码:
https://download.csdn.net/download/bloke_come/12014976