Qt封裝QTcpServer參考資料--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. }  

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. }  

定義run()

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

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

寫的不好,望請指教。

============================
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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM