QTcpSocket-Qt使用Tcp通訊實現服務端和客戶端
本文標題: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文件增加網絡庫
- QT += network
1.2. QTcpServer服務端建立
- QTcpServer server = new QTcpServer();
- connect(server,
- &QTcpServer::newConnection,
- this,
- &MainWindow::server_New_Connect);//監聽
- if(!server->listen(QHostAddress::Any, 8000)) {
- qDebug()<<server->errorString(); //錯誤信息
- }
創建server對象以後首先要監聽客戶端的連接,通過listen函數可以開啟監聽,需要指定監聽的ip和端口號,ip可使用QHostAddress::Any
QTcpServer當有新客戶端連接時會發出QTcpServer::newConnection的信號,只需要關聯到自定義的槽即可。
- void MainWindow::server_New_Connect() {
- //獲取客戶端連接
- auto socket_temp = server->nextPendingConnection();//根據當前新連接創建一個QTepSocket
- m_socket=socket_temp;//記錄此連接用於後續數據讀寫
- //連接QTcpSocket的信號槽,以讀取新數據
- QObject::connect(socket_temp, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);
- //當斷開連接時發出的信號
- QObject::connect(socket_temp, &QTcpSocket::disconnected, this, &MainWindow::socket_Disconnected);
- }
上述為一個新連接到來的槽函數範例,利用nextPendingConnection獲取到新連接的socket,存儲此socket,並關聯對應的信號
主要有兩個:接收到新數據的信號以及連接斷開的信號。
當連接斷開可以通過響應信號槽機制實現斷開後的操作。
1.3. 客戶端建立
客戶端為主動連接方,不需要監聽,直接建立QTcpSocket即可
- m_socket = new QTcpSocket;
- m_socket->connectToHost("127.0.0.1",80100,QTcpSocket::ReadWrite);
- connect(m_socket,SIGNAL(connected()),this,SLOT(connected()));
上述例子使用的是信號槽方式等待連接成功,也可以使用阻塞方式:waitForConnected,等到連接成功才會執行後續代碼,不需要建立新的槽函數。
通過connectToHost連接指定ip和端口,同時將socket的連接成功的信號與對應槽連接,當連接成功可以將自定義的標記位置為true,可進行相應的收發。
- void MainWindow::connected() {
- m_is_connected = true;
- connect(this->socket,SIGNAL(readyRead()),this,SLOT(readyread())); //連接接收消息槽
- QObject::connect(socket_temp,?&QTcpSocket::disconnected,?this,?&MainWindow::socket_Disconnected);//斷開連接
- }
當連接成功建議將接收和斷開連接的信號進行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. 實現單服務器多客戶端通訊
網上大部分例子都是單服務器通訊,若不做修改連接多個客戶端,會出現只有最後一個通訊有效的情況。
主要原因是在監聽到新連接時的處理方式不當:
- connect(server, &QTcpServer::newConnection, this, &MainWindow::server_New_Connect);//監聽
- if(!server->listen(QHostAddress::Any, 8000)) {
- qDebug()<<server->errorString(); //錯誤信息
- }
註意上述代碼在服務端收到信連接時會固定的調用一個槽函數,而槽函數往往寫成下述樣式:
- void MainWindow::server_New_Connect() {
- //獲取客戶端連接
- auto socket_temp = server->nextPendingConnection();//根據當前新連接創建一個QTepSocket
- m_socket=socket_temp;//記錄此連接用於後續數據讀寫
- //連接QTcpSocket的信號槽,以讀取新數據
- QObject::connect(socket_temp, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);
- //當斷開連接時發出的信號
- QObject::connect(socket_temp, &QTcpSocket::disconnected, this, &MainWindow::socket_Disconnected);
- }
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通訊實現服務端和客戶端