1. 程式人生 > >關於QT UDP組播的幾個問題

關於QT UDP組播的幾個問題

UDP組播時最好不要提客戶端/服務端的概念,而是提傳送端/接收端的概念,避免出現邏輯理解混亂。傳送端也需要接收,實際使用的過程中還是得根據業務提服務端/客戶端。組播時A端和B端,都可能收發,把它們都加入組播組就可以了,能夠達到既能接收也能傳送的要求。

注意:如果A端和B端,在同一臺機器上,應當注意bind時的埠衝突。

大家在使用QT UDP時,可能會出現下面的情況:

1.接收不到資料

一般我們會這樣寫:

m_udpSocket = new QUdpSocket();
m_udpSocket->bind(QHostAddress::AnyIPv4,8083,QUdpSocket::ShareAddress
); m_udpSocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption,0); m_udpSocket->joinMulticastGroup(m_McastAddr); connect(m_udpSocket,SIGNAL(readyRead()),this ,SLOT(processPendingDatagrams()));

在接收端加入語句

m_udpSocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption,0);

代表禁止本機迴環接收,自發自收是不能實現的,值應當改為1。
bind Ip時使用QHostAddress::AnyIPv4是必要的。

2.還是接收不到資料

此時應當考慮網絡卡是否選對,如果選擇了類似loopback_1這樣的網絡卡,就只能在本機(127.0.0.1)上收發了,如果A端和B端分別在兩臺機器上,除了兩臺機器都要在一個網段(如192.168.9.0)中,還要選擇在這個網段的網絡卡,因為現在的機器一般都有有線和無線網絡卡。通過QNetworkInterface可以檢視活躍的且支援組播的網絡卡。

QNetworkInterface(name = "wireless_0",flags = IsUp IsRunning CanBroadcast CanMulticast)

以上代表一個無線網絡卡可用。像下面這樣設定

udp->setMulticastInterface(intf);

另外如果A端和B端分別處於2個不同的網段中,也就是說組播要跨越路由器,組播是可以跨網段的,需要IGMP協議支援,但是本次我的測試,沒有成功,沒有路由器的訪問許可權,也就放棄了。我想應當需要到路由器上去設定,檢視啟用IGMP協議的介面

命令show ip igmp interface //這個是來自網上有待驗證

3.readyRead觸發後,忘了讀導致的問題

我在測試時,槽函式中只寫了列印資訊,沒有呼叫readDatagram讀取資料,A端連續傳送資料,B端的readyRead訊號只丟擲了一次,而後無論A端發多少次,B端始終不拋readyRead訊號了。

這是因為QT的socket在丟擲readyRead之前,會將資料讀取到自己的緩衝區中,如果A端一直髮,而B端並不從槽函式中讀取資料,那麼Qt就會不斷的從系統緩衝區中讀出資料放置自己內部緩衝區,最後肯定會出現堆疊滿的情況,系統異常退出。

同時,第一次資料到來,觸發了readyread訊號,如果readyRead的槽函式沒有來得及執行,而新的資料又來了,也許來了很多次(在QTcpSocket快取沒有滿的情況下,滿的情況下系統不會再發資料給應用),那麼,都將會只再觸發一次readyRead訊號。

4.關於readDatagram方法

該方法用於讀取UDP資料報,第2個引數是讀取多少資料(或者說叫buffer大小),如果這個值小於QUdpSocket緩衝區中可讀資料的長度,那麼剩下沒有讀取完的資料,將會被丟棄,需要格外注意。

5.Windows能接收,Linux接收不到

以中標麒麟舉例,它的防火牆中預設情況下,我們設定的埠號,是沒有許可權的,所以要在防火牆中放開限制;那麼其他Linux作業系統可能也是這種問題。

題外話:connectToHost方法在QUdpSocket中的問題

如果QUdpSocket的物件不呼叫connectToHost的方法,連線一個對等的使用者(peer),將無法使用write/read等標準IO函式,只能使用readDatagram/writeDatagram方法。但是請注意connectToHost給一個無法ping通的IP地址,仍然能夠丟擲connected訊號和hostFound訊號,這跟Tcp很不一樣。

題外話:編譯錯誤,報socket在不同的執行緒使用

一般應用中我們會線上程中接收資料,然後用另一個執行緒解析資料,以達到防止主執行緒卡死的問題;無論是繼承QThread過載run函式;還是將接收物件moveToThread,都需要注意是在主執行緒還是在子執行緒中建立的QUdpSocket;

因為QUdpSocket是不支援跨執行緒使用的,如果首先接收物件建構函式中建立QUdpSocket,然後再將接收物件moveToThread,就會出現這個問題,因為此時執行緒還沒有啟動,你就已經new了QUdpSocket了。所以要等到執行緒的started()訊號發出後,再建立QUdpSocket。