Qt封裝QTcpServer參考資料--QT4中構建多線程的服務器

首先說一下對 多線程這個名詞的理解過程。之前據說過不少次多線程這個詞,並且每每與 服務器聯繫起來,所以一直把多線程誤解爲服務器特有的功能;直到此次 課程設計,仔細 學習了一下多線程的機制,才知道真正的意思。簡單的來講,就是同時有多個線程一塊兒運行,而不一樣的線程能夠執行不一樣的操做。舉個例子,一個圖像處理工具,能夠用鼠標一邊移動圖像,一邊用快捷鍵縮放圖像,此時,移動圖像、縮放圖像就是不一樣的線程來處理的,若是不支持多線程而是單線程的,那麼只能挨個操做了。

 

而對於服務器來講,多線程的這個特性太有用了,由於多線程使得服務器可能同時響應多個客戶端的請求,因此如今服務器大多采用多線程,因此纔會形成我開始的誤解。html

不論是多線程,仍是服務器,QT中已經封裝好了特定的類,因此使用起來也很方便。服務器

下面創建一個支持多線程、TCP的服務器。數據結構

首先創建一個服務器。新建一個類(Server)繼承QT中的QTcpServer類便可。服務器的職責是監聽端口。當監聽到有客戶端試圖與服務器創建鏈接的時候,分配socket與客戶端鏈接,再進行數據通訊。QTcpServer的listen()方法執行監聽過程,能夠指定監聽的地址和端口。若給定了QHostAddress類型的監聽地址,則監聽該地址,不然,監聽全部地址;若給定了quint16類型的監聽端口,則監聽該端口,不然,隨機選定一個監聽端口。多線程

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

QTcpServer有一個虛函數incomingConnection(int socketDescriptor),服務器每當監聽到一個客戶端試圖創建鏈接的時候,會自動調用這個函數,所以,處理這個請求的過程就能夠在這個函數中定義,即在子類Server的定義階段,從新定義incomingConnection()這個函數。對於一個多線程的服務器,每當客戶端試圖鏈接的時候,服務器應該啓動一個線程,負責對這個客戶端進行服務,因此,incomingConnection()這個函數所要作的就是創建一個線程,而所創建的線程的做用就是對客戶端進行服務,而這其中創建socket鏈接是基礎。服務器在監聽到客戶端試圖創建socket鏈接時,會爲此socket分配一個惟一的標識socketDescriptor,這個標識將在服務器端創建socket鏈接時使用,因此應提供給每個線程。socket

在QT中使用多線程,創建一個類(Thread)繼承QThread類便可。QThread類也有一個虛函數,這個函數是run(),線程創建並啓動(QThread::start())後,就會執行這裏面的代碼,所以,線程的邏輯過程就應該在run()裏面定義。服務器的線程要根據socketDescriptor標識的socket創建鏈接,而後進行數據通訊,因此要將socketDescriptor傳入到Thread中,前面說過,線程是在incomingConnection()裏面創建,用構造函數將socketDescriptor傳入Thread類,再用socketDescriptor創建socket鏈接。tcp

定義incomingConnection()函數

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

定義run()工具

  1. void run(){   
  2.      QTcpSocket * socket = new QTcpSocket(socketDescriptor);   
  3.      ...//數據通訊   
  4. }  

自此,一個簡單的多線程服務器創建完畢。學習

寫的很差,望請指教。ui

============================
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();  

有問題的接收端代碼以下:

  1. ... // 創建鏈接<BR>   
  2. QDataStream in(&socket);// 接收socket中的數據流<BR>   
  3. ... // 從數據流 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狀態爲A connection is established,鏈接成功創建。
可是仍是收不到信息,參考了一下別人的程序,再比對一下參考手冊,原來QTcpSocket的爺爺類(實際上是父類QAbstractSocket的父類)QIODevice有一個readyRead的信號(signal),當信息準備好並能夠讀的時候,這個信號就將發出,也就是說,只有當這個信號發出的時候,才能夠讀消息。因此要把讀消息的動做read做爲一個槽(slot),並將其與readyRead信號鏈接。

  1. 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 中讀數據  

這樣,數據成功讀取出來,實現數據的通訊。

單向的數據傳輸問題解決了,而後再利用單向的數據通訊組裝成雙向的數據通訊,這過程當中也會遇到很多問題,將在另外一篇日誌介紹。

轉自:https://blog.csdn.net/dongfangyu/article/details/5919789

jerry在我本身編寫qt4.8 多線程tcp服務端的時候未參考,後來看到的,還不錯,供你們參考,重要的我標紅了,其實原理都是相同的,就是多線程我習慣用movetothread。

相關文章
相關標籤/搜索