Qt 4.6自帶的threaddedfortuneserver是個簡單明瞭的 Qt C/S網絡編程server端程序的例子, 該例子演示了 QTcpServer與QThread配合的方法。 代碼很少, 但包含了Qt網絡編程的幾個關鍵點。編程
- FortuneServer類從QTcpServer派生, 調用QTcpServer::listen() 監聽端口等待client鏈接
- FortuneServer重寫了虛函數 incomingConnection()去接受client鏈接,並建立線程處理該鏈接
- FortuneThread是處理client鏈接的子線程, 在該線程裏向client端寫入數據結構很是簡單。 筆者原本想照着這個架構寫個接收client數據的小server,在寫的過程當中發現了一個頗有意思的問題, 且聽我慢慢道來。
不知道你們有沒有發現, 其實FortuneServer這個類看起來是QTcpServer類的簡單包裝,並無加入新的東西, 筆者就嘗試去掉此子類直接使用QTcpServer。設想的程序架構是這樣的:
- 使用QTcpServer監聽端口等待client鏈接
- 在收到QTcpServer::newConnection信號時調用nextPendingConnection得到socket 鏈接, 將socket 鏈接的fd傳送給子線程
- FortuneThread是處理client鏈接的子線程,獲得鏈接的fd後建立一個QTcpSocket並用QTcpSocket::setSocketDescriptor,這樣就能夠用QTcpSocket的方法來監控fd的動向了。這裏咱們用QTcpSocket::waitForReadyRead等待client端發來的數據爲了獲得與client的鏈接的socket fd, 調用了QTcpServer::nextPendingConnection()方法得到一個QTcpSocket指針從該指針獲得鏈接的fd, 再將該fd傳送給子線程去處理。 看上去與原來的程序沒什麼區別, 但運行起來卻發生了奇怪的問題,那就是有時server的waitForReadyRead返回true時卻讀不到數據(bytesAvailable() = 0)彷佛client發來的數據丟了同樣。 真是讓人百思不得其解。網絡
問題出了nextPendingConnection上。仔細回想一下咱們的程序的架構,在server進程裏調用nextPendingConnection得到一個QTcpSocket的指針,將此指針內的fd信息發送給子進程由子進程負責與client通信。你們再想一想QTcpSocket提供了那麼多的API包括signal等, 這意味着什麼?確定Qt在底層對fd進行了監控啊,也就是說在咱們的程序裏出現了兩個QTcpSocket分別在兩個線程裏對同一個fd進行了監控和操做,因此出現一些奇怪的現象也就不算奇怪了。 若是你們嘗試對主線程的QTcpSocket進行處理就會發現,所謂「丟失」的數據均可以在這個socket裏獲得, 即有一部分socket的數據因爲線程切換的關係由主線程的socket截獲了。爲了解決這個問題固然最好的辦法仍是沿用例子中的架構, 對QTcpServer進行派生,由於在incomingConnection的參數裏能夠直接獲得fd,此時尚未建立QTcpSocket對此fd作任何操做, 是個乾淨的狀態, 不會有任何衝突;另外還有一個辦法是在不改變現有程序架構的狀況下把這兩個QTcpSocket搬到同一個線程裏。這樣也不會出現兩個線程同時訪問一個fd的狀況。 具體是使用 QObject::moveToThread方法。 須要注意的是文檔中對moveToThread有個說明, 有parent的object是不能被移動到其餘線程中的,因此還須要把QTcpSocket給setParent(NULL)一下再moveToThread.
通過實驗, 第二種方法也能夠很好的工做。數據結構
jerry建議從QTcpServer派生(固然對象能夠經過 QObject::moveToThread方法移到其它線程中),不建議第二種方法。架構