49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)
8.9更新说明
如下图所示,支持十六进制收发,下载地址已经更新.源码下载地址:https://download.csdn.net/download/qq_37997682/11504836
在上章 48.QT-网络通信讲解1,我们学习了网络通信基础后,本章便来实战一篇.
PS:支持客户端和服务器,提供源码,并且服务器支持多客户端连入,并且可以指定与个别客户端发送数据,也可以给所有连入的客户端发送数据.
1.效果图所下所示:
如下图所示,当服务器状态下,如果有客户端连入,会提示客户端信息:
2.效果操作
客户端操作:
服务器操作:
从上面操作可以看出,服务器支持多客户端连入,并且可以指定与个别客户端发送数据,也可以给所有连入的客户端发送数据.
3.首先创建UI
4.注意事项
不管是服务器还是客户端,都可以通过peerAddress()和peerPort()来获取目标地址和目标端口
4.1服务器监听时
比如服务器,则可以通过QTcpSocket的peerAddress()则可以获取连入的客户端地址
也可以通过children()来获取所有连入的客户端(需要注意的是也会获取到服务器本身的tcp地址和端口),示例如下:
QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
foreach (QTcpSocket *tcp, m_tcps)
{
qDebug() << "Address:" << tcp->peerAddress ();
qDebug() << "Port:" << tcp->peerPort ();
}
如果我们只向连入的客户端某个端口发送数据时,就可以通过上面的方式筛选出来.
这样做的话如果觉得很麻烦,也可以将之前连接上的客户端存到QList里再进行筛选.
4.2 QTcpSocket步骤
- 首先通过connectToHost()来连接服务器.
- 然后调用waitForConnected()来判断是否连接服务器超时
- 当我们接收到服务器数据的时候,则会发出readyRead()信号,然后再进行read ()读取发来的数据
- 发送数据时,则调用write()函数进行发送,当bytesWritten()信号函数触发时,便可以获取成功发送的数据长度.
注意:如果read到的数据长度量不是自己想要的,此时我们便可以通过bytesAvailable()来读取接收到的数据长度量.当达到多少时,再进行read ()读取.
4.3 QTcpServer步骤
- 首先通过listen(QHostAddress::AnyIPv4, port)来监听所有来自IPV4的客户端
- 当有新的客户端连接服务器的时候,会自动触发newConnection()信号函数,然后我们可以通过通过QTcpSocket * nextPendingConnection()成员函数来获取当前连接上的新的客户端类.然后再对QTcpSocket来进行信号槽绑定
- 当客户端发来数据的时候,则可以通过我们定义的onServerDataReady()来读取数据
- 当我们向某个连接的客户端发送数据时,则通过m_server.findChildren()来筛选出来,然后write即可.
5.代码介绍
5.1 头文件介绍
#ifndef WIDGET_H
#define WIDGET_H #include <QWidget>
#include <QTcpSocket>
#include <QTcpServer>
#include <QMessageBox> namespace Ui {
class Widget;
} class Widget : public QWidget
{
Q_OBJECT QTcpSocket m_client;
QTcpServer m_server;
QString targetAddr;
int targetPort;
public:
explicit Widget(QWidget *parent = );
~Widget();
private slots:
void on_btn_switch_clicked();
void on_tcpMode_currentIndexChanged(int index);
void on_btn_send_clicked(); //发送按钮
void on_betn_clear_clicked(); //清空按钮 //客户端槽函数
void onClientConnected();
void onClientDisconnected();
void onClientDataReady();
void onClientBytesWritten(qint64 bytes);
void onClientErr(QAbstractSocket::SocketError socketError); //服务器槽函数
void onServerNewConnection();
void onServerConnected();
void onServerDisconnected();
void onServerDataReady();
void onServerBytesWritten(qint64 bytes);
private:
void startConnect(bool ison); void initClientSignals(); //初始化客户端信号槽
bool startClient(); //启动客户端 void initServerSignals(); //初始化客户端信号槽
bool startServer(); //启动服务器 Ui::Widget *ui;
}; #endif // WIDGET_H
5.2 widget.cpp介绍
该cpp主要是用来处理界面操作的函数
#include "widget.h"
#include "ui_widget.h" Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
startConnect(false);
on_tcpMode_currentIndexChanged();
initClientSignals(); //初始化客户端信号槽
initServerSignals(); //初始化服务器信号槽 //限制只能数字输入
QRegExp regx("[0-9]+$");
QValidator *validator = new QRegExpValidator(regx, this );
ui->ipAddr1->setValidator( validator );
ui->ipAddr2->setValidator( validator );
ui->ipAddr3->setValidator( validator );
ui->ipAddr4->setValidator( validator );
ui->ipPort->setValidator( validator );
}
void Widget::on_tcpMode_currentIndexChanged(int index)
{
if(index==) //clent
{
ui->ipPortLabel->setText("服务器端口号:");
ui->ipAddLabel->show();
ui->ipAdds->show();
ui->targetLabel->hide();
ui->targetObject->hide();
}
else
{
ui->ipAddLabel->hide();
ui->ipAdds->hide();
ui->ipPortLabel->setText("本地端口号:");
}
} void Widget::on_btn_switch_clicked() //切换连接开关
{
bool ret;
if(ui->btn_switch->text()=="打开连接")
{
if(ui->tcpMode->currentIndex()==) //启动客户端
ret=startClient() ;
else
ret=startServer();
if(ret)
startConnect(true);
}
else
{
if(ui->tcpMode->currentIndex()==) //启动客户端
m_client.close();
else
{
if( m_server.isListening() )
{
QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
foreach (QTcpSocket *tcp, m_tcps)
{
tcp->close();
}
m_server.close();
}
}
startConnect(false);
}
} void Widget::startConnect(bool ison)
{
if(!ison)
{
ui->btn_switch->setStyleSheet("color:blue;border: 1px solid blue");
ui->btn_switch->setText("打开连接"); //使能
ui->ipAddr1->setEnabled(true);
ui->ipAddr2->setEnabled(true);
ui->ipAddr3->setEnabled(true);
ui->ipAddr4->setEnabled(true);
ui->tcpMode->setEnabled(true);
ui->ipPort->setEnabled(true);
ui->localPort->setText("");
}
else
{
ui->btn_switch->setStyleSheet("color:red;border: 1px solid red");
ui->btn_switch->setText("关闭连接");
//失能
ui->ipAddr1->setEnabled(false);
ui->ipAddr2->setEnabled(false);
ui->ipAddr3->setEnabled(false);
ui->ipAddr4->setEnabled(false);
ui->tcpMode->setEnabled(false);
ui->ipPort->setEnabled(false);
targetAddr="";
targetPort=;
ui->sendLenLabel->setText("");
}
} void Widget::on_betn_clear_clicked()
{
ui->recvEdit->clear();
targetAddr="";
targetPort=;
} void Widget::on_btn_send_clicked()
{
if(ui->btn_switch->text()!="打开连接")
{
if(ui->tcpMode->currentIndex()==) //客户端
{
m_client.write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length());
}
else
{
if(ui->targetObject->currentText()!="所有对象")
{
QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
foreach (QTcpSocket *tcp, m_tcps)
{
if(ui->targetObject->currentText() == tcp->objectName())
{
tcp->write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length());
break;
}
}
}
else //所有连接上的客户端都发送一遍
{
QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
foreach (QTcpSocket *tcp, m_tcps)
{
tcp->write(ui->sendEdit->toPlainText().toLocal8Bit(),ui->sendEdit->toPlainText().toLocal8Bit().length());
}
}
}
}
} Widget::~Widget()
{
QList<QTcpSocket *> m_tcps = m_server.findChildren<QTcpSocket *>();
foreach (QTcpSocket *tcp, m_tcps)
{
tcp->close();
}
if(m_client.isOpen())
{
m_client.close();
qDebug()<<"m_client close";
}
delete ui;
}
5.3 clentHandler.cpp介绍
该cpp主要用来处理客户端操作相关的文件.
#include "widget.h"
#include "ui_widget.h" void Widget::initClientSignals() //初始化客户端信号槽
{
connect(&m_client, SIGNAL(connected()), this, SLOT(onClientConnected()));
connect(&m_client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()));
connect(&m_client, SIGNAL(readyRead()), this, SLOT(onClientDataReady()));
connect(&m_client, SIGNAL(bytesWritten(qint64)), this, SLOT(onClientBytesWritten(qint64)));
connect(&m_client, SIGNAL(error(QAbstractSocket::SocketError )), this, SLOT(onClientErr(QAbstractSocket::SocketError)));
}
bool Widget::startClient() //启动客户端
{
QString ip = QString("%1.%2.%3.%4").arg(ui->ipAddr1->text()).arg(ui->ipAddr2->text()).arg(ui->ipAddr3->text()).arg(ui->ipAddr4->text());
qDebug()<<ip;
m_client.connectToHost(ip, ui->ipPort->text().toInt());
if(m_client.waitForConnected())
{
return true;
}
else
{
QMessageBox::information(this,"提示",QString("连接超时"),QMessageBox::Ok);
return false;
}
} void Widget::onClientConnected()
{
startConnect(true);
QMessageBox::information(this,"提示","连接成功",QMessageBox::Ok);
ui->localPort->setText(QString("%1").arg(m_client.localPort())); //显示本地端口号
}
void Widget::onClientDisconnected()
{
startConnect(false);
QMessageBox::information(this,"提示","断开完成",QMessageBox::Ok);
}
void Widget::onClientDataReady()
{
if(m_client.peerAddress().toString()!=targetAddr || m_client.peerPort()!=targetPort )
{
targetAddr = m_client.peerAddress().toString();
targetPort = m_client.peerPort();
ui->recvEdit->insertPlainText("[接受来自"+ targetAddr+":"+QString("%1").arg(targetPort)+"]:\r\n");
}
ui->recvEdit->moveCursor(QTextCursor::End);
ui->recvEdit->insertPlainText(QString::fromLocal8Bit(m_client.readAll())+"\r\n");
}
void Widget::onClientBytesWritten(qint64 bytes)
{
qDebug() << "onBytesWritten:" << bytes;
ui->sendLenLabel->setText(QString("%1").arg(ui->sendLenLabel->text().toInt()+bytes));
} void Widget::onClientErr(QAbstractSocket::SocketError socketError)
{
qDebug()<<"onClientErr:"<<socketError;
m_client.close();
startConnect(false);
QMessageBox::information(this,"提示",QString("连接失败:%1").arg((int)socketError),QMessageBox::Ok);
}
5.4 serverHandler.cpp介绍
该cpp主要用来处理服务器操作相关的文件
#include "widget.h"
#include "ui_widget.h" void Widget::initServerSignals() //初始化信号槽
{
connect(&m_server, SIGNAL(newConnection()), this, SLOT(onServerNewConnection()));
}
bool Widget::startServer() //启动服务器
{
if(m_server.listen(QHostAddress::AnyIPv4,ui->ipPort->text().toInt())) //只监听IPV4的所有客户端
{
ui->targetLabel->show();
ui->targetObject->show();
ui->localPort->setText(QString("%1").arg(m_server.serverPort()));
return true;
}
else
return false;
}
void Widget::onServerNewConnection()
{
qDebug() << "onNewConnection";
QTcpSocket* tcp = m_server.nextPendingConnection(); //获取新的客户端信息
QString info=tcp->peerAddress().toString()+":"+QString("%1").arg(tcp->peerPort());
ui->targetObject->addItem(info);
QMessageBox::information(this,"提示",QString("新的客户端连入:%1").arg(info),QMessageBox::Ok);
tcp->setObjectName(info); //设置名称,方便查找
connect(tcp, SIGNAL(connected()), this, SLOT(onServerConnected()));
connect(tcp, SIGNAL(disconnected()), this, SLOT(onServerDisconnected()));
connect(tcp, SIGNAL(readyRead()), this, SLOT(onServerDataReady()));
connect(tcp, SIGNAL(bytesWritten(qint64)), this, SLOT(onServerBytesWritten(qint64)));
}
void Widget::onServerConnected()
{ } void Widget::onServerDisconnected()
{
QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender());
if( tcp != NULL ) //从连接对象中移除掉
{
qDebug() << "onServerDisconnected";
qDebug() << "Local Address:" << tcp->peerAddress();
qDebug() << "Local Port:" << tcp->peerPort();
QString info=tcp->peerAddress().toString()+":"+QString("%1").arg(tcp->peerPort());
QMessageBox::information(this,"提示",QString("客户端断开连接:%1").arg(info),QMessageBox::Ok); int index = ui-> targetObject ->findText(info);
if(index>=)
ui->targetObject->removeItem(index);
}
}
void Widget::onServerDataReady()
{
QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender());
if(tcp->peerAddress().toString()!=targetAddr || tcp->peerPort()!=targetPort )
{
targetAddr = tcp->peerAddress().toString();
targetPort = tcp->peerPort();
ui->recvEdit->insertPlainText("[接受来自"+ targetAddr+":"+QString("%1").arg(targetPort)+"]:\r\n");
}
ui->recvEdit->moveCursor(QTextCursor::End);
ui->recvEdit->insertPlainText(QString::fromLocal8Bit(tcp->readAll())+"\r\n");
}
void Widget::onServerBytesWritten(qint64 bytes)
{
qDebug() << "onBytesWritten:" << bytes;
ui->sendLenLabel->setText(QString("%1").arg(ui->sendLenLabel->text().toInt()+bytes));
}
49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)的更多相关文章
- unix下网络编程之I/O复用(三)
poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时, ...
- Java网络编程之TCP、UDP
Java网络编程之TCP.UDP 2014-11-25 15:23 513人阅读 评论(0) 收藏 举报 分类: java基础及多线程(28) 版权声明:本文为博主原创文章,未经博主允许不得转载. ...
- 网络编程之socket
网络编程之socket socket:在网络编程中的一个基本组件,也称套接字. 一个套接字就是socket模块中的socket类的一个实例. 套接字包括两个: 服务器套接字和客户机套接字 套接字的实例 ...
- [深入浅出WP8.1(Runtime)]网络编程之HttpClient类
12.2 网络编程之HttpClient类 除了可以使用HttpWebRequest类来实现HTTP网络请求之外,我们还可以使用HttpClient类来实现.对于基本的请求操作,HttpClient类 ...
- java网络编程之TCP通讯
java中的网络编程之TCP协议的详细介绍,以及如何使用,同时我在下面举2例说明如何搭配IO流进行操作, /* *TCP *建立连接,形成传输数据的通道: *在连接中进行大数据量传输: *通过三次握手 ...
- QT核心编程之Qt线程 (c)
QT核心编程之Qt线程是本节要介绍的内容,QT核心编程我们要分几个部分来介绍,想参考更多内容,请看末尾的编辑推荐进行详细阅读,先来看本篇内容. Qt对线程提供了支持,它引入了一些基本与平台无关的线程类 ...
- python3网络编程之socketserver
本节主要是讲解python3网络编程之socketserver,在上一节中我们讲到了socket.由于socket无法支持多用户和多并发,于是就有了socket server. socket serv ...
- 网络编程之UDP编程
网络编程之UDP编程 UDP协议是一种不可靠的网络协议,它在通信的2端各建立一个Socket,但是这个Socket之间并没有虚拟链路,这2个Socket只是发送和接受数据的对象,Java提供了Data ...
- 网络编程之TCP编程
网络编程之TCP编程 前面已经介绍过关于TCP协议的东西,这里不做赘述.Java对于基于TCP协议的网络通信提供了良好的封装,Java使用socket对象来代表两端的通信窗口,并通过Socket产生I ...
随机推荐
- QT MSVC编译中文乱码问题
两种解决方案: 1,在文件头添加 #if _MSC_VER >= 1600 #pragma execution_character_set("utf-8") #endif 注 ...
- Python 3网络爬虫开发实战中文 书籍软件包(原创)
Python 3网络爬虫开发实战中文 书籍软件包(原创) 本书书籍软件包为本人原创,想学爬虫的朋友你们的福利来了.软件包包含了该书籍所需的所有软件. 因为软件导致这个文件比较大,所以百度网盘没有加速的 ...
- 浅说——状压DP
第一次没认真听,没听懂.(有点难) 第二次才搞懂,主要位运算太烦了!!! 位运算基础知识: 名称 符号 规则 按位与 & 全一则一,否则为零 按位或 | 有一则一,否则为零 按位取反 ~ 是零 ...
- HDU 1724:Ellipse(自适应辛普森积分)
题目链接 题意 给出一个椭圆,问一个[l, r] 区间(蓝色区域)的面积是多少. 思路 自适应辛普森积分 具体一些分析如上. 很方便,套上公式就可以用了. 注意 eps 的取值影响了跑的时间,因为决定 ...
- scrapy实战4 GET方法抓取ajax动态页面(以糗事百科APP为例子):
一般来说爬虫类框架抓取Ajax动态页面都是通过一些第三方的webkit库去手动执行html页面中的js代码, 最后将生产的html代码交给spider分析.本篇文章则是通过利用fiddler抓包获取j ...
- Egret入门学习日记 --- 第四篇
第四篇(学习篇) 好了,今天继续把昨天的问题解决了. 今天见鬼了. 现在界面又出来了.唯一我动过的地方,应该就是这里: 是的,我点了一下刷新.之后,不管我怎么创建新的EXML文件,放在src目录,还是 ...
- 小程序组件-swipe多页切换,并支持下拉刷新,上拉加载,menu动态联动切换
前言 最近一个小程序项目中遇到一个需求,就是实现类似资讯类app的多页面切换的那种效果, 如下图: 最终效果: 1.功能分析 首先实现这个功能分为三步: 实现顶部menu菜单 实现多页面滑动切换 支持 ...
- 《Predict Anchor Links across Social Networks via an Embedding Approach》阅读笔记
基本信息 文献:Predict Anchor Links across Social Networks via an Embedding Approach 时间:2016 期刊:IJCAI 引言 预测 ...
- android_sdcard读写(一)
现在的android手机其实就是一个小小的掌上电脑,平时电脑有的硬件它估计也有了.这次本人研究下了其中充当手机硬盘的角色,就是sdcard.这是一个保存应用程序的好地方. 老规矩,上代码,学习代码才是 ...
- mplayer+ffmpeg 组合截图
mplayer截图的优点:对于一个时长很长的视频,可以任意指定一个时间点截图,mplayer会直接跳到这个时间点开始解码截图: 缺点:由于是直接跳到指定的时间点,也就是直接跳过了之前的帧,这样解码出来 ...