TCP和UDP网络通信类的使用
Pornhub
1. 程序框架搭建
接着上一篇文章,这里就开始设计UDP的相关功能函数了,首先将其UDP的相关配置进行隐藏;
1.1 构造函数讲解
MainWindow::MainWindow(QWidget *parent):QMainWindow(parent),ui(new Ui::MainWindow)
其意义是执行父类QWidget的构造函数,创建一个Ui::MainWindow的类对象ui。这个UI就是Widget的private部分定义的指针变量ui。
构造函数里执行了ui->setupUi(this);相当于执行了Ui::Widget类的setupUi()函数,这个函数可以实现窗口的生成与各种属性的设置、信号与槽的关联。
析构函数则是删除new创建的指针ui。
1.2 屏蔽UDP组件
我们希望在界面初始状态下UDP的组件不被显示出来。
ui->txtUdpTxIP->hide();
ui->spbUdpTxPort->hide();
ui->cbbTxPort->hide();
1.3 声明UDP对象
在头文件中的private中申明一个UDP类的对象;
QUdpSocket *udp;
再在.cpp中进行申明;
udp = new QUdpSocket(this);
2. UI初始化准备工作
2.1 列出支持的网络通信类型
头文件public:初始化:void listNetworkType(QComboBox *cbbNetworkType);
/**
* @brief MainWindow::listNetworkType 列出支持的网络通信类型
* @param cbbNetworkType
*/
void MainWindow::listNetworkType(QComboBox *cbbNetworkType)
{
QMetaEnum mtaEnum = QMetaEnum::fromType<MainWindow::NetworkType>();
for (int i = 0; i < mtaEnum.keyCount(); i++) {
cbbNetworkType->addItem(mtaEnum.key(i), mtaEnum.value(i));
}
cbbNetworkType->setCurrentText("UDP"); // 设定默认值
}
2.2 获取主机的IPV4
头文件public:初始化:QStringList getHostAddress();
/**
* @brief MainWindow::getHostAddress 获取主机的 IPv4
*/
QStringList MainWindow::getHostAddress()
{
QList<QHostAddress> addrList = QNetworkInterface::allAddresses();
QStringList tmp;
foreach (QHostAddress hostAddr, addrList){
if(hostAddr.protocol() == QAbstractSocket::IPv4Protocol){
if(hostAddr.toString().contains("127.0.")) continue;
tmp<<hostAddr.toString();
}
else if (hostAddr.isNull()) // 主机地址为空
continue;
}
return tmp;
}
2.3 UDP、TCP模式下界面修改
头文件public:初始化:void initComboBoxUDP();
void MainWindow::initComboBoxUDP()
{
ui->lblIP->setText("本地IP");
ui->btnLink->setText("连接");
ui->cbbTxPort->hide();
ui->txtUdpTxIP->show();
ui->spbUdpTxPort->show();
}
TCPServer模式下界面修改
void initComboBoxServer();
void initComboBoxClient();
/**
* @brief MainWindow::initComboBoxServer 界面修改 服务器
*/
void MainWindow::initComboBoxServer()
{
ui->lblIP->setText("本地IP");
ui->btnLink->setText("监听");
ui->txtUdpTxIP->hide();
ui->spbUdpTxPort->hide();
ui->cbbTxPort->show();
}
/**
* @brief MainWindow::initComboBoxClient 界面修改 客户端
*/
void MainWindow::initComboBoxClient()
{
ui->lblIP->setText("远程IP");
ui->btnLink->setText("连接");
ui->txtUdpTxIP->hide();
ui->spbUdpTxPort->hide();
ui->cbbTxPort->show();
}
2.4 UDP初始化下拉列表
头文件public:初始化:void initComboBox_Config();
/**
* @brief MainWindow::initComboBox_Config 初始化下拉列表
*
*/
void MainWindow::initComboBox_Config()
{
this->listNetworkType( ui->cbbNetType );
QStringList hostAddrList = getHostAddress();
if( !hostAddrList.isEmpty() )
ui->txtIP->setText( hostAddrList.at(0) );
this->initComboBoxUDP();
}
2.5 下拉列表切换三种通信模式
/**
* @brief MainWindow::on_cbbNetType_currentIndexChanged 切换通信模式
* @param index
*/
void MainWindow::on_cbbNetType_currentIndexChanged(int index)
{
switch(index){
case TCPServer:
this->initComboBoxServer();
break;
case TCPClient:
this->initComboBoxClient();
break;
case UDP:
this->initComboBoxUDP();
break;
}
}
2.6 连接按钮事件
获取下拉列表的值:
enum NetworkType{
TCPServer = 0,
TCPClient = 1,
UDP = 2,
};
Q_ENUM(NetworkType)
NetworkType networkType = (NetworkType)ui->cbbNetType->currentIndex();
列出枚举的类型,根据下拉列表读取当时的值;
然后进行判断,如果已经点击连接按键;判断下拉列表的值,如果是TCPServer则进行监听,如果是TCPClient则进行连接远端的服务器,如果是UDP则绑定端口号;
如果没有点击连接,则断开上述接口;
在头文件声明指针;
QTcpServer *server;
QTcpSocket *client;
QList<QTcpSocket *>clients;
void MainWindow::on_btnLink_clicked(bool checked)
{
NetworkType networkType = (NetworkType)ui->cbbNetType->currentIndex();
bool ok = false;
if(checked){
switch(networkType){
case TCPServer:
// ok = this->startListen();
break;
case TCPClient:
// ok = this->connectRemoteServer();
break;
case UDP:
// ok = this->bindAddrAndPort();
break;
}
ok? ui->cbbNetType->setEnabled(false): ui->cbbNetType->setEnabled(true);
}else{
switch(networkType){
case TCPServer:
// this->stopListen();
break;
case TCPClient:
// this->disconnectRemoteServer();
break;
case UDP:
// this->unbindAddrAndPort();
break;
default:break;
}
ui->cbbNetType->setEnabled(true);
}
}
现在根据上述流程,将相关函数进行完善;
2.7 UDP通信
UDP每次发送数据报都要指定目标地址端口号;
QUdpSocket *udp;在头文件中声明UDP类用于实现UDP通信;
进行UDP数据接收需要使用
QUdpSocket::bid()函数绑定一个端口;用于接收传入数据报,
udp->bind( *ip, port );
获取客户端IP;获取端口号;
监听函数,返回bool值;
2.7.1 添加状态显示StatusBar
右击,选择添加状态栏;
在提升类的名称中命名为statusBar
ui->statusBar->showMessage("正在监听");
2.7.2 定义槽函数
public slots:
void slots_udpRxCallback();
2.7.3 插入时间戳
/**
* @brief MainWindow::insertTimeStamp 插入时间戳前缀
* @param tmp
*/
void MainWindow::insertTimeStamp(QString &tmp)
{
QTime currentTime = QTime::currentTime();
tmp.prepend( "[" + currentTime.toString("hh:mm:ss:zzz") + "]");
}
2.7.4 数据记录在RxBrowser中
/**
* @brief MainWindow::logInRxBrowser 数据记录在RxBrowser中
* @param ipInfo ip,port
* @param tmp
*/
void MainWindow::logInRxBrowser(QString ipInfo, QByteArray &tmp)
{
if(ui->ckbTimeStamp->isChecked())
this->insertTimeStamp(ipInfo);
QString msg;
if( ui->ckbHexRx->isChecked() ){
msg = tmp.toHex(' ').toUpper();
}else{
msg = QString::fromLocal8Bit(tmp);
}
ui->txtRxBrowser->append( ipInfo + msg );
}
判断;时间戳添加;
判断;转化为十六进制;
txtRxBrowser文本框输出ip信息和数据信息
2.7.5 发出信息插入服务器信息
/**
* @brief MainWindow::insertServerInfo 插入服务器信息
* @param tmp
*/
void MainWindow::insertTxInfo(QString &tmp)
{
QString ip ;
QString port;
NetworkType networkType = (NetworkType)ui->cbbNetType->currentIndex();
switch(networkType){
case TCPServer :
ip = server->serverAddress().toString();
port = QString::number( server->serverPort() );
break;
case TCPClient:
ip = client->localAddress().toString();
port = QString::number( client->localPort() );
break;
case UDP:
ip = ui->txtIP->text();
port = ui->txtPort->text();
break;
}
tmp.prepend( "[" + QString("Tx from %1:%2").arg(ip, port) +"]\n" );
}
2.7.6 接收插入发送端IP和端口号
/**
* @brief MainWindow::insertRxInfo 插入发送端的IP和端口号
* @param client
* @param tmp
*/
void MainWindow::insertRxInfo(QNetworkDatagram *datagram, QString &tmp)
{
QString ip = datagram->senderAddress().toString();
QString port = QString::number( datagram->senderPort() );
tmp.prepend( "[" + QString("Rx from %1:%2").arg(ip, port) +"]\n" );
}