2020년 9월 1일 화요일

QTcpSocket 외부 쓰레드에서 write 하는 방법

 QTcpSocket 은 생성된 쓰레드 외에 다른 쓰레드에서 write 를 시도할 경우 에러를 리턴하며 전송이 되지않는다. 이럴 경우 아래와 같이 이벤트를 발생시켜 생성 쓰레드에서 write 처리를 하도록해서 문제를 해결할 수 있다.


 #ifndef TCPAUDIOSENDER_H  
 #define TCPAUDIOSENDER_H  
 #include <QThread>  
 #include <QTcpServer>  
 #include <QTcpSocket>  
 #include <QMutex>  
 #include "logwriter.h"  
 #include "bufferqueue.h"  
 class TcpAudioSender : public QThread  
 {  
   Q_OBJECT  
 public:  
   explicit TcpAudioSender(BufferQueue<AudioBuffer> *pSendQue, QThread *parent = 0);  
   virtual ~TcpAudioSender();  
 signals:  
   void socketWriteEvent(QTcpSocket *sock, AudioBuffer *buffer);  
 public slots:  
   void onNewConnection();  
   void onSocketStateChanged(QAbstractSocket::SocketState socketState);  
   void onReadyRead();  
   void onSocketWrite(QTcpSocket *sock, AudioBuffer *buffer);  
 public:  
   bool startServer(int port);  
   void stopServer();  
 protected:  
   virtual void run() override;  
 protected:  
   bool      m_bRun;  
   QTcpServer*     m_pServer;  
   int         m_nPort;  
   QList<QTcpSocket*> m_clientList;  
   QMutex       m_clientListMutex;  
   BufferQueue<AudioBuffer>*  m_pSendQue;  
   LogWriter*   m_pLogWriter;  
 };  
 #endif // TCPAUDIOSENDER_H  



 #include "tcpaudiosender.h"  
 TcpAudioSender::TcpAudioSender(BufferQueue<AudioBuffer> *pSendQue, QThread *parent) : QThread(parent)  
 {  
   m_bRun = false;  
   m_pServer = new QTcpServer(this);  
   m_pSendQue = pSendQue;  
   m_pLogWriter = LogWriter::getInstance();  
 }  
 TcpAudioSender::~TcpAudioSender()  
 {  
   delete m_pServer;  
 }  
 bool TcpAudioSender::startServer(int port)  
 {  
   if (!m_pServer->listen(QHostAddress::Any, port)) {  
     m_pLogWriter->writeLog(QString("TcpAudioSender cannot open %1 port").arg(port));  
     return false;  
   }  
   m_nPort = port;  
   connect(m_pServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));  
   connect(this, SIGNAL(socketWriteEvent(QTcpSocket*, AudioBuffer*)), this, SLOT(onSocketWrite(QTcpSocket*, AudioBuffer*)));  
   m_bRun = true;  
   QThread::start();  
   return true;  
 }  
 void TcpAudioSender::stopServer()  
 {  
   m_pServer->close();  
   m_bRun = false;  
   QThread::wait();  
 }  
 void TcpAudioSender::onNewConnection()  
 {  
   QTcpSocket *clientSocket = m_pServer->nextPendingConnection();  
   connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));  
   connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));  
   m_clientListMutex.lock();  
   m_clientList.push_back(clientSocket);  
   m_clientListMutex.unlock();  
 }  
 void TcpAudioSender::onSocketStateChanged(QAbstractSocket::SocketState socketState)  
 {  
   if (socketState == QAbstractSocket::UnconnectedState)  
   {  
     QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());  
     m_clientListMutex.lock();  
     m_clientList.removeOne(sender);  
     m_clientListMutex.unlock();  
   }  
 }  
 void TcpAudioSender::onReadyRead()  
 {  
   /*  
   QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());  
   QByteArray datas = sender->readAll();  
   for (QTcpSocket* socket : m_clientList) {  
     if (socket != sender)  
       socket->write(QByteArray::fromStdString(sender->peerAddress().toString().toStdString() + ": " + datas.toStdString()));  
   }  
   */  
 }  
 void TcpAudioSender::onSocketWrite(QTcpSocket *sock, AudioBuffer *buffer)  
 {  
   int res = sock->write(buffer->data, buffer->size);  
   if (res != buffer->size) {  
     qDebug() << QString("TcpAudioSender(%1) cannot send client : %2, %3").arg(m_nPort).arg(res).arg(buffer->size);  
   }  
   delete buffer;  
   //qDebug() << "TcpAudioSender send :" << size << "bytes";  
 }  
 void TcpAudioSender::run()  
 {  
   while (m_bRun)  
   {  
     AudioBuffer *buffer = m_pSendQue->pop();  
     if (!buffer) {  
       usleep(10);  
       continue;  
     }  
     m_clientListMutex.lock();  
     for (QTcpSocket* socket : m_clientList) {  
       AudioBuffer *sendBuffer = new AudioBuffer(buffer->data, buffer->size);  
       emit socketWriteEvent(socket, sendBuffer);  
     }  
     m_clientListMutex.unlock();  
     delete buffer;  
   }  
 }