使用QT TCP 相关网络库的时候,需要在工程文件内添加network
QT += network
1. TCP 服务端
使用tcp服务端的主要相关步骤如下:
1.1 声明QTcpServer与QTcpSocket
QTcpServer是QT中对Tcp服务端的封装,主要有以下功能:
- 绑定IP和端口
- 处理客户端连接/断开
QTcpSocket是QT中用来对socket进行操作的api,主要有以下功能:
- 读取数据
- 写入数据
在.h文件中声明指针
// .h 文件
#include <QTcpServer>
#include <QTcpSocket>
#include <QMap>
// ....
private:
QTcpServer *tcpServer;
QMap<QString, QTcpSocket*> tcpClient;
QTcpSocket *currentClient;
int cnt;
// ....
声明中:
- tcpServer:用来保存QTcpServer对象的指针。
- tcpClient:是一个map表,保存了所有与客户端连接后新建的socket对象。为了在多连接的时候能够区分不同的对象,将每个QTcpSocket的ip和socket指针一起保存在QMap中。
- currentClient:是当前使用的QTcpSocket对象指针。
多连接的处理逻辑是:
- 有新连接来后,将ip和对应QTcpSocket指针存到Map里。
- 读取到数据的时候,遍历map,根据QTcpSocket指针(value)可以找到ip值(key)。
- 发送数据时候,根据界面上的ip(key),可以直接取出对应的value。
1.2 初始化QTcpServer
1.2.1 初始化服务端
tcpServer = new QTcpServer(this);
1.2.2 实现处理新连接连接信号
tcpServer 提供了多个触发信号(新连接,连接断开等),我们必须要处理的信号是有新连接到来信号newConnection。
void newConnection()
这个信号通知我们有新的客户端请求连接服务端,我们需要为这个客户端新建一个QTcpSocket;因此我们编写了一个槽函数,与newConnection()信号连接,用来处理新连接。
在处理连接的槽函数中需要需要完成以下工作:
- 使用nextPendingConnection(),获得为新的客户端连接创建的QTcpSocket
- 获取当前连接的客户端的ip
- 将ip字符串 和 新建的QTcpSocket保存到map中
- 为新建的QTcpSocket,连接数据可读取信号槽
具体代码:
首先在头文件中定义槽函数
slots:
void :NewConnectionSlot();
void MainWindows::NewConnectionSlot()
{
currentClient = tcpServer->nextPendingConnection(); // 获取新建的socket
int current_port = currentClient->peerPort(); //获取当前连接的客户端的端口
QString ip = currentClient->peerAddress().toString().split("::ffff:")[1] + QString::number(current_port);
tcpClient.insert(ip, currentClient);
// 可以将这个ip 字符串,更新在ui的客户端ip选择框里
connect(currentClient, SIGNAL(readyRead()), this, SLOT(ReadData()));
}
在新建QTcpServer对象后,将我们上面定义的槽函数NewConnectionSlot和newConnection信号进行连接
tcpServer = new QTcpServer(this);
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(NewConnectionSlot()));
完整初始化函数为:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
tcpServer = new QTcpServer(this);
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(NewConnectionSlot()));
}
void MainWindows::NewConnectionSlot()
{
currentClient = tcpServer->nextPendingConnection(); // 获取新建的socket
int current_port = currentClient->peerPort(); //获取当前连接的客户端的端口
QString ip = currentClient->peerAddress().toString().split("::ffff:")[1] + QString::number(current_port);
tcpClient.insert(ip, currentClient);
// 可以将这个ip 字符串,更新在ui的客户端ip选择框里
ui->cbxConnection->append(ip);
connect(currentClient, SIGNAL(readyRead()), this, SLOT(ReadData()));
}
到这里tcpServer的初始化部分已经完成。
1.3 实现数据读取槽函数
QTcpSocket拥有readyRead信号, 接下来需要实现在NewConnectionSlot中,用来和readyRead信号连接的数据处理函数
void MainWindows::ReadData()
{
QTcpSocket *socket = qobject_cast<QMessageBox *>(sender()); // 获取是哪个socket发送的信号
QByteArray buffer = socket->readAll();
// buffer是读取到的数据, 接下来可以打印在ui上
}
1.4 实现数据发送函数
从ui上获取要发送的数据和目标客户端后,可以通过QTcpsocket的write()将数据发送出去
void MainWindows::on_sendButton_clicked(){
QString clientIP = ui->cbxConnection->currentText();
QString data = ui->textEdit->toPlainText();
tcpClient[clientIP]->write(data.toLatin1());
}
1.5 监听端口和ip
调用tcpServer的listen()函数对端口进行监听,第一个参数QHostAddress::Any代表本机上所有网卡,第二个参数是要监听的端口。
tcpServer->listen(QHostAddress::Any, ui->portEdit->text().toInt());
2. 客户端
2.1 初始化QTcpSocket
2.1.1 新建对象并连接信号槽
新建对象,并连接有数据可读信号槽
tcpClient = new QTcpSocket(this); //实例化tcpClient
connect(tcpClient, SIGNAL(readyRead()), this, SLOT(ReadData()));
2.1.2 实现有数据可读槽函数
实现上一步中connect的ReadData槽函数
void MainWindows::ReadData()
{
QByteArray buffer = tcpClient->readAll();
if(!buffer.isEmpty())
{
ui->edtRecv->append(buffer);
}
}
2.2 实现数据发送函数
获取文本框上的数据文字,通过tcpClient->write来发送, 发送的时候需要转成二进制格式。
void MainWindows::on_sendButton_clicked(){
QString data = ui->edtSend->toPlainText();
if(data != "")
{
tcpClient->write(data.toLatin1());
}
}
2.3 连接到目标服务器
在按下连接按钮后,连接到目标服务器。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
tcpClient = new QTcpSocket(this); //实例化tcpClient
connect(tcpClient, SIGNAL(readyRead()), this, SLOT(ReadData()));
}
void MainWindows::on_connectButton_clicked(){
tcpClient->connectToHost(ui->edtIP->text(), ui->edtPort->text().toInt());
}