网络编程概览
网络编程(network programming)是在一定协议下实现两台计算机通信的程序。Qt 中所有 Socket 类都是非阻塞的,通信过程由事件驱动(数据到达发信号)。使用网络类必须在 .pro 文件添加 QT += network。
pro 加 QT += network,否则编译报"找不到头文件"。
三个核心类分工:
-
QTcpServer(TCP 服务器套接字)------服务器端,监听连接。
-
QTcpSocket(TCP 套接字)------客户端连接,也用于服务器与客户端通信。
-
QUdpSocket(UDP 套接字)------UDP 通信,服务器、客户端都用它。
TCP 服务器流程
TCP 服务器(QTcpServer)五个步骤:①创建套接字 → ②listen 监听 → ③newConnection 信号检测连接、nextPendingConnection 取客户端 → ④读写数据 → ⑤close 关闭。
listen(地址, 端口):进入监听模式,如 listen(QHostAddress("127.0.0.1"), 8000)。127.0.0.1 是本机回环地址(只在本机通信测试用)。
newConnection 信号:有客户端连接时发射;槽里调 nextPendingConnection() 取客户端 QTcpSocket*。
取到客户端后要再 connect 它的 readyRead 信号,有数据到达时触发读取。
客户端信息:cli->peerAddress().toString()(客户端 IP)、cli->peerPort()(客户端端口)。
发送:cli->write(数据);接收:readAll()(一次读全部)。
关闭:ser->close()(停止监听)、cli->close()(断开连接)。
TCP 客户端流程
TCP 客户端(QTcpSocket)四步:①创建套接字 → ②connectToHost 连接服务器 → ③readyRead 收 / write 发 → ④close 关闭。
connectToHost(服务器地址, 端口):发起连接,如 connectToHost(QHostAddress("127.0.0.1"), 8000)。
收发与服务器端相同:readyRead 信号 + readAll() 接收;write() 发送。
发送中文要转编码:write(str.toUtf8())(QString 转 UTF-8 字节)。
同步(阻塞)方式:非 GUI/多线程中可用 waitForConnected() 等 waitFor... 系列函数暂停线程等待操作完成(一般 GUI 用信号异步方式即可)。
// ===== 服务器端 =====
QTcpServer *ser = new QTcpServer(this);
ser->listen(QHostAddress("127.0.0.1"), 8000); // 监听本机8000端口
connect(ser, &QTcpServer::newConnection, this, &server::recv_conn);
void server::recv_conn() { // 有客户端连入
cli = ser->nextPendingConnection(); // 取客户端 socket
qDebug() << cli->peerAddress().toString(); // 客户端 IP
connect(cli, &QTcpSocket::readyRead, this, &server::recv_data);
}
void server::recv_data() { // 收到数据
QByteArray rdata = cli->readAll(); // 读全部数据
ui->txt_recv->append(rdata);
}
void server::on_btn_send_clicked() { // 发送
cli->write(ui->txt_send->text().toUtf8());
}
// ===== 客户端 =====
QTcpSocket *cli = new QTcpSocket(this);
cli->connectToHost(QHostAddress("127.0.0.1"), 8000); // 连接服务器
connect(cli, &QTcpSocket::readyRead, this, &tcpClient::recv_data);
void tcpClient::recv_data() {
QString rstr = cli->readAll();
ui->txt_recv->append(rstr);
}
sender() 获取信号发送者
QObject::sender() 是 QObject 的成员函数,返回当前正在执行的槽是由哪个对象发出的信号触发的(指针)。
只能在槽函数内调用(信号触发时有效)。
返回 QObject*,需强转回实际类型:cli = (QTcpSocket*)sender();
void server::recv_data() {
// 多个客户端共用一个槽时,用 sender() 区分是谁发来的
cli = (QTcpSocket*)sender(); // 强转回 QTcpSocket*
QByteArray rdata = cli->readAll();
ui->txt_recv->append(rdata);
}
网络数据的接收与发送细节
数据先进入发送缓冲区,之后由操作系统送往远端;flush() 强制立刻送走。
接收缓冲区大小可用 setReadBufferSize(字节数) 设置。
错误处理:error() 返回错误类型、errorString() 返回错误提示字符串。
接收线程在后台运行,完成后再交给主线程,不阻塞界面。
UDP 通信
UDP 收发流程
UDP(User Datagram Protocol,用户数据报协议)无连接,服务器/客户端概念淡化------两端做的工作大致相同:①创建 QUdpSocket → ②bind 绑定端口 → ③readDatagram 收 / writeDatagram 发 → ④close 关闭。
bind(地址, 端口):把 socket 绑定到本机某端口,才能收到发给该端口的数据。
接收:readDatagram(缓存, 最大字节, &发送方地址, &发送方端口)------返回实际收到的字节数;pendingDatagramSize() 可先查待收数据大小来准备缓存。
发送:writeDatagram(数据, 目标地址, 目标端口)。
发送方地址用 QHostAddress,端口用 quint16(无符号 16 位整数,0~65535)。
接收后从 addr/port 参数取到"谁发来的",可直接回发给对方。
// ===== UDP 服务端 =====
QUdpSocket *ser = new QUdpSocket(this);
ser->bind(QHostAddress("127.0.0.1"), 8000); // 绑定本机端口
connect(ser, &QUdpSocket::readyRead, this, &udp_server::recv_data);
void udp_server::recv_data() {
char buff[100] = {0};
// 读数据,同时取出发送方地址和端口
int ret = ser->readDatagram(buff, 100, &addr, &port);
if (ret != -1) // 成功才处理
ui->txt_recv->append(buff);
}
void udp_server::on_btn_send_clicked() {
QString str = ui->txt_send->text().trimmed();
// 发给刚才收到数据的地址和端口(回发)
ser->writeDatagram(str.toUtf8().data(), 100, addr, port);
}
// ===== UDP 客户端(与服务端几乎一样)=====
QUdpSocket *cli = new QUdpSocket(this);
addr.setAddress("127.0.0.1"); // 目标地址
port = 8000; // 目标端口
cli->writeDatagram(str.toUtf8().data(), 100, addr, port); // 发送
TCP 与 UDP 对比
| 对比点 | TCP | UDP |
|---|---|---|
| 连接 | 需建立连接(connectToHost / listen) | 无连接,直接收发 |
| 可靠性 | 可靠(不丢包、有序) | 不可靠(可能丢包) |
| 服务器类 | QTcpServer(监听) | 无专门类,QUdpSocket 通用 |
| 客户端类 | QTcpSocket | QUdpSocket |
| 绑定端口 | 服务器 listen | 两端都要 bind |
| 收发函数 | readAll() / write() | readDatagram() / writeDatagram() |
四、文本编辑 QTextEdit 小功能
ui->textEdit->copy(); // 复制选中内容
ui->textEdit->paste(); // 粘贴
ui->textEdit->undo(); // 撤销
ui->textEdit->zoomIn(); // 放大
ui->textEdit->zoomOut(); // 缩小
// 保存到文件:文字转 UTF-8 字节再写
QString path = QFileDialog::getSaveFileName(); // 选保存路径
QFile file(path);
file.open(QIODevice::WriteOnly);
file.write(ui->textEdit->toPlainText().toUtf8().data(), // 写内容
ui->textEdit->toPlainText().size());
file.close();