文章目录
写在前面
最近由于工作需要,需要用Qt写一个udp通信的程序,这个udp程序只用来接收数据,不发送(等之后需要发送数据了在进行补充)于是就有了下面一个程序,我先将功能部分拆解,然后之后会将所有的源码放在下面,有需要的可以直接拷贝到下面,写这篇文章主要是为了分享给有需要的人,同时也记录下来方便自己以后回忆。
实现的主要功能
点击连接网络,会将本地的IP自动分配一个,然后建立连接,此时状态会显示建立udp连接成功,同时会在本地文件夹中生成一个
.log文件,这个文件作为日志文件,之后会将接收的数据放到log日志文件中去。清空显示则是清空接收。 接收计数则是显示接收的数据数量(具体的数据格式与数量之间的关系自己去算一下吧~)
功能实现
获取ip
连接udp
生成log文件
判断字符串格式
因为工作需要,需要判断一些错误,将含有ERR的字符串设置为红色,其余设置为绿色。
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size(), &address, &port);
tmpIPPort =QHostAddress(address.toIPv4Address()).toString()+ ":" + QString::number(port, 10);
QString rcvMsg = QString::fromUtf8(datagram, datagram.size());
if(CurIPPort != tmpIPPort) {
CurIPPort = tmpIPPort;
// if(ui->ReceiveTextEdit->toPlainText().size() == 0) {
// ui->ReceiveTextEdit->moveCursor(QTextCursor::Start);
// }
// 设置文本颜色
QTextCharFormat fmt;
fmt.setForeground(Qt::green);
ui->ReceiveTextEdit->mergeCurrentCharFormat(fmt);
ui->ReceiveTextEdit->appendPlainText(tr("【数据来自") + CurIPPort + tr("】")+"\n"); //添加数据 192.~
rcvDataCnt += datagram.size();
ui->lEdit_RcvCnt->setText(QString::number(rcvDataCnt, 10));
}
// // 设置文本颜色
// QTextCharFormat fmt;
// fmt.setForeground(Qt::green);
int index = 0;
QTextCharFormat fmt;
while(index<rcvMsg.size()){
if(rcvMsg.mid(index).toLower().contains("err")){
fmt.setForeground(Qt::red);
// qDebug()<<"进入红色";
break;
}
else{
fmt.setForeground(Qt::green);
// qDebug()<<"进入绿色";
}
index++;
}
if(ui->ReceiveTextEdit->toPlainText().size() == 0) {
ui->ReceiveTextEdit->moveCursor(QTextCursor::Start);
ui->ReceiveTextEdit->mergeCurrentCharFormat(fmt);
ui->ReceiveTextEdit->insertPlainText(rcvMsg);
// qDebug()<<rcvMsg;
}else{
// 插入文本
ui->ReceiveTextEdit->moveCursor(QTextCursor::End);
ui->ReceiveTextEdit->mergeCurrentCharFormat(fmt);
ui->ReceiveTextEdit->insertPlainText(rcvMsg);
}
使用Plaintextedit的insertPlainText方法设置不会拖动而重复行
在我们使用plaintextedit的insertPlainText方法的时候有时候鼠标光标移动会让内容错行,虽然可以使用appendPlainText方法防止移动,但是append方法会自动换行,这就让人很是难受,所以需要通过设置方法来设置这些问题。
//在界面初始化的时候将plaintextedit设置成这个
ui->ReceiveTextEdit->ensureCursorVisible();
ui->ReceiveTextEdit->setReadOnly(true);
用光标这样会有一个Bug就是在你使用clear方法的时候,你在接收数据界面是不会显示的,就很让人难过,于是我通过在使用clear方法之后重新设置一下光标就可以了。
界面实现效果
.log文件
源码
.pro文件
#-------------------------------------------------
#
# Project created by QtCreator 2014-04-10T19:21:07
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = NetAssistant
target.path=/usr/local/bin
INSTALLS=target
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp
HEADERS += \
define.h \
mainwindow.h
FORMS += \
mainwindow.ui
RESOURCES += \
qrc.qrc
QT +=network
RC_FILE += icon.rc
DISTFILES += \
android/AndroidManifest.xml
TRANSLATIONS += language/English.ts \
language/Chinese.ts
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
.h文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtNetwork>
#include <QDataStream>
#include <QTextStream>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void setUdpGuiExt();
void setTcpSvrGuiExt();
void setTcpClientGuiExt();
QList<int> tcpClientSocketDescriptorList;
int TcpClientLinkCnt;
QString logFilePath;
signals:
void sendDataToClient(char *msg, int length, int socketDescriptor, int socketDescriptorEx);
private slots:
void on_pBtnNetCnnt_clicked(bool checked);
void on_pBtnResetCnt_clicked();
char ConvertHexChar(char ch);
char ConvertHexStr(QString hexSubStr);
//=======UDP========
void udpDataReceived();
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
QString m_ip;
//-----UDP-----
QUdpSocket *udpSocket;
QHostAddress lhAddr;
int lhPort;
QHostAddress rmtAddr;
int rmtPort;
//Global state
unsigned int rcvDataCnt;
unsigned int sndDataCnt;
//bool NetState;
QTimer *timer;
bool loopSending;
QString CurIPPort;
QString CurPath;
QFile *curFile;
QTranslator translator;
};
#endif // MAINWINDOW_H
.cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTranslator>
#include "define.h"
#include <QMessageBox>
#include <stdlib.h>
#include <QString>
#include <QTimer>
#include <QInputDialog>
#include <QDateTime>
#include <QFile>
#include <QDateTime>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->ReceiveTextEdit->ensureCursorVisible();
ui->ReceiveTextEdit->setReadOnly(true);
//设置程序的开启默认画面
setUdpGuiExt();
/*创建.log文件*/
QString logFileName = "logfile.log"; // 日志文件名
// 获取当前应用程序所在的目录
QString currentPath = QCoreApplication::applicationDirPath();
// 拼接日志文件的完整路径
logFilePath = currentPath + "/" + logFileName;
if (!QFile::exists(logFilePath)) {
QFile logFile(logFilePath);
if (logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
logFile.close();
ui->label->setText("日志文件已生成");
qDebug() << "Log file created.";
} else {
ui->label->setText("日志文件生成失败");
qDebug() << "Failed to create log file.";
// 处理文件创建失败的情况
}
}
else{
ui->label->setText("日志文件已存在");
}
//获取本机的IP地址,并初始化相应的控件属性和变量
QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// use the first non-localhost IPv4 address
for(int i = 0; i < ipAddressesList.size(); ++i) {
if(ipAddressesList.at(i) != QHostAddress::LocalHost && ipAddressesList.at(i).toIPv4Address()) {
m_ip = ipAddressesList.at(i).toString();
break;
}
}
// 设置获取的IP到相应文本框
//ui->lEditIpAddr->setText(m_ip);
// ui->lEditUdpIP->setText(m_ip);
//初始化全局变量
rcvDataCnt = 0;
sndDataCnt = 0;
TcpClientLinkCnt = 0;
//NetState = false;
loopSending = false;
CurIPPort = "";
CurPath = "";
curFile = 0;
}
MainWindow::~MainWindow()
{
delete ui;
}
/**********************************************************/
//Function for connection button
void MainWindow::on_pBtnNetCnnt_clicked(bool checked)
{
if(checked) { //切换到链接状态
//建立UDP链接
udpSocket = new QUdpSocket(this);
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(udpDataReceived()));
lhAddr.setAddress(m_ip);
lhPort =1686;
rmtAddr.setAddress("192.168.1.16");
rmtPort = 1689;
bool result = udpSocket->bind(lhPort);
if(!result)
{
ui->pBtnNetCnnt->setChecked(0);
QMessageBox::information(this, tr("错误"), tr("UDP绑定端口失败!"));
return;
}
ui->CurState->setText(tr("建立UDP连接成功"));
ui->pBtnNetCnnt->setText(tr("断开网络"));
//NetState = true;
} else { //切换到断开状态
//断开UDP链接
udpSocket->close();
delete udpSocket;
ui->pBtnNetCnnt->setText(tr("连接网络"));
ui->CurState->setText(tr(""));
// NetState = false;
}
}
/**********************************************************/
//set UDP mode
void MainWindow::setUdpGuiExt()
{
// ui->labelSpaceUdp->setVisible(true);
// ui->lEditUdpIP->setVisible(true);
// ui->labelUdp->setVisible(true);
// ui->labelUdp1->setVisible(true);
// ui->lEditUdpPort->setVisible(true);
// ui->labelSpaceClients->setVisible(false);
}
/**********************************************************/
//udp data received
void MainWindow::udpDataReceived()
{
QHostAddress address;
quint16 port;
QString tmpIPPort = "";
while(udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size(), &address, &port);
tmpIPPort =QHostAddress(address.toIPv4Address()).toString()+ ":" + QString::number(port, 10);
QString rcvMsg = QString::fromUtf8(datagram, datagram.size());
if(CurIPPort != tmpIPPort) {
CurIPPort = tmpIPPort;
// if(ui->ReceiveTextEdit->toPlainText().size() == 0) {
// ui->ReceiveTextEdit->moveCursor(QTextCursor::Start);
// }
// 设置文本颜色
QTextCharFormat fmt;
fmt.setForeground(Qt::green);
ui->ReceiveTextEdit->mergeCurrentCharFormat(fmt);
ui->ReceiveTextEdit->appendPlainText(tr("【数据来自") + CurIPPort + tr("】")+"\n"); //添加数据 192.~
rcvDataCnt += datagram.size();
ui->lEdit_RcvCnt->setText(QString::number(rcvDataCnt, 10));
}
// // 设置文本颜色
// QTextCharFormat fmt;
// fmt.setForeground(Qt::green);
int index = 0;
QTextCharFormat fmt;
while(index<rcvMsg.size()){
if(rcvMsg.mid(index).toLower().contains("err")){
fmt.setForeground(Qt::red);
// qDebug()<<"进入红色";
break;
}
else{
fmt.setForeground(Qt::green);
// qDebug()<<"进入绿色";
}
index++;
}
if(ui->ReceiveTextEdit->toPlainText().size() == 0) {
ui->ReceiveTextEdit->moveCursor(QTextCursor::Start);
ui->ReceiveTextEdit->mergeCurrentCharFormat(fmt);
ui->ReceiveTextEdit->insertPlainText(rcvMsg);
// qDebug()<<rcvMsg;
}else{
// 插入文本
ui->ReceiveTextEdit->moveCursor(QTextCursor::End);
ui->ReceiveTextEdit->mergeCurrentCharFormat(fmt);
ui->ReceiveTextEdit->insertPlainText(rcvMsg);
}
/*输出到.log文件中*/
QFile logFile(logFilePath);
if (logFile.open(QIODevice::Append | QIODevice::Text)) {
QTextStream logStream(&logFile);
logStream << QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss] ")
<< rcvMsg ;
logFile.close();
} else {
qDebug() << "Failed to open log file for writing.";
// 处理日志文件写入失败的情况
}
// ui->ReceiveTextEdit->insertPlainText(rcvMsg);
// ui->ReceiveTextEdit->appendPlainText(rcvMsg);
//ui->ReceiveTextEdit->insertPlainText("\n");
//ui->ReceiveTextEdit->ensureCursorVisible();
//接收计数增加
rcvDataCnt += datagram.size();
ui->lEdit_RcvCnt->setText(QString::number(rcvDataCnt, 10));
}
}
/**********************************************************/
//reset the conters
void MainWindow::on_pBtnResetCnt_clicked()
{
sndDataCnt = 0;
rcvDataCnt = 0;
ui->lEdit_RcvCnt->setText(QString::number(0, 10));
}
/***********************************************/
//转化十六进制中的字符到ASCII
char MainWindow::ConvertHexChar(char ch)
{
if((ch >= '0') && (ch <= '9'))
return ch - 0x30;
else if((ch >= 'A') && (ch <= 'F'))
return ch - 'A' + 10;
else if((ch >= 'a') && (ch <= 'f'))
return ch - 'a' + 10;
else return (-1);
}
/***********************************************/
//转化十六进制字符到ASCII字母
char MainWindow::ConvertHexStr(QString hexSubStr)
{
char ch = 0;
if(hexSubStr.length() == 2) {
// ch = ConvertHexChar(hexSubStr.at(0).toAscii())*16+ ConvertHexChar(hexSubStr.at(1).toAscii()); //qt4.8
ch = ConvertHexChar(hexSubStr.at(0).toLatin1()) * 16 + ConvertHexChar(hexSubStr.at(1).toLatin1());
} else if(hexSubStr.length() == 1) {
//ch = ConvertHexChar(hexSubStr.at(0).toAscii()); //qt4.8
ch = ConvertHexChar(hexSubStr.at(0).toLatin1());
}
return ch;
}
void MainWindow::on_pushButton_clicked()
{
ui->ReceiveTextEdit->clear();
}