Qt TCP通信,多线程服务器端

相信许多初学Qt的同学都会和我一样遇到这样的问题:

  • 更新于2019-06-15:
    感谢sleikang的评论,因为在serverThread的线程函数中调用了exec(),就算client退出后,该线程也将一直处于事件循环中,不会退出。只需要在退出时,调用quit()函数就好了。
    还有就是一点,此例子也只是仅供参考,因为在实际中为每一个连接去建立一个线程还是比较“奢侈”的事情。本身这里只需要将新接入的MySocket进行一下管理就好了,还望各位熟知。

一、Qt TCP通信在使用nextPendingConnect后,服务器端就只会与最后接入的客户端通信,这个时候就会考虑继承QThread实现多线程,从而实现多个客户端与服务器端通信,每当一个新的客户端连接时,通过标识码socketDescriptor,实现与对应的客户端通信。这里的Server类继承于QTcpServer,重写其中的void incomingConnection(int sockDesc)方法,该方法在有客户端接入时自动调用。

void Server::incomingConnection(int sockDesc)
{
    m_socketList.append(sockDesc);

    serverThread *thread = new serverThread(sockDesc);

    m_dialog->showConnection(sockDesc);

    connect(thread, SIGNAL(disconnectTCP(int)), this, SLOT(clientDisconnected(int)));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

    connect(thread, SIGNAL(dataReady(const QString&, const QByteArray&)),
            m_dialog, SLOT(recvData(const QString&, const QByteArray&)));

    connect(m_dialog, SIGNAL(sendData(int, const QByteArray&)),
            thread, SLOT(sendDataSlot(int, const QByteArray&)));

    thread->start();
}

二、虽然多线程服务器端的例子书上和网上很多(虽然基本一样= =!), 都是简单的时间服务器,只实现简单的发送功能,而且每个客户端发一次就断开了,但是许多时候我们都要使用完整的收发功能。对于发送实现还比较简单只需要根据socketDescriptor和write函数就可以将信息发送到指定的客户端:

void serverThread::sendData(const QString &data, int id)
{
    if (id == socketDescriptor) {
        tso->write(data.toLocal8Bit());
    }
}

接收方面,许多人第一时间就会想到连接readReady()信号,这个时候问题又发生了,经过一番qDebug发现readReady()信号根本就没触发。到这里网上的资料也少了,在许多资料都提到阻塞式接收和waitForReadyRead(),但是具体的都没写了,就一个函数要怎么用啊,多少给个例子呗,然而怎么找都没有。然后我就在Qt文档里找这个函数,居然就发现了一个例子:

int numRead = 0, numReadTotal = 0;
char buffer[50];

forever {
	numRead  = socket.read(buffer, 50);
    // do whatever with array
	numReadTotal += numRead;
    if (numRead == 0 && !socket.waitForReadyRead()) {
    	 break;
    }          
}

果然还是官方的靠谱,赶紧把自己的程序改改,然后就可以接受数据了,然后就没有然后了。

void serverThread::run()
{
    tso = new QTcpSocket;
    if (!tso->setSocketDescriptor(socketDescriptor)) {
        return;
    }

	connect(tso, &QTcpSocket::disconnected, this, &serverThread::disconnectToHost);

    QByteArray data;
    forever {
        data = tso->readAll();
        QString msg = QString::fromLocal8Bit(data);
        if (tso->waitForReadyRead()) {    
            if (msg.length() != 0) {
                msg = tso->peerAddress().toString() + ':'+ msg;
                emit recvData(msg);
            }
        }
    }
}

当然这种方法比较low,而且在实现send的时候会出现一个在线程中新开一个线程的警告,所以为了达到更好的效果,我们可以继承TcpSocket类,在里面实现数据的收发,而且这样也不需要使用到阻塞。

修改后serverThread的部分源码:

void serverThread::run(void)
{
    m_socket = new MySocket(m_sockDesc);

    if (!m_socket->setSocketDescriptor(m_sockDesc)) {
        return ;
    }

    connect(m_socket, &MySocket::disconnected, this, &serverThread::disconnectToHost);
    connect(m_socket, SIGNAL(dataReady(const QString&, const QByteArray&)),
            this, SLOT(recvDataSlot(const QString&, const QByteArray&)));
    connect(this, SIGNAL(sendData(int, const QByteArray&)),
            m_socket, SLOT(sendData(int, const QByteArray&)));

    this->exec();
}

void serverThread::sendDataSlot(int sockDesc, const QByteArray &data)
{
    if (data.isEmpty()) {
        return ;
    }

    emit sendData(sockDesc, data);
}

void serverThread::recvDataSlot(const QString &ip, const QByteArray &data)
{
    emit dataReady(ip, data);
}

这里线程中只是一个信号转发的功能。

在MySocket类中只需要像普通一样实现数据收发就行啦:

MySocket::MySocket(int sockDesc, QObject *parent) :
    QTcpSocket(parent),
    m_sockDesc(sockDesc)
{
    connect(this, SIGNAL(readyRead()), this, SLOT(recvData()));
}

void MySocket::sendData(int id, const QByteArray &data)
{
    if (id == m_sockDesc && !data.isEmpty()) {
        this->write(data);
    }
}

void MySocket::recvData(void)
{
    QString ip = peerAddress().toString().remove(0, 7);
    QByteArray data = readAll();

    emit dataReady(ip, data);
}

程序运行图如下:
这里写图片描述
因为是自己平常调试用,所以端口号是写死了的,需要动态设置端口的同学,就自己多加几个控件,多写几行代码啦。

代码下载,由于CSDN的下载有点坑,请移步GitHub:
https://github.com/DragonPang/QtMultiThreadTcpServer

  • 32
    点赞
  • 242
    收藏
    觉得还不错? 一键收藏
  • 58
    评论
哈哈哈,小白在学校期间的练手作品,很粗糙,很简陋,bug也有,但是对于新手来说还是很具有参考价值的,不喜勿喷,指出问题,共同进步。 项目简介: 1.项目名称:学生信息管理与收发系统(客户端+服务器)-(学生端-服务器-教师端) 2.使用工具:QT Creator 5.6 + Mysql5.6; 3.使用技术:C/S(客户端-服务器)、TCP/IP(协议)、socket、多线程、数据库; 4.项目描述:1)服务器服务器监听一个IP地址,用来连接教师端和学生端,用于数据转发(eg:教师端发消息到服务器,在由服务器发消息到学生端); 2)教师端:教师端的主要功能是选择需要发送的学生(可以发送给不在线学生),输入将要发送给一部分学生的表格名(标题),和1-8个字段名(不能重复,因为数据库中的字段名不能重复),在点击发送后由服务器转发给学生端。在学生端收到消息并且提交消息后可以查询学生的信息和提交的信息,还可以将数据表导出成xls文件。文件发送还没有完成0.0…… 3)学生端:学生端可以编辑个人信息。学生端可以查询收到的并未提交的数据表并且提交信息。(可以收到离线信息)(在线学生收到消息提示后从数据库中查找教师端所发出的数据)(不在线学生在上线后从数据库中查找数据)。文件发送还没有完成0.0…… 5.注意事项:本系统只能用于局域网中的数据传输,并且由于本项目是在学校完成后并没有改动,所以服务器所监听的地址为我本身的地址,在下载后本系统是不可用的。还有就是数据库的问题,数据库是我在花钱买的一个远程服务器上搭建的,所以数据库也是不可用的。因此 1)在拿到本系统的代码时应该修改IP地址(服务器-教师端-学生端)改为你所需要的。 2)在拿到本系统的代码时应该把我所发的数据库加入到你的数据库中,并且修改代码中跟数据库有关的代码。 代码我就不贴了,自己下载看吧。
### 回答1: Qt 中使用多线程连接 TCP 服务器可以通过以下步骤实现: 1. 创建 QTcpSocket 类的对象,并连接到指定的 IP 地址和端口。 2. 创建 QThread 类的对象,并在该线程中运行 QTcpSocket 的对象。 3. 在 QTcpSocket 的对象中处理与服务器的连接,接收和发送数据。 4. 通过信号和槽机制在主线程中处理数据,以实现多线程通信。 希望这个回答对您有所帮助! ### 回答2: Qt Tcp多线程连接服务器通常是为了实现并发处理多个客户端请求的需求。在Qt中,可以使用Qt网络模块提供的QTcpServer和QTcpSocket类实现TCP服务器和客户端的功能,并结合多线程技术来处理多个客户端的连接和请求。 首先,创建一个QTcpServer对象,并使用其listen函数指定服务器的端口号。然后,可以使用QObject的moveToThread函数将QTcpServer对象移动到一个新的线程中,以便实现多线程处理。 在新的线程中,使用QTcpServer的连接信号与槽机制,将新连接的客户端套接字传递给一个自定义的处理类。此处理类继承自QObject,并包含一个QTcpSocket对象作为成员变量,用于与客户端进行通信。 在处理类中,使用QTcpSocket的readyRead信号与槽机制,接收并处理客户端发送的数据。可以使用QObject的moveToThread函数将QTcpSocket对象移动到同一个新的线程中,以便实现多线程处理。 当客户端断开连接时,可以使用QTcpSocket的disconnected信号与槽机制,进行相应的处理。 需要注意的是,在多线程处理中,需要考虑线程间的同步和互斥机制,以避免竞态条件和资源冲突的问题。可以使用Qt提供的互斥量、条件变量等工具类来实现线程间的同步和互斥操作。 总结来说,Qt Tcp多线程连接服务器的步骤包括创建服务器对象、指定端口号、移动服务器对象到新线程,创建处理类对象、将处理类对象移动到同一线程,处理客户端连接请求、接收和处理数据、处理断开连接事件,并注意线程间的同步和互斥机制。 ### 回答3: Qt是一个流行的跨平台C++开发框架,提供了丰富的功能和工具来开发应用程序。其中,Qt提供了一个Tcp模块,用于实现基于Tcp协议的网络通信。而多线程可以使程序能够同时处理多个任务,提高程序的性能和响应速度。 在使用Qt进行Tcp多线程连接服务器时,可以按照以下步骤进行操作: 1. 引入相应的头文件:在需要使用Tcp模块的代码中,首先需要引入相应的头文件,以提供相应的函数和类。 2. 创建Tcp服务器:使用QtQTcpServer类创建一个Tcp服务器对象,然后调用其listen函数指定服务器监听的地址和端口。 3. 实现服务器的连接槽函数:创建一个槽函数来处理客户端的连接请求,在其中可以使用QTcpServer的nextPendingConnection函数获取新的客户端套接字,并进行相关处理。 4. 创建Tcp客户端:使用QtQTcpSocket类创建一个Tcp客户端对象,然后调用其connectToHost函数指定连接的服务器地址和端口。 5. 实现客户端的连接槽函数:创建一个槽函数来处理客户端的连接状态,可以根据连接结果进行相应的处理。 6. 处理服务器收发数据:对于服务器端和客户端,可以使用QTcpSocket类的write函数发送数据,使用readyRead信号和read函数接收数据。 7. 使用多线程:可以使用QtQThread类创建一个线程对象,并将需要多线程处理的任务放入该对象的run函数中,以实现程序的并发处理。 通过以上步骤,可以使用Qt进行Tcp多线程连接服务器的相关开发。在具体实现过程中,还可以根据具体需求进行相应的扩展和优化,以满足应用程序的要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 58
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值