QT下多线程调用TCP的问题及可能的解决方案

更新时间:2023-06-30 10:53:10 阅读: 评论:0

QT下多线程调⽤TCP的问题及可能的解决⽅案
背景:在上⼀篇博⽂中结尾时,提到QT下所有IO类都不允许跨线程调⽤,这极⼤增加了编程难度。本⽂接着上⼀篇,主要讨论当套接字与UI线程不在同⼀线程时,如何使TCP的收发实时。
1. 能否跨线程调⽤TCP套接字?
研究生考点
  对于TCP通信,⼀个常见的操作就是读写分开,即读写分别在不同线程中执⾏,这样实现实时全双⼯通信,那么在QT中能否实现读写线程分开呢?理论上将是不可以的,但是实际操作发现能实现(会有错误警告)。
  这涉及到信号和槽的连接⽅式。通常QT的信号和槽有三种常⽤的连接⽅式:
(1) Qt::AutoConnection:QT默认连接⽅式。当信号接收⽅与信号发送⽅在同⼀线程时,等价于Qt::DirectConnection;否则等价于Qt::QueuedConnection。
(2) Qt::DirectConnection: 当信号被发送后,槽函数⽴即执⾏。这对于实时通信意义重⼤,如UI界⾯发送TCP消息之后,需要实时等待TCP响应以进⾏不同的操作,这种连接⽅式就能保证TCP能⽴即发送消息。
(3) Qt::QueuedConnection: 当信号发送时,对应的槽函数加⼊到槽函数所在线程事件处理队列中,等待执⾏。
对于Qt::DirectConnection连接,有⼀个默认属性:当信号和槽不在同⼀个线程时,槽函数不会在槽函数所在线程执⾏,⽽是会在在信号发送的线程执⾏(具体细节见QT官⽅⽂档)。这就为不同线程调⽤TCP套接字提供了可能,以上⼀篇(链接见开题头)的程序为基础,⼜添加了TCP发送功能,点击按钮,需要将对应消息实时发送出去,界⾯如下:
  其他部分不变,在上⼀篇⽂章的基础上添加了输⼊框和发送按钮。由于TCP所在线程主循环⼀直调⽤TCP接收消息,所以点击按钮发送消息时,⼦线程⼀直忙碌,并不会响应,所以此时我希望UI线程来发送消息。对上⼀篇⽂章TcpMoveToThread类构造函数稍加修改:
成都培训公司1 TcpMoveToThread::TcpMoveToThread(QObject* parent)
2 {
3    veToThread(&m_thread); //加⼊到⼦线程
4
cdb5    connect(&m_thread,&QThread::started,&m_tcp,&TcpModel::tcpWork); //⼀旦线程开始,就调⽤接收Tcp的函数
6    connect(&m_tcp,&TcpModel::dataRecved,this,&TcpMoveToThread::dataChangedSlot);
7    connect(&m_thread,&QThread::finished,&m_tcp,&TcpModel::tcpClo); //线程结束时关闭socket,删除申请内存
8
9//直接连接槽函数
10    connect(this,&TcpMoveToThread::nddataSignal,&m_tcp,&TcpModel::tcpSendMsgSlot,Qt::DirectConnection);
11
12    m_thread.start(); //开启⼦线程
13 }
红⾊部分是修改部分,即采⽤了Qt::DirectConnection。由于信号发送⽅在UI线程,接收⽅在TCP⼦线程,所以此时调⽤的槽函数不会在⼦线程中执⾏,⽽是直接在UI线程执⾏,这样收发在不同线程,都能实时响应。这种做法虽然没有影响收发效果,但是每次会提⽰Socket notifiers cannot be enabled or disabled from another thread,从提⽰结果就能看出这是跨线程调⽤TCP造成的。这种提⽰的后果未知(因为程序仍能正常运⾏)。
2. 如何优化设计
  虽然1所介绍的⽅案能实现功能,但是毕竟出现错误提⽰,不知道会造成何种后果。所以最好还是不采⽤以上⽅式。那么对于TCP的收发,如何做到实时响应呢?
  造成1中的程序原因是在TCP⼦线程⼀直循环接收TCP数据,这种操作是⾮常不明智的。为了让TCP⼦线程空闲,只在收发数据时运⾏,可以将TCP接收也改为信号和槽的形式。TCP 收到消息时,会发送信号QTcpSocket::readyRead,通过该信号与TCP接收绑定,就不⽤在⼦线程中循环调⽤TCP接收函数了。改造后的tcpmodel如下:
1//tcpmodel.h
2
3 #ifndef TCPMODEL_H
4#define TCPMODEL_H
5 #include <QTcpSocket>
6 #include <QThread>
7 #include <QTimer>
8
9class TcpModel:public QObject
10 {
11    Q_OBJECT
12public:
13    TcpModel(QObject* parent=nullptr);
14
橘子的英文
15    QString ndmsg;
16
17 signals:
education in china
18void dataRecved(QString data);  //通知TcpMoveToThread类,数据接收
19
20public slots:
21void tcpWork();
22void tcpClo();
23void tcpSendMsgSlot(QString msg);
24void tcpRecvSlot();  //接收消息的槽函数
25
26
27private:
28    QTcpSocket* m_socket;
29    QString msg;
30 };
31
32//将TcpModel在QML初始化时移⼊到⼦线程
33class TcpMoveToThread: public QObject
34 {
35    Q_OBJECT
36    Q_PROPERTY(QString m_data MEMBER m_data)
37public:
38    TcpMoveToThread(QObject* parent=nullptr);
39    ~TcpMoveToThread();
40
41 signals:
42void dataChanged();  //⽤于通知QML应⽤,数据接收到
43void nddataSignal(QString msg);  //发送数据的信号
44
45
46public slots:
47void dataChangedSlot(QString msg);
48我的歌声里英文版
49
50private:
51    QThread m_thread;
52    TcpModel m_tcp;
53    QString m_data;  //保存接收数据
54 };
日语就业前景55
56#endif// TCPMODEL_H
57
58
59//tcpmodel.cpp
60 #include "tcpmodel.h"
61 #include <QObject>
62
63 TcpModel::TcpModel(QObject* parent)
64 {
65 }
campus66
67void TcpModel::tcpWork()
英文缩写大全68 {
69    m_socket=new QTcpSocket();
70    m_socket->connectToHost("127.0.0.1",8000);
71connect(m_socket,&QTcpSocket::readyRead,this,&TcpModel::tcpRecvSlot);//连接TCP接收槽函数
72 }
南京电脑
73
74void TcpModel::tcpClo()
75 {
76    m_socket->clo();
77delete m_socket;
78 }
79
80void TcpModel::tcpSendMsgSlot(QString msg)
81 {
82    m_socket->Local8Bit(),Local8Bit().length());
83m_socket->waitForBytesWritten();
84 }
85
86void TcpModel::tcpRecvSlot()
87 {
88if(m_socket->bytesAvailable()>0)
89    {
90        QByteArray res=m_socket->readAll();
91        msg=QString::fromLocal8Bit(res.data());
92        emit dataRecved(msg); //接收完成信号
93    }
94 }
95
96
97
98 TcpMoveToThread::TcpMoveToThread(QObject* parent)
99 {
100    veToThread(&m_thread); //加⼊到⼦线程
101
102    connect(&m_thread,&QThread::started,&m_tcp,&TcpModel::tcpWork); //⼀旦线程开始,就调⽤接收Tcp的函数103    connect(&m_tcp,&TcpModel::dataRecved,this,&TcpMoveToThread::dataChangedSlot);
104    connect(&m_thread,&QThread::finished,&m_tcp,&TcpModel::tcpClo); //线程结束时关闭socket,删除申请内存105
106//直接连接槽函数,功能正常,但提⽰Socket notifiers cannot be enabled or disabled from another thread
107    connect(this,&TcpMoveToThread::nddataSignal,&m_tcp,&TcpModel::tcpSendMsgSlot);
108
109    m_thread.start(); //开启⼦线程
110 }
111
112 TcpMoveToThread::~TcpMoveToThread()
113 {
114    it();
115    m_thread.wait();
116 }
117
118void TcpMoveToThread::dataChangedSlot(QString msg)
119 {
120    m_data=msg;
121    emit dataChanged();
122 }
主要改动都⽤红⾊标出了,特别注意:
第83⾏:QT的TCP通信默认都是异步的,所以即时调⽤了write和read函数,也可能不会⽴即进⾏读写,表现在程序中就是能⽴即执⾏槽函数,但TCP收发有明显延迟(可以将83注释掉,再看现象)。所以为了将异步改为同步,QT的TCP规定了以下函数:
waitForConnected() 等待链接的建⽴
waitForReadyRead() 等待新数据的到来
waitForBytesWritten() 等待数据写⼊socket
waitForDisconnected() 等待链接断开

本文发布于:2023-06-30 10:53:10,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/78/1070060.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:线程   函数   接收   发送   信号
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图