TCP通信,要區分服務端,和客戶端。
服務端的功能是,設置要監聽的端口,然後等待客戶端發起的連接。
QT中TCP服務器端的編程步驟:
(1)new一個TCP服務器 QTcpServer
(2)進入listen監聽狀態(形參爲:要監聽的IP,一般填Any, 要監聽的端口號)
(3)一旦有客戶端申請接入,服務端會產生消息QTcpServer::newConnection,在消息的處理函數中,記錄下客戶端發起的socket連接QTcpSocket(一般要用鏈表數據結構記錄,因爲可能會有多個客戶端要接入)。
(4)把剛纔記錄下的socket的【已連接】、【連接已斷開】、【收到數據】3個常用的信號槽寫好。(這一步驟和客戶端編程是一樣的,請參考我的另一篇博客《QT socket TCP通信程序模板- 客戶端》)
(5)發送數據用QTcpSocket::write(uint8_t *bug, int65_t maxLen);
示例代碼:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QMap>
#include <QByteArray>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_ptnCreateTcp_clicked();
void on_ptnSend_clicked();
void when_tcp_newConnection(void);
void when_tcpClientDisconnected(void);
void when_receivedData(void);
private:
Ui::MainWindow *ui;
QTcpServer *tcpServer;
QList<QTcpSocket*> tcpSktList;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#define debug_print(str) ui->textEditDebug->append(str)
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
tcpServer = new QTcpServer();
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(when_tcp_newConnection()));
for(int i = 0; i < ui->tableClients->rowCount(); i++)
{
ui->tableClients->setItem(i, 0, new QTableWidgetItem("無IP"));
ui->tableClients->setItem(i, 1, new QTableWidgetItem("無端口"));
ui->tableClients->setItem(i, 2, new QTableWidgetItem("未連接"));
}
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_ptnCreateTcp_clicked()//TCP服務器進入監聽狀態
{
uint16_t port = ui->spinPort->value();
tcpServer->close();//若listen時,想更換監聽的端口號,必須先close掉
tcpServer->listen(QHostAddress::Any, port);
debug_print(QString("正在監聽本機IP的第%1端口").arg(port));
}
void MainWindow::on_ptnSend_clicked()
{
}
void MainWindow::when_tcp_newConnection()
{
debug_print("有TCP客戶端請求連接");
QTcpSocket *tcpSocket = tcpServer->nextPendingConnection();//新的客戶端發起的連接
QHostAddress clientIp = tcpSocket->peerAddress();//客戶端的IP
quint16 port = tcpSocket->peerPort();//客戶端的端口
if(tcpSktList.contains(tcpSocket))
{
debug_print(QString("%1:%2的連接早已建立過").arg(clientIp.toString()).arg(port));
}
else
{
debug_print("新的連接已建立");
tcpSktList.append(tcpSocket);//記錄下客戶端發起的連接
connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(when_tcpClientDisconnected()));//客戶端掉線處理
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(when_receivedData()));//客戶端發來的數據處理
}
// ui->tableClients->item(0,0)->setText(clientIp.toString());//
// ui->tableClients->item(0,1)->setText(QString::number(port));
}
void MainWindow::when_tcpClientDisconnected(void)
{
debug_print("tcp客戶端掉線");
QTcpSocket *tcpSocket = static_cast<QTcpSocket *>( QObject::sender() );
tcpSktList.removeOne(tcpSocket);
}
void MainWindow::when_receivedData()
{
QTcpSocket *tcpSocket = static_cast<QTcpSocket *>( QObject::sender() );
qDebug() <<QString("收到來自客戶端%1的消息:").arg(tcpSocket->peerAddress().toString());
QByteArray ba = tcpSocket->readAll();
qDebug() << ba;//打印收到的來自客戶端的消息
}
服務器端的TCP程序,不需要本機的IP地址,listen時,只填端口號即可。