QT4中构建多线程的服务器and QT'Socket

QT4中构建多线程的服务器

出错和经验 2009-04-28 15:43:48 阅读12 评论0   字号: 订阅

QT4中构建多线程的服务器

首先说一下对 多线程这个名词的理解过程。以前听说过很多次多线程这个词,而且往往与 服务器联系起来,因此一直把多线程误解为服务器特有的功能;直到这次 课程设计,仔细 学习了一下多线程的机制,才知道真正的意思。简单的来说,就是同时有多个线程一起运行,而不同的线程可以执行不同的操作。举个例子,一个图像处理工具,可以用鼠标一边移动图像,一边用快捷键缩放图像,此时,移动图像、缩放图像就是不同的线程来处理的,如果不支持多线程而是单线程的,那么只能挨个操作了。

 

而对于服务器来说,多线程的这个特性太有用了,因为多线程使得服务器可能同时响应多个客户端的请求,所以现在服务器大多采用多线程,所以才会造成我开始的误解。

不管是多线程,还是服务器,QT中已经封装好了特定的类,所以使用起来也很方便。

下面建立一个支持多线程、TCP的服务器。

首先建立一个服务器。新建一个类(Server)继承QT中的QTcpServer类即可。服务器的职责是监听端口。当监听到有客户端试图与服务器建立连接的时候,分配socket与客户端连接,再进行数据通信。QTcpServer的listen()方法执行监听过程,可以指定监听的地址和端口。若给定了QHostAddress类型的监听地址,则监听该地址,否则,监听所有地址;若给定了quint16类型的监听端口,则监听该端口,否则,随机选定一个监听端口。

  1. Server * server = new Server;   
  2. if(!server->listen(host,port)){   
  3. ...//error   
  4. }  
Server * server = new Server;if(!server->listen(host,port)){...//error}

QTcpServer有一个虚函数incomingConnection(int socketDescriptor),服务器每当监听到一个客户端试图建立连接的时候,会自动调用这个函数,因此,处理这个请求的过程就可以在这个函数中电影,即在子类Server的定义阶段,重新定义incomingConnection()这个函数。对于一个多线程的服务器,每当客户端试图连接的时候,服务器应该启动一个线程,负责对这个客户端进行服务,所以,incomingConnection()这个函数所要做的就是建立一个线程,而所建立的线程的作用就是对客户端进行服务,而这其中建立socket连接是基础。服务器在监听到客户端试图建立socket连接时,会为此socket分配一个唯一的标识socketDescriptor,这个标识将在服务器端建立socket连接时使用,所以应提供给每一个线程。

在QT中使用多线程,建立一个类(Thread)继承QThread类即可。QThread类也有一个虚函数,这个函数是run(),线程建立并启动(QThread::start())后,就会执行这里面的代码,因此,线程的逻辑过程就应该在run()里面定义。服务器的线程要根据socketDescriptor标识的socket建立连接,然后进行数据通信,所以要将socketDescriptor传入到Thread中,前面说过,线程是在incomingConnection()里面建立,用构造函数将socketDescriptor传入Thread类,再用socketDescriptor建立socket连接。

定义incomingConnection()

  1. void incomingConnection(int socketDescriptor){   
  2.      Thread * thread = new Thread(socketDescriptor);   
  3.     thread->start();   
  4. }  
void incomingConnection(int socketDescriptor){    Thread * thread = new Thread(socketDescriptor);    thread->start();}

定义run()

  1. void run(){   
  2.      QTcpSocket * socket = new QTcpSocket(socketDescriptor);   
  3.      ...//数据通信   
  4. }  
void run(){    QTcpSocket * socket = new QTcpSocket(socketDescriptor);    ...//数据通信}

自此,一个简单的多线程服务器建立完毕。

写的不好,望请指教。

============================
QT4中socket通信

近的软件工程课程设计让我重新开始使用QT,上次数据结构的课程设计也是用QT,虽然是做出来了,但是现在想想,那个时候对QT的理解,或者说得更广一点,对OO的理解,简直太差劲了,当然,人的知识是进步的,所以现在有这样的感受是很正常的。虽然整体的开发工作还没有完全结束,但是已经有了很多心得体会,所以特来记录分享一下。

 

我们的系统采用的是C/S结构,所以客户端与服务器通信是最关键,不幸的是,虽然我们没有用过QT的socket类,我们也没有估计好通信的难度,等到意识到第一次使用的困难时,已经是第5天了,始终没有进展,我临危受命。现在是第6天,刚刚把通信模块封装好,算是对这两天的突击的一个回报。

我们遇到的问题socket已经建立,并且发送端已经将消息发送,但是接收端始终收不到消息。(我用的socket类型是TCP,也就是QTcpSocket类)

发送端(发送端一直不存在问题)代码如下:

  1. ... // 建立连接,客户端和服务器端有区别,在此省略<BR>   
  2. QByteArray block;   
  3. QDataStream out(&block,QIODevice::WriteOnly);  // 写信息至block中,用到QDataStream类<BR>   
  4. socket.write(block);// 信息写完毕,写入socket,由socket发送<BR>   
  5. socket.disconnectFormHost();   
  6. socket.waitForDisconnected();  
... // 建立连接,客户端和服务器端有区别,在此省略
QByteArray block;QDataStream out(&block,QIODevice::WriteOnly); // 写信息至block中,用到QDataStream类
socket.write(block);// 信息写完毕,写入socket,由socket发送
socket.disconnectFormHost();socket.waitForDisconnected();

有问题的接收端代码如下:

  1. ... // 建立连接<BR>   
  2. QDataStream in(&socket);// 接收socket中的数据流<BR>   
  3. ... // 从数据流 in 中读数据  
... // 建立连接
QDataStream in(&socket);// 接收socket中的数据流
... // 从数据流 in 中读数据

以上是最原始的接收和发送端工作过程,调试过程中,分别讲两端的socket的状态打印出来,结果是发送端为A connection is established. 而接收端为The socket has started establishing a connection. 也就是说发送端正确的建立了连接,并将数据写入,而接收端只是正在建立连接,而并没有建立好,所以是根本不会受到数据的。所以先要确保接收端的连接建立好。waitForConnected()方法就可以解决这个问题,它将一直等待直到连接已经建立。

改进后的接收端代码:

  1. ... // 建立连接<BR>   
  2. socket.waitForConnected(5000) // 5000表示等待的时间,默认参数为3000,单位是百万分之一秒   
  3. QDataStream in(&socket);// 接收socket中的数据流<BR>   
  4. ... // 从数据流 in 中读数据  
... // 建立连接
socket.waitForConnected(5000) // 5000表示等待的时间,默认参数为3000,单位是百万分之一秒QDataStream in(&socket);// 接收socket中的数据流
... // 从数据流 in 中读数据

此时,接收端输出的socket状态为A connection is established,连接成功建立。
但是还是收不到信息,参考了一下别人的程序,再比对一下参考手册,原来QTcpSocket的爷爷类(其实是父类QAbstractSocket的父类)QIODevice有一个readyRead的信号(signal),当信息准备好并可以读的时候,这个信号就将发出,也就是说,只有当这个信号发出的时候,才可以读消息。所以要把读消息的动作read作为一个槽(slot),并将其与readyRead信号连接。

  1. connect(&socket,SIGNAL(readyRead()),this,SLOT(read()));  
connect(&socket,SIGNAL(readyRead()),this,SLOT(read()));

但是直接触发socket信号,而不用图形界面的动作来触发一个动作并由这个动作来触发socket信号一直也触发不了read这个动作。但是我要封装成一个接口类提供给上层使用,用图形界面自然是不现实的,于是翻阅了手册,发现了一个QAbstractSocket类的一个方法——waitForReadyRead(),这个方法将一直等待到数据可以读时结束,此时就可以读数据了。方法也很简单:

  1. ... // 建立连接<BR>   
  2. socket.waitForConnected(5000) // 5000表示等待的时间,默认参数为3000,单位是百万分之一秒<BR>   
  3. if(!socket.waitForReadyRead(3000)){//3000为等待时间,没有默认的等待时间,单位是百万分之一秒   
  4. return ;   
  5. }<BR>   
  6. QDataStream in(&socket);// 接收socket中的数据流<BR>   
  7. ... // 从数据流 in 中读数据  
... // 建立连接
socket.waitForConnected(5000) // 5000表示等待的时间,默认参数为3000,单位是百万分之一秒
if(!socket.waitForReadyRead(3000)){//3000为等待时间,没有默认的等待时间,单位是百万分之一秒return ;}
QDataStream in(&socket);// 接收socket中的数据流
... // 从数据流 in 中读数据

这样,数据成功读取出来,实现数据的通信。

单向的数据传输问题解决了,然后再利用单向的数据通信组装成双向的数据通信,这过程中也会遇到不少问题,将在另一篇日志介绍。

========================

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Qt是一种现代化的C++应用程序开发框架,它提供了许多功能强大和易于使用的类库,QtSocket库用于实现网络通信、传输数据等功能。QtSocket库在应用发挥了非常重要的作用,特别是在session服务器的开发,由于该服务器需要同时处理多个客户端的请求,所以使用多线程技术能够让服务器更加高效的处理请求。 具体实现Qt Socket多线程session服务器代码可以使用QThread类来实现多线程QThread是Qt专门用于处理线程的类,可以创建多个线程,并且可以通过信号和槽机制来实现线程的通信。对于session服务器,可以每当一个客户端连接到服务器,使用QThread类创建一个新的线程来处理该客户端的请求。在每个线程,我们可以使用QTcpSocket类来处理客户端的请求,并且使用信号和槽机制来实现线程间的通信。 下面是一个简单的Qt Socket多线程session服务器代码的示例: ```cpp class SessionThread : public QThread { Q_OBJECT public: SessionThread(qintptr socketDescriptor); signals: void error(QTcpSocket::SocketError socketError); protected: void run() override; private: QTcpSocket *m_tcpSocket; qintptr m_socketDescriptor; }; class SessionServer : public QTcpServer { Q_OBJECT public: SessionServer(QObject *parent = nullptr); void startServer(); protected: void incomingConnection(qintptr socketDescriptor) override; signals: void newConnection(qintptr socketDescriptor); }; SessionThread::SessionThread(qintptr socketDescriptor) : m_socketDescriptor(socketDescriptor) { } void SessionThread::run() { m_tcpSocket = new QTcpSocket(); if (!m_tcpSocket->setSocketDescriptor(m_socketDescriptor)) { emit error(m_tcpSocket->error()); return; } connect(m_tcpSocket, &QTcpSocket::readyRead, this, [this]() { QByteArray request = m_tcpSocket->readAll(); // handle request m_tcpSocket->write("processed request"); }); connect(m_tcpSocket, &QTcpSocket::disconnected, m_tcpSocket, &QTcpSocket::deleteLater); exec(); } SessionServer::SessionServer(QObject *parent) : QTcpServer(parent) { } void SessionServer::startServer() { if (!listen(QHostAddress::LocalHost, 5555)) { qDebug() << "error: " << errorString(); } else { qDebug() << "server started"; } } void SessionServer::incomingConnection(qintptr socketDescriptor) { emit newConnection(socketDescriptor); SessionThread *thread = new SessionThread(socketDescriptor); connect(thread, &SessionThread::finished, thread, &SessionThread::deleteLater); connect(thread, &SessionThread::error, this, [thread](QTcpSocket::SocketError error) { qDebug() << "error: " << error; thread->quit(); }); thread->start(); } ``` 在上面的代码,`Session_server`是一个派生自`QTcpServer`的类,用于创建一个TCP服务器。在这个类实现了`incomingConnection()`函数,该函数在客户端开始连接时被调用,该函数我们可以通过创建一个新的线程来处理客户端的请求。在每个线程,我们可以使用QTcpSocket类来处理客户端请求,在线程实现处理请求和发送回应过程。`\ 使用QTsocket库开发多线程session服务器代码时,需要特别注意线程间的同步问题,确保每个线程的请求能够被正确的处理、及时的响应客户端请求。同时,在请求响应过程,需要考虑到服务端的性能问题,精细地设计线程和网络IO操作等细节,保证高效运行和稳定性。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值