目录
网络模块介绍
TCP介绍
TCP 服务端应用实例
TCP 客户端应用实例
运行结果:
网络模块介绍
Qt 网络模块为我们提供了编写 TCP / IP 客户端和服务器的类。它提供了较低级别的类,例
如代表低级网络概念的 QTcpSocket , QTcpServer 和 QUdpSocket ,以及诸如 QNetworkRequest ,
QNetworkReply 和 QNetworkAccessManager 之类的高级类来执行使用通用协议的网络操作。
它
还提供了诸如 QNetworkConfiguration , QNetworkConfigurationManager 和 QNetworkSession 等类,
实现承载管理。
想要在程序中使用 Qt 网络模块,我们需要在 pro 项目配置文件里增加下面的一条语句。
QT += network
TCP介绍
TCP 协议( Transmission Control Protocol )全称是传输控制协议是一种 面向连接的、可靠的、
基于字节流 的传输层通信协议。
TCP 通信必 须先建立 TCP 连接 ,通信端分为客户端和服务端。服务端通过监听某个端口
来监听是否有客户端连接到来,如果有连接到来,则建立新的 socket 连接;客户端通过 ip 和
port 连接服务端,当成功建立连接之后,就可进行数据的收发了。需要注意的是,在 Qt 中,
Qt 把 socket 当成输入输出流来对待的,数据的收发是通过 read() 和 write() 来进行的,需要与我
们常见的 send() 与 recv() 进行区分。
TCP 客户端与服务端通信示意图如下。
TCP 服务端应用实例
本例大体流程
首先获取本地 IP 地址。创建一个 tcpSocket 套接字,一个 tcpServer 服务端。点击监听即监听本
地的主机 IP 地址和端口,同时等待服务端的连接。此程序需要结合客户端一起使用,可实现多个客户端的传播。
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkInterface>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:Ui::Widget *ui;/* tcp 服务器 */QTcpServer *tcpServer;/* 通信套接字 */QTcpSocket *tcpSocket;/* 存储本地的 ip 列表地址 */QList<QHostAddress> IPlist;/* 获取本地的所有 ip */void getLocalHostIP();private slots:/* 客户端连接处理槽函数 */void clientConnected();/* 开始监听槽函数 */void startListen();/* 停止监听槽函数 */void stopListen();/* 清除文本框时的内容 */void clearTextBrowser();/* 接收到消息 */void receiveMessages();/* 发送消息 */void sendMessages();/* 连接状态改变槽函数 */void socketStateChange(QAbstractSocket::SocketState);};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);tcpServer = new QTcpServer(this);tcpSocket = new QTcpSocket(this);ui->spinBox->setRange(10000, 99999);ui->pushButton_5->setEnabled(false);getLocalHostIP();/* 信号槽连接 */connect(ui->pushButton, SIGNAL(clicked()),this, SLOT(startListen()));connect(ui->pushButton_5, SIGNAL(clicked()),this, SLOT(stopListen()));connect(ui->pushButton_6, SIGNAL(clicked()),this, SLOT(clearTextBrowser()));connect(ui->pushButton_4, SIGNAL(clicked()),this, SLOT(sendMessages()));connect(tcpServer, SIGNAL(newConnection()),this, SLOT(clientConnected()));
}Widget::~Widget()
{delete ui;
}
/* 新的客户端连接 */
void Widget::clientConnected()
{/* 获取客户端的套接字 */tcpSocket = tcpServer->nextPendingConnection();/* 客户端的 ip 信息 */QString ip = tcpSocket->peerAddress().toString();/* 客户端的端口信息 */quint16 port = tcpSocket->peerPort();/* 在文本浏览框里显示出客户端的连接信息 */ui->textBrowser->append("客户端已连接");ui->textBrowser->append("客户端 ip 地址:" + ip);ui->textBrowser->append("客户端端口:" + QString::number(port));connect(tcpSocket, SIGNAL(readyRead()),this, SLOT(receiveMessages()));connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(socketStateChange(QAbstractSocket::SocketState)));
}
void Widget::getLocalHostIP()
{QList<QNetworkInterface> list= QNetworkInterface::allInterfaces();/* 遍历 list */foreach (QNetworkInterface interface, list) {/* QNetworkAddressEntry 类存储 IP 地址子网掩码和广播地址 */QList<QNetworkAddressEntry> entryList= interface.addressEntries();/* 遍历 entryList */foreach (QNetworkAddressEntry entry, entryList) {/* 过滤 IPv6 地址,只留下 IPv4 */if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {ui->comboBox->addItem(entry.ip().toString());/* 添加到 IP 列表中 */IPlist<<entry.ip();}}}
}
/* 开始监听槽函数 */
void Widget::startListen()
{/* 需要判断当前主机是否有IP项 */if (ui->comboBox->currentIndex() != -1) {qDebug()<<"start listen"<<endl;tcpServer->listen(IPlist[ui->comboBox->currentIndex()],ui->spinBox->value());/* 设置按钮与下拉列表框的状态 */ui->pushButton->setEnabled(false);ui->pushButton_5->setEnabled(true);ui->comboBox->setEnabled(false);ui->spinBox->setEnabled(false);/* 在文本浏览框里显示出服务端 */ui->textBrowser->append("服务器IP地址:"+ ui->comboBox->currentText());ui->textBrowser->append("正在监听端口:"+ ui->spinBox->text());}
}
/* 停止监听槽函数 */
void Widget::stopListen()
{qDebug()<<"stop listen"<<endl;/* 停止监听 */tcpServer->close();/* 如果是连接上了也应该断开,如果不断开客户端还能继续发送信息,* 因为socket未断开,还在监听上一次端口 */if (tcpSocket->state() == tcpSocket->ConnectedState)tcpSocket->disconnectFromHost();/* 设置按钮与下拉列表框的状态 */ui->pushButton_5->setEnabled(false);ui->pushButton->setEnabled(true);ui->comboBox->setEnabled(true);ui->spinBox->setEnabled(true);/* 将停止监听的信息添加到文本浏览框中 */ui->textBrowser->append("已停止监听端口:" + ui->spinBox->text());
}
/* 清除文本框时的内容 */
void Widget::clearTextBrowser()
{/* 清除文本浏览器的内容 */ui->textBrowser->clear();
}/* 接收到消息 */
void Widget::receiveMessages()
{/* 读取接收到的消息 */QString messages = "客户端:" + tcpSocket->readAll();ui->textBrowser->append(messages);
}
/* 发送消息 */
void Widget::sendMessages()
{QList <QTcpSocket *> socketList=tcpServer->findChildren<QTcpSocket *>();qDebug()<<"tcpSocket 数量: "<<socketList.count()<<endl;if(socketList.count()== 0){ui->textBrowser->append("请先与客户端连接!");return;}foreach(QTcpSocket *tmpTcpSocket,socketList){tmpTcpSocket->write(ui->lineEdit->text().toUtf8());}ui->textBrowser->append("服务端 "+ui->lineEdit->text());
}
/* 连接状态改变槽函数 */
void Widget::socketStateChange(QAbstractSocket::SocketState state)
{switch (state) {case QAbstractSocket::UnconnectedState:ui->textBrowser->append("scoket状态:客户端断开连接");tcpSocket->deleteLater();break;case QAbstractSocket::ConnectedState:ui->textBrowser->append("scoket状态:客户端已连接");break;case QAbstractSocket::ConnectingState:ui->textBrowser->append("scoket状态:ConnectingState");break;case QAbstractSocket::HostLookupState:ui->textBrowser->append("scoket状态:HostLookupState");break;case QAbstractSocket::ClosingState:ui->textBrowser->append("scoket状态:ClosingState");break;case QAbstractSocket::ListeningState:ui->textBrowser->append("scoket状态:ListeningState");break;case QAbstractSocket::BoundState:ui->textBrowser->append("scoket状态:BoundState");break;default:break;}
}
TCP 客户端应用实例
本例大体流程:
首先获取本地 IP 地址。创建一个 tcpSocket 套接字,然后用 tcpSocket 套接字使用 connectToHost 函数连接服务端的主机 IP 地址和端口,即可相互通信。
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkInterface>
#include <QDebug>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:Ui::Widget *ui;/* 通信套接字 */QTcpSocket *tcpSocket;/* 存储本地的ip列表地址 */QList<QHostAddress> IPlist;/* 获取本地的所有ip */void getLocalHostIP();
private slots:/* 连接 */void toConnect();/* 断开连接 */void toDisConnect();/* 已连接 */void connected();/* 已断开连接 */void disconnected();/* 清除文本框时的内容 */void clearTextBrowser();/* 接收到消息 */void receiveMessages();/* 发送消息 */void sendMessages();/* 连接状态改变槽函数 */void socketStateChange(QAbstractSocket::SocketState);
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);/* tcp套接字 */tcpSocket = new QTcpSocket(this);/* 设置端口号的范围,注意不要与主机的已使用的端口号冲突 */ui->spinBox->setRange(10000, 99999);/* 设置断开连接状态不可用 */ui->pushButton_2->setEnabled(false);/* 获取本地 ip */getLocalHostIP();/* 信号槽连接 */connect(ui->pushButton, SIGNAL(clicked()),this, SLOT(toConnect()));connect(ui->pushButton_2, SIGNAL(clicked()),this, SLOT(toDisConnect()));connect(ui->pushButton_3, SIGNAL(clicked()),this, SLOT(clearTextBrowser()));connect(ui->pushButton_4, SIGNAL(clicked()),this, SLOT(sendMessages()));connect(tcpSocket, SIGNAL(connected()),this, SLOT(connected()));connect(tcpSocket, SIGNAL(disconnected()),this, SLOT(disconnected()));connect(tcpSocket, SIGNAL(readyRead()),this, SLOT(receiveMessages()));connect(tcpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(socketStateChange(QAbstractSocket::SocketState)));
}Widget::~Widget()
{delete ui;
}
void Widget::toConnect()
{/* 如果连接状态还没有连接 */if (tcpSocket->state() != tcpSocket->ConnectedState) {/* 指定IP地址和端口连接 */tcpSocket->connectToHost(IPlist[ui->comboBox->currentIndex()],ui->spinBox->value());}
}void Widget::toDisConnect()
{/* 断开连接 */tcpSocket->disconnectFromHost();/* 关闭socket*/tcpSocket->close();
}void Widget::connected()
{/* 显示已经连接 */ui->textBrowser->append("已经连上服务端");/* 设置按钮与下拉列表框的状态 */ui->pushButton->setEnabled(false);ui->pushButton_2->setEnabled(true);ui->comboBox->setEnabled(false);ui->spinBox->setEnabled(false);
}void Widget::disconnected()
{/* 显示已经断开连接 */ui->textBrowser->append("已经断开服务端");/* 设置按钮与下拉列表框的状态 */ui->pushButton_2->setEnabled(false);ui->pushButton->setEnabled(true);ui->comboBox->setEnabled(true);ui->spinBox->setEnabled(true);
}/* 获取本地IP */
void Widget::getLocalHostIP()
{// /* 获取主机的名称 */// QString hostName = QHostInfo::localHostName();// /* 主机的信息 */// QHostInfo hostInfo = QHostInfo::fromName(hostName);// /* ip列表,addresses返回ip地址列表,注意主机应能从路由器获取到// * IP,否则可能返回空的列表(ubuntu用此方法只能获取到环回IP) */// IPlist = hostInfo.addresses();// qDebug()<<IPlist<<endl;// /* 遍历IPlist */// foreach (QHostAddress ip, IPlist) {// if (ip.protocol() == QAbstractSocket::IPv4Protocol)// comboBox->addItem(ip.toString());// }/* 获取所有的网络接口,* QNetworkInterface类提供主机的IP地址和网络接口的列表 */QList<QNetworkInterface> list = QNetworkInterface::allInterfaces();/* 遍历list */foreach (QNetworkInterface interface, list) {/* QNetworkAddressEntry类存储IP地址子网掩码和广播地址 */QList<QNetworkAddressEntry> entryList = interface.addressEntries();/* 遍历entryList */foreach (QNetworkAddressEntry entry, entryList) {/* 过滤IPv6地址,只留下IPv4 */if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {ui->comboBox->addItem(entry.ip().toString());/* 添加到IP列表中 */IPlist<<entry.ip();}}}
}/* 清除文本浏览框里的内容 */
void Widget::clearTextBrowser()
{/* 清除文本浏览器的内容 */ui->textBrowser->clear();
}/* 客户端接收消息 */
void Widget::receiveMessages()
{/* 读取接收到的消息 */QString messages = tcpSocket->readAll();ui->textBrowser->append("服务端:" + messages);
}/* 客户端发送消息 */
void Widget::sendMessages()
{if(NULL == tcpSocket)return;if(tcpSocket->state() == tcpSocket->ConnectedState) {/* 客户端显示发送的消息 */ui->textBrowser->append("客户端:" + ui->lineEdit->text());/* 发送消息 */tcpSocket->write(ui->lineEdit->text().toUtf8().data());}
}/* 客户端状态改变 */
void Widget::socketStateChange(QAbstractSocket::SocketState state)
{switch (state) {case QAbstractSocket::UnconnectedState:ui->textBrowser->append("scoket状态:与服务端未连接");tcpSocket->deleteLater();break;case QAbstractSocket::ConnectedState:ui->textBrowser->append("scoket状态:与服务端已连接");break;case QAbstractSocket::ConnectingState:ui->textBrowser->append("scoket状态:ConnectingState");break;case QAbstractSocket::HostLookupState:ui->textBrowser->append("scoket状态:HostLookupState");break;case QAbstractSocket::ClosingState:ui->textBrowser->append("scoket状态:ClosingState");break;case QAbstractSocket::ListeningState:ui->textBrowser->append("scoket状态:ListeningState");break;case QAbstractSocket::BoundState:ui->textBrowser->append("scoket状态:BoundState");break;default:break;}
}
运行结果: