本次分享下关于QTcp*的使用。我们的服务端为客户端发送图片。
服务端,继承QTcpServer,重写incomingConnection方法。
- 一个线程,一个map来管理客户端的socket描述符。
- 重写incomingConnection,从源头获取客户端的套接字描述符
实现如下:
class CusTcpServer:public QTcpServer
{
Q_OBJECT
public:
CusTcpServer(QObject *parent = nullptr);
~CusTcpServer();
//新连接直接获取套接字描述符
void incomingConnection(qintptr socketDescriptor) override;
bool insertSocketMap(qintptr fd,CusSocket *socket);
void sendImage(CusSocket *socket);
signals:
void sig_debugOutPut(QString info);
public slots:
// 监听
void slot_startListen();
private:
QMap<qintptr,CusSocket*> map_clientSocket;
QThread *m_thread;
QImage m_image;
};
CusTcpServer::CusTcpServer(QObject *parent)
:QTcpServer (parent)
{
m_image.load(":/image/111.jpg");
m_thread = new QThread;
this->moveToThread(m_thread);
m_thread->start();
}
CusTcpServer::~CusTcpServer()
{
qDeleteAll(map_clientSocket.begin(),map_clientSocket.end());
map_clientSocket.clear();
m_thread->quit();
m_thread->deleteLater();
m_thread = nullptr;
}
void CusTcpServer::incomingConnection(qintptr socketDescriptor)
{
// 处于线程
CusSocket *t_socket = new CusSocket(socketDescriptor);// 不可指定父亲
t_socket->moveToThread(m_thread);
connect(t_socket, &QTcpSocket::readyRead, t_socket, &CusSocket::slot_getMessage);
// add
insertSocketMap(socketDescriptor, t_socket);
// sendimage ,more to do
sendImage(t_socket);
}
bool CusTcpServer::insertSocketMap(qintptr fd, CusSocket *socket)
{
bool ret = false;
if(!map_clientSocket.contains(fd))
{
map_clientSocket.insert(fd,socket);
ret = true;
}
return ret;
}
void CusTcpServer::sendImage(CusSocket *socket)
{
// 字节
QByteArray bytes;
QBuffer buffer;
m_image.save(&buffer,"JPG");
bytes.append(buffer.data());
socket->write(bytes);
}
void CusTcpServer::slot_startListen()
{
// 使用信号和槽连接,线程中
if(!this->isListening())
{
QHostAddress address = QHostAddress::LocalHost;
quint16 port = 6789;
bool ret = listen(address,port);
emit sig_debugOutPut(QString("server listen ip:%1 , port:%2, ret = %3").arg(address.toString()).arg(port).arg(ret));
}
emit sig_debugOutPut("server is already listening");
}
由于我们将server移入到其他线程,最好将其操作都置于槽函数中,eg:我们对listen进行包裹,有新的连接时,触发 incomingConnection方法,该方法位于线程中,获取源描述符,并移到线程中。
客户端,继承QTcpSocket,连接时,connectToHost即可。
class CusSocket:public QTcpSocket
{
Q_OBJECT
public:
CusSocket(qintptr socketDescriptor,QObject *parent = nullptr);
public slots:
void slot_getMessage();
private:
qintptr m_fd;
};
CusSocket::CusSocket(qintptr socketDescriptor, QObject *parent)
:QTcpSocket(parent)
{
m_fd = socketDescriptor;
setSocketDescriptor(m_fd);
}
void CusSocket::slot_getMessage()
{
if(bytesAvailable() <= 0)
{
return;
}
QByteArray dataBytes = readAll();
QImage image = QImage::fromData(dataBytes,"JPG");
image.save("D:/tcpget.jpg","JPG",100);
}
本次发送的数据比较小,重点在于理解重写incomingConnection方法获取源描述符和线程管理。