1. 程式人生 > >QTcpSocket-Qt使用Tcp通訊實現服務端和客戶端

QTcpSocket-Qt使用Tcp通訊實現服務端和客戶端

超過 amp 方式 all trac tro article ada 內部

版權聲明:若無來源註明,Techie亮博客文章均為原創。 轉載請以鏈接形式標明本文標題和地址:
本文標題:QTcpSocket-Qt使用Tcp通訊實現服務端和客戶端 本文地址:http://techieliang.com/2017/12/530/ 文章目錄
  • 1. 基本功能
  •  1.1. pro文件配置
  •  1.2. QTcpServer服務端建立
  •  1.3. 客戶端建立
  •  1.4. 消息收發
  • 2. 其他
  •  2.1. 實現單服務器多客戶端通訊
  •  2.2. 關於QTcpServer
  •  2.3. 關於數據收發

1. 基本功能

詳細說明請見官方文檔

1.1. pro文件配置

使用Qt網絡功能需要在pro文件增加網絡庫

  1. QT += network

1.2. QTcpServer服務端建立

  1. QTcpServer server = new QTcpServer();
  2. connect(server,
  3. &QTcpServer::newConnection,
  4. this,
  5. &MainWindow::server_New_Connect);//監聽
  6. if(!server->listen(QHostAddress::Any, 8000)) {
  7. qDebug()<<server->errorString(); //錯誤信息
  8. }

創建server對象以後首先要監聽客戶端的連接,通過listen函數可以開啟監聽,需要指定監聽的ip和端口號,ip可使用QHostAddress::Any

QTcpServer當有新客戶端連接時會發出QTcpServer::newConnection的信號,只需要關聯到自定義的槽即可。

  1. void MainWindow::server_New_Connect() {
  2. //獲取客戶端連接
  3. auto socket_temp = server->nextPendingConnection();//根據當前新連接創建一個QTepSocket
  4. m_socket=socket_temp;//記錄此連接用於後續數據讀寫
  5. //連接QTcpSocket的信號槽,以讀取新數據
  6. QObject::connect(socket_temp, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);
  7. //當斷開連接時發出的信號
  8. QObject::connect(socket_temp, &QTcpSocket::disconnected, this, &MainWindow::socket_Disconnected);
  9. }

上述為一個新連接到來的槽函數範例,利用nextPendingConnection獲取到新連接的socket,存儲此socket,並關聯對應的信號

主要有兩個:接收到新數據的信號以及連接斷開的信號。

當連接斷開可以通過響應信號槽機制實現斷開後的操作。

1.3. 客戶端建立

客戶端為主動連接方,不需要監聽,直接建立QTcpSocket即可

  1. m_socket = new QTcpSocket;
  2. m_socket->connectToHost("127.0.0.1",80100,QTcpSocket::ReadWrite);
  3. connect(m_socket,SIGNAL(connected()),this,SLOT(connected()));

上述例子使用的是信號槽方式等待連接成功,也可以使用阻塞方式:waitForConnected,等到連接成功才會執行後續代碼,不需要建立新的槽函數。

通過connectToHost連接指定ip和端口,同時將socket的連接成功的信號與對應槽連接,當連接成功可以將自定義的標記位置為true,可進行相應的收發。

  1. void MainWindow::connected() {
  2. m_is_connected = true;
  3. connect(this->socket,SIGNAL(readyRead()),this,SLOT(readyread())); //連接接收消息槽
  4. QObject::connect(socket_temp,?&QTcpSocket::disconnected,?this,?&MainWindow::socket_Disconnected);//斷開連接
  5. }

當連接成功建議將接收和斷開連接的信號進行connect

1.4. 消息收發

  • 不阻塞收發:

無論客戶端還是服務端只有在建立連接時有差異,後續的消息收發都相同。

首先通過QTcpSocket::close()可以主動斷開連接,無論客戶端服務端都可以執行主動斷開

通過readyRead()信號可以在接到信息後進行信息操作,在槽中執行QTcpSocket::readAll()可以讀取緩沖區所有數據

QTcpSocket::send()可發送信息,調用flush可立即發送緩沖區的數據,不需等待。

  • 阻塞收發:

Qt同時提供了阻塞收發及連接、斷開連接的函數:

virtual bool waitForConnected(int msecs = 30000)
virtual bool waitForDisconnected(int msecs = 30000)
virtual bool waitForBytesWritten(int msecs = 30000)
virtual bool waitForReadyRead(int msecs = 30000)

通過上述函數可以實現阻塞連接、斷開連接、發送、接收數據內容

2. 其他

2.1. 實現單服務器多客戶端通訊

網上大部分例子都是單服務器通訊,若不做修改連接多個客戶端,會出現只有最後一個通訊有效的情況。

主要原因是在監聽到新連接時的處理方式不當:

  1. connect(server, &QTcpServer::newConnection, this, &MainWindow::server_New_Connect);//監聽
  2. if(!server->listen(QHostAddress::Any, 8000)) {
  3. qDebug()<<server->errorString(); //錯誤信息
  4. }

註意上述代碼在服務端收到信連接時會固定的調用一個槽函數,而槽函數往往寫成下述樣式:

  1. void MainWindow::server_New_Connect() {
  2. //獲取客戶端連接
  3. auto socket_temp = server->nextPendingConnection();//根據當前新連接創建一個QTepSocket
  4. m_socket=socket_temp;//記錄此連接用於後續數據讀寫
  5. //連接QTcpSocket的信號槽,以讀取新數據
  6. QObject::connect(socket_temp, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);
  7. //當斷開連接時發出的信號
  8. QObject::connect(socket_temp, &QTcpSocket::disconnected, this, &MainWindow::socket_Disconnected);
  9. }

m_socket=socket_temp;//記錄此連接用於後續數據讀寫

這一行等於是每次有一個新的連接都替換了舊的連接記錄,自然之友最後一個客戶端連接有效,正確的可以建立list存儲所有連接的socket,當收發數據時根據需要指定socket進行收發。這時將disconnected信號進行connect就具有了作用,當某個連接斷開時應該從所有連接鏈表中刪除此記錄。

由nextPendingConnection創建的QTcpSocket,會有QTcpServer維護,當QTcpServer銷毀是會自動銷毀所有創建的socket,若想提前釋放內容可以在disconnected信號發生時主動delete

2.2. 關於QTcpServer

可能考慮到跨平臺問題,Qt使用select實現io多路復用,連接數量限制是1024,若需要poll,epoll等可使用其他庫,比如libevent

2.3. 關於數據收發

可以通過setReadBufferSize設置接收緩沖區大小(Qt內部緩沖區大小)

當發送端發送的數據超過buffer大小時會觸發readyread信號,需要註意對此情況的處理方法,可以考慮在消息頭增加消息長度

未驗證:Qt有自己內部的緩沖區,消息發送到系統緩沖區,Qt會讀取出來,而調用的Qt函數readall等實際上讀取的是Qt內部緩沖區而非系統緩沖區

QTcpSocket-Qt使用Tcp通訊實現服務端和客戶端