点击上方蓝字关注我们
网络编程在应用程序开发中非常重要,一般的应用程序都会涉及到网络通信。网络编程说的就是互联网通用的TCP/IP协议,TCP/IP的层次分为4层:
(1)应用层:属于高层的网络传输协议。例如:HTTP、FTP、DNS协议等。
(2)传输层:底层的网络传输协议。例如:TCP、UDP、RTP、SCTP协议等。
(3)网络互连层对于TCP/IP来说这是因特网协议。
(4)网络接口层:例如以太网、Wi-Fi、MPLS底层驱动等。
对于应用编程来说,我们只需要用到其中的应用层和传输层(统称为上层)。目前主流的操作系统都有自己的一套上层网络编程接口,比如:windows、linux系统等。但这些编程接口使用起来相对比较繁琐,而且需要依赖底层操作系统的一些数据结构。QT为了解决跨平台的问题,本身提供了一套完整的上层网络编程接口,这套接口包含在QT的network模块内。如果要使用QT网络编程相关的一些类,就需要先包含network模块。包含方式:在当前pro工程文件中加入以下代码。
关于QtNetwork模块的详细介绍可以查看QT自带的帮助文档。查找方式截图如下:
图12-0-1 Qt Network模块帮助文档
下表列出了QT5提供的所有网络编程相关的C++类:
QAbstractNetworkCache
| 实现缓存的接口
| QAbstractSocket
| 所有类型套接字的基础功能
| QAuthenticator
| 认证对象
| QDnsDomainNameRecord
| 存储关于域名信息记录
| QDnsHostAddressRecord
| 存储主机地址信息记录
| QDnsLookup
| 表示DNS查找相关操作
| QDnsMailExchangeRecord
| DNS MX记录信息
| QDnsServiceRecord
| DNS SRV记录信息
| QDnsTextRecord
| DNS TXT存储信息记录
| QHostAddress
| 存放主机的IP地址信息
| QHostInfo
| 关于查询主机名的静态函数
| QHttpMultiPart
| 通过HTTP发送的类似于MIME的多部件消息
| QHttpPart
| 包含了HTTP多部件MIME消息的主体部分
| QLocalServer
| 基于本地套接字的服务器
| QLocalSocket
| 本地套接字
| QNetworkAccessManager
| 允许应用程序发送网络请求和接收应答
| QNetworkAddressEntry
| 保存一个IP地址,连同掩码和广播地址
| QNetworkCacheMetaData
| 缓存信息
| QNetworkConfiguration
| 一个或者多个接入点设置的抽象
| QNetworkConfigurationManager
| 管理系统提供的网络设置
| QNetworkCookieJar
| 实现了一个简单的jar QNetworkCookie对象
| QNetworkDiskCache
| 基本磁盘高速缓存
| QNetworkInterface
| 列出主机的网络地址和网络接口
| QNetworkProxy
| 网络层代理
| QNetworkProxyFactory
| 细粒度代理选择
| QNetworkProxyQuery
| 用于查询代理设置套接字
| QNetworkReply
| 包含了QNetworkAccessManager发送请求时的数据和头
| QNetworkRequest
| 包含了QNetworkAccessManager发送的请求
| QNetworkSession
| 支配系统的接入点,并且在当多个客户接入了同一个接入点的情况下,允许使用会话管理
| QSslCertificateExtension
| 访问X509证书扩展的API
| QSslCipher
| 代表一个SSL加密密码
| QSslConfiguration
| 配置和SSL连接的状态
| QSslEllipticCurve
| 代表了一种密码算法
| QSslError
| SSL error
| QSslKey
| 私钥和公钥接口
| QSslPreSharedKeyAuthenticator
| 共享密钥认证密码
| QSslSocket
| 客户端和服务器的SSL加密套接字
| QTcpServer
| 基于tcp的服务器
| QTcpSocket
| TCP套接字
| QUdpSocket
| udp套接字
|
表12-0-1 Qt Network模块包含的所有C++类
12.1 获取本机网络信息 获取本机网络信息相关的类如下:QHostInfo、QHostAddress、QNetworkInterface、QNetworkAddressEntry。
12.1.1 获取本机IP地址 下面代码通过QHostAddress和QNetworkInterface类列出本机全部的IP地址。
#include <QApplication>
#include <QHostInfo>
#include <QNetworkInterface>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString addr_info;
addr_info="列出本机全部IP地址:\n";
QList<QHostAddress> list = QNetworkInterface::allAddresses();
for(int i=0;i<list.count();i++)
{
QHostAddress addr=list.at(i);
if(addr.protocol() == QAbstractSocket::IPv4Protocol)
{
addr_info+=addr.toString();
addr_info+="\n";
}
}
QLabel label;
label.setText(addr_info);
label.show();
return a.exec();
}
|
图12-1-1 获取IP地址
以上代码中主要的函数是allAddresses()函数,该函数是QNetworkInterface类提供的一个静态函数,用于获取本机的所有的IP地址信息,使用QList<QHostAddress>列表方式返回,列表中的每一个成员就是一个QHostAddress类。函数原型如下:
[static] QList<QHostAddress> QNetworkInterface::allAddresses()
|
QHostAddress类提供一种独立于平台和协议的方式来保存IPv4和IPv6地址。QHostAddress通常与QTcpSocket、QTcpServer、QUdpSocket一起使用,来连接到主机或建立一个服务器。可以通过setAddress()来设置一个主机地址,使用toIPv4Address()、toIPv6Address()或toString()来检索主机地址。通过protocol()来检查协议类型等。下面将介绍QHostAddress常用的一些接口函数用法:
1. QHostAddress常用的地址枚举
QHostAddress::LocalHost
| 本地主机IPV4地址。相当于("127.0.0.1")
| QHostAddress::LocalHostIPv6
| 本地主机IPV6地址。相当于("::1")
| QHostAddress::Broadcast
| 广播地址。相当于("255.255.255.255")
| QHostAddress::AnyIPv4
| IPv4的所有地址.相当于("0.0.0.0")
| QHostAddress::AnyIPv6
| IPv6的所有地址。相当于("::")
| QHostAddress::Any
| 本地所有地址。包括IPV4和IPV6
|
2. 构造QHostAddress
QHostAddress::QHostAddress(quint32 ip4Addr)
QHostAddress::QHostAddress(quint8 *ip6Addr)
QHostAddress::QHostAddress(const QString &address)
|
QHostAddress的构造函数中可以传入ipv4和ipv6类型IP地址。还可以通过字符串方式传入。
示例:
QHostAddress ipaddr("192.168.1.123");
|
3. 设置IP地址
void QHostAddress::setAddress(quint32 ip4Addr)
void QHostAddress::setAddress(quint8 *ip6Addr)
bool QHostAddress::setAddress(const QString &address)
|
除了构造函数可以传入IP地址外,也可以通过setAddress函数进行设置。如果需要清除IP地址,可以通过clear()函数实现,clear()将IP地址设置为0.0.0.0。
4. 获取IP地址
Q_IPV6ADDR QHostAddress::toIPv6Address() const
quint32 QHostAddress::toIPv4Address() const
QString QHostAddress::toString() const //使用字符串形式返回
|
以上3个函数用于将QHostAddress对象包含的IP地址使用特定的格式返回。
示例:
QHostAddress addr("192.168.1.1");
addr.toString(); //返回:"192.168.1.1"
addr.toIPv4Address(); //返回:3232235777
Q_IPV6ADDR ipv6_addr=addr.toIPv6Address();
for(int i = 0; i < 16; ++i)
{
//ipv6_addr[i];
}
|
5. 获取主机网络层的通信协议
QAbstractSocket::NetworkLayerProtocol QHostAddress::protocol() const
|
返回值QAbstractSocket是一个枚举类型,其中:
QAbstractSocket::IPv4Protocol 表示IPv4协议
QAbstractSocket::IPv6Protocol 表示IPv6协议
12.1.2 获取本地网络连接详细信息 下面代码通过QNetworkInterface与QNetworkAddressEntry类获取本机的网络连接详细信息。比如:MAC地址、子网掩码等等。
#include <QApplication>
#include <QHostInfo>
#include <QtNetwork>
#include <QHostInfo>
#include <QDebug>
#include <QLabel>
#include <QNetworkInterface>
#include <QHostAddress>
#include <QMessageBox>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString addr_info;
QList<QNetworkInterface> list = QNetworkInterface::allInterfaces();
for(int i=0;i<list.count();i++)
{
QNetworkInterface inter=list.at(i);
addr_info+="设备名称:"+inter.name()+"\n";
addr_info+="MAC地址:"+inter.hardwareAddress()+"\n";
addr_info+="连接名称:"+inter.humanReadableName()+"\n";
QList<QNetworkAddressEntry> entry=inter.addressEntries();
for(int n=0;n<entry.count();n++)
{
QNetworkAddressEntry netaddr=entry.at(n);
addr_info+="IP地址:"+netaddr.ip().toString()+"\n";
addr_info+="子网掩码:"+netaddr.netmask().toString()+"\n";
}
}
QMessageBox::information(NULL,"本机网络连接详细信息",addr_info,QMessageBox::Ok);
return a.exec();
}
|
图12-1-2 获取本机网络连接详细信息
12.2 搭建TCP服务器与客户端 TCP提供了一种面向连接、可靠的字节流服务。面向连接比较好理解,就是连接双方在通信前需要预先建立一条连接,这犹如实际生活中的打电话。助于可靠性,TCP协议中涉及了诸多规则来保障通信链路的可靠性。因此,对可靠性要求高的数据通信场合,一般使用TCP协议进行传输数据。
QT提供了QTcpSocket和QTcpServer类实现TCP协议编程。其中QTcpSocket类是QAbstractSocket的子类,提供一个TCP套接字,用于建立TCP连接和传输的数据流。QTcpServer类提供了一个基于tcp的服务器搭建方法。这个类可以接受传入的TCP连接,可以选择监听特定的地址或者本机的所有地址。每次有新的客户端连接时会发出newConnection()信号。
下面给出了TCP协议通信的模型图:
图12-2-1 TCP协议通信模型图
12.2.1 QTcpSocket类介绍 QTcpSocket提供了TCP通信的套接字,该类是QAbstractSocket的子类,下图介绍了QTcpSocket的继承关系。
图12-2-2 QTcpSocket继承关系图
QTcpSocket类所有的成员函数都继承于QAbstractSocket类和QIODevice类。下面介绍TCP网络编程中会用到的函数接口与相关的信号。
QTcpSocket类继承于QAbstractSocket类的相关函数介绍
1. 绑定端口
bool QAbstractSocket::bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform)
bool QAbstractSocket::bind(quint16 port = 0, BindMode mode = DefaultForPlatform)
|
Bind函数一般用在服务器端,用于将本地地址与一套接口捆绑。
2. 连接到服务器主机
(1) void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol)
(2) void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite)
|
connectToHost函数一般在客户端使用,用于连接服务器。其中const QHostAddress &address参数表示填写服务器的IP地址信息,quint16 port参数填写服务器的端口号。
3. 关闭套接字
void QAbstractSocket::disconnectFromHost()
void QAbstractSocket::abort()
|
关闭套接字给出了以上两种方法:使用disconnectFromHost函数关闭套接字时,不会立即关闭套接字,会将缓冲区的数据读写完毕后,再发出disconnected()关闭信号。而abort()函数会立即关闭套接字,丢弃缓冲区里所有数据。
4. 获取本地主机的地址信息
QHostAddress QAbstractSocket::localAddress() const //获取本地主机的IP地址
quint16 QAbstractSocket::localPort() const //获取本地主机的端口号
|
5. 获取远程主机的地址信息
QHostAddress QAbstractSocket::peerAddress() const //获取远程主机IP地址
quint16 QAbstractSocket::peerPort() const //获取远程主机端口号
QString QAbstractSocket::peerName() const //获取远程主机名称
|
示例:
void Widget::new_TCP_Connection()
{
//获取下一个等待连接的QTcpSocket对象
server_socket=tcp_server->nextPendingConnection();
qDebug()<<server_socket->localPort();
qDebug()<<server_socket->localAddress().toString();
qDebug()<<server_socket->peerPort();
qDebug()<<server_socket->peerName();
qDebug()<<server_socket->peerAddress();
}
|
6. 设置缓冲区大小
qint64 QAbstractSocket::readBufferSize() const
void QAbstractSocket::setReadBufferSize(qint64 size)
|
默认情况下,缓冲区的大小为0,表示缓冲区大小不受限制。
7. 获取套接字的状态
SocketState QAbstractSocket::state() const
|
状态枚举值:
QAbstractSocket::ConnectedState 表示套接字已经建立连接。
QAbstractSocket::UnconnectedState 表示套接字已经断开连接
8. 获取套接字描述符
qintptr QAbstractSocket::socketDescriptor() const
|
如果返回值为负值,表示该套接字不可用。可以使用此函数判断该套接字对象是否已经断开连接。
9. 等待套接字断开
bool QAbstractSocket::waitForDisconnected(int msecs = 30000)
|
示例:
socket->disconnectFromHost();
if (socket->state() == QAbstractSocket::UnconnectedState ||
socket->waitForDisconnected(1000))
qDebug("Disconnected!");
|
QTcpSocket类继承于QIODevice类的相关函数介绍
1. 读出缓冲区中接收到的网络数据
QByteArray QIODevice::read(qint64 maxSize)
qint64 QIODevice::read(char *data, qint64 maxSize)
QByteArray QIODevice::readAll()
|
readAll函数比较常用,直接将缓冲区的数据全部读出,使用QByteArray类返回。
2. 发送数据
qint64 QIODevice::write(const char *data, qint64 maxSize)
qint64 QIODevice::write(const char *data)
qint64 QIODevice::write(const QByteArray &byteArray)
|
write函数用于将数据写入缓冲区,最终发送给服务器或者客户端。
3. 检测是否有数据可读
bool QAbstractSocket::atEnd() const
|
如果缓冲区有数据可读将返回true。
1. 成功建立连接信号
[signal] void QAbstractSocket::connected()
|
当客户端与服务器成功建立连接时会发出connected()信号,该信号在客户端使用。用于响应是否成功连接上服务器。
2. 断开连接信号
[signal] void QAbstractSocket::disconnected()
|
当套接字断开连接时,会发出disconnected信号,可在disconnected信号关联的槽函数里做释放操作。如果有必要,可以将该信号直接与void QObject::deleteLater()槽函数关联,直接删除对象。
3. 产生错误信号
void error(QAbstractSocket::SocketError socketError)
|
套接字在通信过程中发生了错误,会发出error信号。通过该信号形参可以判断错误的类型。
4. 数据可读信号
[signal] void QIODevice::readyRead()
|
当缓冲区有新的数据可读时,会发出readyRead信号。可在readyRead信号关联的槽函数里读取数据。
12.2.2 QtcpServer介绍 QTcpServer类提供了一个基于tcp服务器的创建方式,QTcpServer可以接受TCP客户端的连接。下面将介绍QTcpServer类常用的函数接口。
1. 设置服务器监听的IP和端口
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
|
如果端口是0,表示自动选择一个端口进行监听。如果地址是QHostAddress::Any,表示服务器将监听本地所有IP地址。成功返回true,否则返回false。
2. 判断当前服务器监听状态
bool QTcpServer::isListening() const
|
如果当前服务器正在监听返回true,否则返回false。
3. 获取当前服务器地址信息
QHostAddress QTcpServer::serverAddress() const
|
当客户端连接上服务器时,才可使用serverAddress函数获取当前服务器的地址信息。如果服务器监听的地址是全部地址,返回的IP地址是0。
4. 获取等待连接的客户端QTcpSocket套接字对象
QTcpSocket *QTcpServer::nextPendingConnection()
|
该函数一般在newConnection()信号的槽函数中调用。
5. 设置服务器允许连接的最大数量
void QTcpServer::setMaxPendingConnections(int numConnections) //设置最大连接的数量
int QTcpServer::maxPendingConnections() const //返回当前剩余的可连接数量
|
默认情况下最大连接数量为30。
6. 处理新连接
[virtual protected] void QTcpServer::incomingConnection(qintptr socketDescriptor)
|
incomingConnection函数是一个虚函数,可在子类中重写该函数。只要有客户端连接上服务器,就会调用incomingConnection函数,可在incomingConnection函数内部处理客户端的连接。
7. 获取新连接的客户端信息
QTcpSocket *QTcpServer::nextPendingConnection()
|
该函数在实例化QTcpServer类,接收到newConnection()信号时调用,用于获取新连接的客户端。
示例:
//服务器模式:响应新连接的客户端
void QWidget_SharedImage::NewTcpConnection()
{
/*获取新客户端的套接字信息*/
QTcpSocket *ServerSocket=LocalTcpServer->nextPendingConnection();
/*关联可读信号*/
connect(ServerSocket,SIGNAL(readyRead()),this,SLOT(ReadTcpClientData()));
/*关联断开信号*/
connect(ServerSocket,SIGNAL(disconnected()),this,SLOT(TcpClientDisconnected()));
TcpFarClientList.append(ServerSocket);//将连接上的客户端添加到列表,用于记录已经连接的客户端
.................其他功能代码省略..........................
}
|
8. 使用本机套接字初始化socket连接
bool QAbstractSocket::setSocketDescriptor(qintptr socketDescriptor, SocketState socketState = ConnectedState, OpenMode openMode = ReadWrite)
|
是子类化(继承)方式实现TCP服务器时,新连接上来的客户端会通过传入一个套接字描述符socketDescriptor用于初始化QAbstractSocket,可将此套接字传入到其他线程进行处理。如果socketDescriptor为有效的套接字描述符,则返回true; 否则返回false。套接字以openMode指定的模式打开,并进入由socketState指定的套接字状态。
注意:使用相同的本地套接字描述符不可能初始化两个抽象套接字。
9. 等待数据发送完毕
bool QAbstractSocket::waitForBytesWritten(int msecs = 30000)
|
从QIODevice :: waitForBytesWritten()重新实现。
此功能会阻塞,直到socket上至少写入一个字节并发出bytesWritten()信号。 该函数在msecs毫秒后超时; 默认超时时间为30000毫秒。
如果发送bytesWritten()信号,该函数返回true; 否则返回false(如果发生错误或操作超时)。
注意:此功能可能在Windows上随机失败。如果您的软件将在Windows上运行,请考虑使用事件循环和bytesWritten()信号。
10. 连接到服务器
void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite)
|
此函数在TCP客户端调用,用于连接到指定的TCP服务器。
11. 等待连接
bool QAbstractSocket::waitForConnected(int msecs = 30000)
|
此函数在TCP客户端调用,一直等到套接字连接。 如果连接已建立,则此函数返回true; 否则返回false。在返回false的情况下,可以调用error()来确定错误的原因。
如果msecs是-1,则该函数不会超时。
以下示例等待一秒钟以建立连接:
示例:
socket->connectToHost("imap", 143);
if (socket->waitForConnected(1000))
qDebug("Connected!");
|
12. 等待有数据可读
bool QAbstractSocket::waitForReadyRead(int msecs = 30000)
|
从QIODevice :: waitForReadyRead()重新实现。
此功能会阻止,直到有新的数据可用于读取并且readyRead()信号已发出。 该函数在msecs毫秒后超时; 默认超时时间为30000毫秒。
如果readyRead()信号发出并且有新的数据可供读取,该函数返回true; 否则返回false(如果发生错误或操作超时)。
注意:此功能可能在Windows上随机失败。如果您的软件将在Windows上运行,请考虑使用事件循环和readyRead()信号。
1. 新连接请求信号
[signal] void QTcpServer::newConnection()
|
当客户端连接上服务器时,会触发newConnection信号。可在newConnection关联的槽函数中创建套接字。
2. 连接错误信号
[signal] void QTcpServer::acceptError(QAbstractSocket::SocketError socketError)
|
acceptError信号是在接受一个新的客户端连接,产生错误时发出。socketError参数描述了错误发生的类型。
12.2.3 TCP服务器与客户端创建步骤 第一种方式步骤:
(1) 子类化QTcpServer类(重新继承QTcpServer)
(2) 调用listen函数设置监听端口和IP地址
(3) 重新实现服务器端的incomingConnection虚函数,该函数用来处理连接上的TCP请求,创建socket类实现通信。
(4) 关联disconnected信号,处理断开的客户端套接字
第二种方式步骤:
(1) 实例化QTcpServer类
(2) 调用listen函数设置监听端口和IP地址
(3) 关联newConnection信号。用于处理新连接上来的客户端请求,创建socket类实现通信
(4) 关联disconnected信号,处理断开的客户端套接字
(1) 实例化QTcpSocket类
(2) 绑定connected和disconnected信号。用于响应连接和断开服务器的操作
(3) 调用connectToHost函数连接服务器
下面将编写TCP服务器与客户端的创建模型示例,先学习简单服务器与客户端的创建方法。
(1) “widget.h”文件代码示例
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QUdpSocket>
#include <QtNetwork>
#include <QHostInfo>
#include <QTcpSocket>
#include <QHostAddress>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
QTcpServer *TcpServer;
QTcpSocket *ServerSocket;
public slots:
void NewTcpConnection();
void ReadTcpData();
void TcpClientDisconnected();
};
#endif // WIDGET_H
|
(2) “widget.c”文件代码示例
#include "widget.h"
Widget::Widget(QWidget *parent): QWidget(parent)
{
/*1. 实例化服务器*/
TcpServer= new QTcpServer;
/*2. 设置监听的端口*/
TcpServer->listen(QHostAddress::Any,8888);
/*3. 关联连接信号,检测是否有新的客户端连接*/
connect(TcpServer,SIGNAL(newConnection()),this,SLOT(NewTcpConnection()));
}
Widget::~Widget()
{
}
void Widget::NewTcpConnection()
{
/*创建本地服务器套接字*/
ServerSocket=TcpServer->nextPendingConnection();
/*关联可读信号*/
connect(ServerSocket,SIGNAL(readyRead()),this,SLOT(ReadTcpData()));
/*关联断开信号*/
connect(ServerSocket,SIGNAL(disconnected()),this,SLOT(TcpClientDisconnected()));
}
void Widget::ReadTcpData()
{
//读取数据
}
void Widget::TcpClientDisconnected()
{
//处理断开连接的情况
}
|
(1) “widget.h”文件代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QUdpSocket>
#include <QtNetwork>
#include <QHostInfo>
#include <QTcpSocket>
#include <QHostAddress>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
QHostAddress *ServerAddr;
QTcpSocket *TcpClientSocket;
public slots:
void slotConnected();
void slotDisconnected();
void dataReceived();
};
#endif // WIDGET_H
|
(2) “widget.c”文件代码
#include "widget.h"
Widget::Widget(QWidget *parent): QWidget(parent)
{
/*1. 创建TCP套接字*/
TcpClientSocket = new QTcpSocket(this);
/*2. 设置服务器IP地址*/
ServerAddr =new QHostAddress("192.168.49.1");
/*3. 连接信号槽*/
connect(TcpClientSocket,SIGNAL(connected()),this,SLOT(slotConnected()));
connect(TcpClientSocket,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));
connect(TcpClientSocket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
/*4. 尝试连接服务器主机*/
TcpClientSocket->connectToHost(*ServerAddr,8080);
}
Widget::~Widget()
{
}
void Widget::slotConnected()
{
//连接上服务器
}
void Widget::slotDisconnected()
{
//断开服务器
}
void Widget::dataReceived()
{
//有数据可读
}
|
12.2.4 TCP网络调试助手实例 为了更好的讲解TCP服务器与客户端的使用方法,下面将编写一个TCP调试助手实例。该调试助手实现了TCP服务器与客户端的创建,服务器可以同时与多个客户端进行通讯,并且可以检测当前在线的客户端。 (配套程序编号CH12-3)
TCP调试助手实例运行效果图如下:
图12-2-4-1 TCP网络调试助手运行界面
(1) “widget.ui”设计师界面
图12-2-4-2 UI设计界面
(2) “widget.h”头文件代码示例
widget.h文件中主要用于声明需要用到的各种类指针和槽函数的声明。
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QHostInfo>
#include <QUdpSocket>
#include <QtNetwork>
#include <QHostInfo>
#include <QDebug>
#include <QTcpSocket>
#include <QHostAddress>
#include <QDebug>
#include <QMessageBox>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QComboBox>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
int TxCount;
int RxCount;
//关于客户端模式相关的变量定义
QTcpServer *TcpServer;
QTcpSocket *ServerSocket;
QTcpSocket *LocalTcpClientSocket;
QLineEdit *ClientEditProt;
QLineEdit *ClientEditIP;
QLabel *ClientShowLocalProtLabel;
QLabel *ClientShowLocalIptLabel;
QHBoxLayout *ClientLayout;
void comboBox_config();
void NewClinet();
//关于服务器相关的变量定义
QTcpServer *LocalTcpServer;
void NewServer();
QList<QTcpSocket*> TcpFarClientList;
QLabel *SaverShowLocalProtLabel;
QComboBox *ClientComboBoxList;
QHBoxLayout *ServerLayout;
private slots:
void on_pushButton_connect_clicked();
void LocalTcpClientConnectedSlot();
void LocalTcpClientDisconnectedSlot();
void LocalTcpClientReadDtatSlot();
void NewTcpConnection();
void TcpClientDisconnected();
void ReadTcpClientData();
void on_comboBox_protocol_activated(int index);
void on_pushButton_SendData_clicked();
void on_pushButton_clear_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
|
(3) “widget.cpp”文件代码示例
widget.cpp文件中编写了全部的功能代码,实现了众多服务器与客户端槽函数。
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
comboBox_config();
}
//基本配置
void Widget::comboBox_config()
{
/*第一部分:网络设置*/
//1.1 配置协议
ui->comboBox_protocol->addItem("TCP Server");
ui->comboBox_protocol->addItem("TCP Client");
//1.2. 获取并配置本地IP地址
QList<QHostAddress> list = QNetworkInterface::allAddresses();
for(int i=0;i<list.count();i++)
{
QHostAddress addr=list.at(i);
if(addr.protocol() == QAbstractSocket::IPv4Protocol)
{
ui->comboBox_ipaddress->addItem(addr.toString());
}
}
//1.3 设置可编辑性
ui->comboBox_ipaddress->setEditable(true);
//1.4. 设置默认端口号
ui->lineEdit_prot->setText("8080");
/*第二部分:发送设置*/
//2.1设置循环发送时间
ui->lineEdi_LoopTime->setText("1000");
//2.2 设置默认发送的内容
ui->lineEdit_InputTxDtat->setText("欢迎学习QT5编程!");
//接收数量置0
TxCount=0;
RxCount=0;
/*第三部分:选择客户端时用于显示本地端口和IP*/
ClientShowLocalProtLabel =new QLabel("本地端口:");
ClientShowLocalIptLabel =new QLabel("本地IP地址:");
ClientEditProt= new QLineEdit;
ClientEditIP= new QLineEdit;
ClientLayout = new QHBoxLayout;
ClientLayout->addWidget(ClientShowLocalIptLabel);
ClientLayout->addWidget(ClientEditIP);
ClientLayout->addWidget(ClientShowLocalProtLabel);
ClientLayout->addWidget(ClientEditProt);
/*第四部分:选服务器时用于显示已经连接的客户端*/
SaverShowLocalProtLabel =new QLabel("已连接的客户端列表:");
ClientComboBoxList=new QComboBox;
ClientComboBoxList->setMinimumWidth(180);
ServerLayout = new QHBoxLayout;
ServerLayout->addWidget(SaverShowLocalProtLabel);
ServerLayout->addWidget(ClientComboBoxList);
ServerLayout->addStretch();
}
Widget::~Widget()
{
delete ui;
}
//创建服务器或者连接到服务器
void Widget::on_pushButton_connect_clicked()
{
if(ui->pushButton_connect->text()=="连接")
{
switch(ui->comboBox_protocol->currentIndex())
{
case 0: //服务器模式
NewServer();
break;
case 1: //客户端模式
NewClinet();
break;
}
}else
{
switch(ui->comboBox_protocol->currentIndex())
{
case 0: //服务器模式
ui->pushButton_connect->setText("连接");
for(int i=0;i<TcpFarClientList.count();i++)
{
TcpFarClientList.at(i)->close();
TcpFarClientList.removeAt(i);
}
LocalTcpServer->close();//关闭服务器
//取消客户端列表显示
ui->verticalLayout_9->removeItem(ServerLayout);
ClientComboBoxList->close();
SaverShowLocalProtLabel->close();
ui->comboBox_ipaddress->setEnabled(true);
ui->comboBox_protocol->setEnabled(true);
ui->lineEdit_prot->setEnabled(true);
break;
case 1: //客户端模式
LocalTcpClientSocket->close();
break;
}
}
}
//服务器模式:创建服务器
void Widget::NewServer()
{
/*1. 实例化服务器*/
LocalTcpServer= new QTcpServer;
/*2. 设置监听的端口和IP地址*/
quint16 port=QString(ui->lineEdit_prot->text()).toInt();
if(ui->comboBox_ipaddress->currentText()=="QHostAddress::Any")
{
LocalTcpServer->listen(QHostAddress::Any,port);
}else
{
QHostAddress addr(ui->comboBox_ipaddress->currentText());
LocalTcpServer->listen(addr,port);
}
/*3. 关联连接信号,检测是否有新的客户端连接*/
connect(LocalTcpServer,SIGNAL(newConnection()),this,SLOT(NewTcpConnection()));
ui->pushButton_connect->setText("断开连接");
//添加布局,显示已经连接的客户端列表
ui->verticalLayout_9->insertLayout(1,ServerLayout);
ClientComboBoxList->show();
SaverShowLocalProtLabel->show();
//创建服务器之后设置控件可用
ui->comboBox_ipaddress->setEnabled(false);
ui->comboBox_protocol->setEnabled(false);
ui->lineEdit_prot->setEnabled(false);
}
//客户端模式:创建客户端
void Widget::NewClinet()
{
/*1. 创建本地客户端TCP套接字*/
LocalTcpClientSocket = new QTcpSocket;
/*2. 设置服务器IP地址*/
QString Ipaddr=ui->comboBox_ipaddress->currentText();
QHostAddress FarServerAddr(Ipaddr);
/*3. 连接客户端的信号槽*/
connect(LocalTcpClientSocket,SIGNAL(connected()),this,SLOT(LocalTcpClientConnectedSlot()));
connect(LocalTcpClientSocket,SIGNAL(disconnected()),this,SLOT(LocalTcpClientDisconnectedSlot()));
connect(LocalTcpClientSocket,SIGNAL(readyRead()),this,SLOT(LocalTcpClientReadDtatSlot()));
/*4. 尝试连接服务器主机*/
int prot=ui->lineEdit_prot->text().toInt();
LocalTcpClientSocket->connectToHost(FarServerAddr,prot);
}
//客户端模式:响应连接上服务器之后的操作
void Widget::LocalTcpClientConnectedSlot()
{
//显示本地端口和IP
ClientEditProt->setText(QString::number(LocalTcpClientSocket->localPort()));
ClientEditIP->setText(LocalTcpClientSocket->localAddress().toString());
ui->verticalLayout_9->insertLayout(1,ClientLayout);
ClientEditProt->show();
ClientEditIP->show();
ClientShowLocalProtLabel->show();
ClientShowLocalIptLabel->show();
//当连接上服务器之后设置控件不可用
ui->comboBox_ipaddress->setEnabled(false);
ui->comboBox_protocol->setEnabled(false);
ui->lineEdit_prot->setEnabled(false);
ui->pushButton_connect->setText("断开连接");
}
//客户端模式:断开服务器
void Widget::LocalTcpClientDisconnectedSlot()
{
ui->verticalLayout_9->removeWidget(ClientEditProt);
ui->verticalLayout_9->removeWidget(ClientEditIP);
ui->verticalLayout_9->removeWidget(ClientShowLocalProtLabel);
ui->verticalLayout_9->removeWidget(ClientShowLocalIptLabel);
ui->verticalLayout_9->removeItem(ClientLayout);
ClientEditProt->close();
ClientEditIP->close();
ClientShowLocalProtLabel->close();
ClientShowLocalIptLabel->close();
ui->pushButton_connect->setText("连接");
//当断开上服务器之后设置控件可用
ui->comboBox_ipaddress->setEnabled(true);
ui->comboBox_protocol->setEnabled(true);
ui->lineEdit_prot->setEnabled(true);
}
//客户端模式:读取服务器发过来的数据
void Widget::LocalTcpClientReadDtatSlot()
{
QByteArray array=LocalTcpClientSocket->readAll();
//记录接收的字节数并显示
TxCount+=QString(array).toLocal8Bit().length();
ui->lcdNumber_RxNumber->display(TxCount);
QString text;
//判断是否需要显示时间
if(ui->checkBox_ShowTime->isChecked())
{
QDateTime time = QDateTime::currentDateTime(); //获取系统现在的时间
text+= time.toString("yyyy-MM-dd hh:mm:ss ddd"); //设置显示格式
text+=" :";
}
text+=QString("").fromLocal8Bit(array);
ui->plainTextEdit_ShowData->appendPlainText(text);
}
void Widget::on_comboBox_protocol_activated(int index)
{
switch(index)
{
case 0: //服务器模式
ui->label_ip->setText("(2)本地IP地址");
ui->label_prot->setText("(3)本地端口号");
ui->comboBox_ipaddress->clear();
ui->comboBox_ipaddress->addItem("QHostAddress::Any");
break;
case 1: //客户端模式
ui->label_ip->setText("(2)服务器IP地址");
ui->label_prot->setText("(3)服务器端口号");
ui->comboBox_ipaddress->clear();
break;
}
QList<QHostAddress> list = QNetworkInterface::allAddresses();
for(int i=0;i<list.count();i++)
{
QHostAddress addr=list.at(i);
if(addr.protocol() == QAbstractSocket::IPv4Protocol)
{
ui->comboBox_ipaddress->addItem(addr.toString());
}
}
}
//发送数据
void Widget::on_pushButton_SendData_clicked()
{
QString text=ui->lineEdit_InputTxDtat->text();
if(text.isEmpty())
{
QMessageBox::warning(this,"发送错误","发送的数据不能为空!",QMessageBox::Ok);
return;
}
QByteArray array=text.toLocal8Bit();
int count=0;
switch(ui->comboBox_protocol->currentIndex())
{
case 0: //服务器模式
if(TcpFarClientList.count()<=0)
{
QMessageBox::warning(this,"发送错误","没有连接的客户端!",QMessageBox::Ok);
return;
}
for(int i=0;i<TcpFarClientList.count();i++)
{
//取出地址列表中的一个客户端地址
QTcpSocket *item = TcpFarClientList.at(i);
count=item->write(array);
}
TxCount+=count;
break;
case 1: //客户端模式
if(LocalTcpClientSocket->state()==QAbstractSocket::ConnectedState)
{
TxCount+=LocalTcpClientSocket->write(array);
}
else
{
LocalTcpClientSocket->close();
QMessageBox::warning(this,"发送错误","未连接服务器!",QMessageBox::Ok);
return;
}
break;
}
ui->lcdNumber_TxNumber->display(TxCount);
}
//清除计数
void Widget::on_pushButton_clear_clicked()
{
TxCount=0;
RxCount=0;
ui->lcdNumber_RxNumber->display(0);
ui->lcdNumber_TxNumber->display(0);
}
//服务器模式:响应新连接的客户端
void Widget::NewTcpConnection()
{
/*创建本地服务器套接字*/
QTcpSocket *ServerSocket=LocalTcpServer->nextPendingConnection();
/*关联可读信号*/
connect(ServerSocket,SIGNAL(readyRead()),this,SLOT(ReadTcpClientData()));
/*关联断开信号*/
connect(ServerSocket,SIGNAL(disconnected()),this,SLOT(TcpClientDisconnected()));
TcpFarClientList.append(ServerSocket);//添加到列表
//显示已经连接的客户端
ClientComboBoxList->clear();
for(int i=0;i<TcpFarClientList.count();i++)
{
QString info=TcpFarClientList.at(i)->peerAddress().toString();
info+=":";
info+=QString::number(TcpFarClientList.at(i)->peerPort());
ClientComboBoxList->addItem(info);
}
}
//服务器模式:响应断开的客户端
void Widget::TcpClientDisconnected()
{
for(int i=0;i<TcpFarClientList.count();i++)
{
//取出地址列表中的一个客户端地址
QTcpSocket *item = TcpFarClientList.at(i);
//判断该客户端是否已经断开
if(item->socketDescriptor()==-1)
{
&n |
公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满)
我知道了
|