最近在用QML写一个小工具Mock,模拟下位机数据用于测试中控软件,正好用到了QTcpServer。
这里分享一下代码,继承ClientHandle,重写方法即可。做一个小demo还是比较方便的。
#ifndef SOCKETSERVER_H
#define SOCKETSERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QThread>
#include <QMetaType>
using Port = quint16;
// After new client connected, a thread will be created.
// Demo only
template <typename ClientHandle>
class SocketServer : public QTcpServer
{
public:
SocketServer(QObject *parent = nullptr)
:QTcpServer(parent) {
qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
}
~SocketServer() { uninit(); }
public:
bool init(const QString &ip_addr, const Port &port) {
QHostAddress host_addr(ip_addr);
return this->listen(host_addr, port);
}
bool uninit() {
if(this->isListening()) this->close();
for(auto iter = connections_.begin();
iter != connections_.end();)
{
delete (*iter);
iter = connections_.erase(iter);
}
return true;
}
bool sendToAll(const QByteArray &data){
for(auto iter = connections_.begin();
iter != connections_.end();){
if((*iter)->state() == QAbstractSocket::UnconnectedState){
iter = connections_.erase(iter);
continue;
}
else if(!(*iter)->isValid()){
continue;
}
(*iter)->send(data);
++iter;
}
return true;
}
private:
void incomingConnection(qintptr handle) override {
QThread *thread = new QThread();
ClientHandle *tcp_client = new ClientHandle(handle, thread);
connections_.push_back(tcp_client);
}
private:
std::list<ClientHandle*> connections_;
};
class ClientHandle : public QTcpSocket{
Q_OBJECT
public:
ClientHandle(qintptr handle, QThread *thread) {
this->setSocketDescriptor(handle);
connect(this, &ClientHandle::readyRead,
this, &ClientHandle::onReadyRead);
connect(this, &ClientHandle::sendData,
this, &ClientHandle::onSendData);
connect(this, &ClientHandle::disconnected,
thread, &QThread::quit);
this->moveToThread(thread);
thread->start();
}
~ClientHandle() { this->disconnect(); }
void send(const QByteArray &data){
emit sendData(data);
}
protected:
virtual void onHandleData(const QByteArray &, QByteArray &){ }
signals:
void sendData(const QByteArray &data);
private slots:
void onSendData(const QByteArray &data){
this->write(data);
}
void onReadyRead(){
ClientHandle *socket = (ClientHandle*)sender();
QByteArray recv_bytes, send_bytes;
recv_bytes = socket->readAll();
onHandleData(recv_bytes, send_bytes);
if(send_bytes.size() == 0) return;
if(socket->write(send_bytes) == -1)
qDebug()<< "Write failed.";
return;
}
void onSocketError(QAbstractSocket::SocketError) {
ClientHandle* socket = (ClientHandle*)sender();
socket->disconnect();
return;
}
};
#endif // SOCKETSERVER_H