Qt UDP的初步使用

 为了使用Qt自带的Socket进行网络编程,先必须熟悉Socket编程的原理,另外还需对Qt一些基本类的操作比较熟悉。由于刚接触不久,所以还是以看人家的代码来学习。这次主要是学Qt下UDP的编程,且熟悉一些Qt下代码的编写流程,所以本文参照的是《Qt及Qt Quick开发实战精解》一书中的第5个例子:局域网聊天工具中的UDP聊天部分。

     另外http://www.yafeilinux.com/ 上有其源码和相关教程下载。

     该程序实现的功能是:局域网内,每个用户登录到聊天软件,则软件界面的右端可以显示在线用户列表,分别显示的是用户名,主机名,ip地址。软件左边那大块是聊天内容显示界面,这里局域网相当于qq中的qq群,即群聊。每个人可以在聊天输入界面中输入文字并发送。其聊天界面如下:

    

     该程序实现的是每个用户登录既是客户端又是服务器端,这就需要看你站在哪个角度看问题了。简单的说,当用户发送信息给别人时就是客户端,当接收别人的信息是就可以看做是服务器端。

  下面分服务器端和客户端2部分来介绍。

  服务器端:建立一个UDP Socket并绑定在固定端口后,用信号与槽的方式进行监听是否有数据来临。如果用,接收其数据并分析数据的消息类型,如果消息是新用户登录则更新用户列表并在聊天显示窗口中添加新用户上线通知;同理,如果是用户下线,则在用户列表中删除该用户且在聊天显示窗口中显示下线通知;如果是聊天消息,则接收该消息并且在窗口中显示。其流程图如下:

    

  客户端:首先当客户端登录时,获取本机的用户名,计算机名和ip地址,并广播给局域网的服务器更新用户列表。然后当客户端需要发送信息时,则在聊天输入栏中输入信息并按发送键发送聊天内容,当然于此同时也广播本地系统的各种信息。其流程图如下:

   

  程序主要代码和注释如下:

widget.h:

复制代码
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
class QUdpSocket;namespace Ui {
class Widget;
}// 枚举变量标志信息的类型,分别为消息,新用户加入,用户退出,文件名,拒绝接受文件
enum MessageType{Message, NewParticipant, ParticipantLeft, FileName, Refuse};class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();protected:void newParticipant(QString userName,QString localHostName, QString ipAddress);void participantLeft(QString userName,QString localHostName, QString time);void sendMessage(MessageType type, QString serverAddress="");QString getIP();QString getUserName();QString getMessage();private:Ui::Widget *ui;QUdpSocket *udpSocket;qint16 port;private slots:void processPendingDatagrams();void on_sendButton_clicked();
};#endif // WIDGET_H
复制代码

widget.cpp:

复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QUdpSocket>
#include <QHostInfo>
#include <QMessageBox>
#include <QScrollBar>
#include <QDateTime>
#include <QNetworkInterface>
#include <QProcess>Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);udpSocket = new QUdpSocket(this);//创建一个QUdpSocket类对象,该类提供了Udp的许多相关操作port = 45454;//此处的bind是个重载函数,连接本机的port端口,采用ShareAddress模式(即允许其它的服务连接到相同的地址和端口,特别是//用在多客户端监听同一个服务器端口等时特别有效),和ReuseAddressHint模式(重新连接服务器)udpSocket->bind(port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);//readyRead()信号是每当有新的数据来临时就被触发connect(udpSocket, SIGNAL(readyRead()), this, SLOT(processPendingDatagrams()));sendMessage(NewParticipant);//打开软件时就向外发射本地信息,让其他在线用户得到通知
}Widget::~Widget()
{delete ui;
}// 使用UDP广播发送信息,MessageType是指头文件中的枚举数据类型
//sendMessage即把本机的主机名,用户名+(消息内容后ip地址)广播出去
void Widget::sendMessage(MessageType type, QString serverAddress)
{QByteArray data;    //字节数组//QDataStream类是将序列化的二进制数据送到io设备,因为其属性为只写QDataStream out(&data, QIODevice::WriteOnly);QString localHostName = QHostInfo::localHostName();//返回主机名,QHostInfo包含了一些关于主机的静态函数QString address = getIP();    //调用自己类中的getIP()函数//将type,getUserName(),localHostName按照先后顺序送到out数据流中,消息类型type在最前面out << type << getUserName() << localHostName;switch(type){case Message :if (ui->messageTextEdit->toPlainText() == "") {    //将输入框里的文字转化成纯文本发送//当发送的文本为空时创建一个警告信息窗口,tr函数为译本函数,即译码后面的text内容QMessageBox::warning(0,tr("警告"),tr("发送内容不能为空"),QMessageBox::Ok);    return;}out << address << getMessage();//将ip地址和得到的消息内容输入out数据流ui->messageBrowser->verticalScrollBar()    //返回垂直条->setValue(ui->messageBrowser->verticalScrollBar()->maximum());//设置垂直滑动条的最大值break;case NewParticipant :out << address;    //为什么此时只是输出地址这一项呢?因为此时不需要传递聊天内容break;case ParticipantLeft :break;case FileName :break;case Refuse :break;}//一个udpSocket已经于一个端口bind在一起了,这里的data是out流中的data,最多可以传送8192个字节,但是建议不要超过//512个字节,因为这样虽然可以传送成功,但是这些数据需要在ip层分组,QHostAddress::Broadcast是指发送数据的目的地址//这里为本机所在地址的广播组内所有机器,即局域网广播发送udpSocket->writeDatagram(data,data.length(),QHostAddress::Broadcast, port);//将data中的数据发送
}// 接收UDP信息
void Widget::processPendingDatagrams()
{//hasPendingDatagrams返回true时表示至少有一个数据报在等待被读取while(udpSocket->hasPendingDatagrams()){QByteArray datagram;//pendingDatagramSize为返回第一个在等待读取报文的size,resize函数是把datagram的size归一化到参数size的大小一样datagram.resize(udpSocket->pendingDatagramSize());//将读取到的不大于datagram.size()大小数据输入到datagram.data()中,datagram.data()返回的是一个字节数组中存储//数据位置的指针udpSocket->readDatagram(datagram.data(), datagram.size());QDataStream in(&datagram, QIODevice::ReadOnly);//因为其属性为只读,所以是输入int messageType;    //此处的int为qint32,在Qt中,qint8为char,qint16为uintin >> messageType;    //读取1个32位长度的整型数据到messageTyep中
        QString userName,localHostName,ipAddress,message;QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");//将当前的时间转化到括号中的形式switch(messageType){case Message://in>>后面如果为Qstring,则表示读取一个直到出现'\0'的字符串in >> userName >> localHostName >> ipAddress >> message;ui->messageBrowser->setTextColor(Qt::blue);//设置文本颜色ui->messageBrowser->setCurrentFont(QFont("Times New Roman",12));//设置字体大小//     ui->messageBrowser->append("[ " +userName+" ] "+ time);//输出的格式为用户名加时间显示//输出的格式为主机名加时间显示,但输出完后为什么会自动换行呢?ui->messageBrowser->append("[ " +localHostName+" ] "+ time);ui->messageBrowser->append(message);//消息输出break;case NewParticipant:in >>userName >>localHostName >>ipAddress;newParticipant(userName,localHostName,ipAddress);break;case ParticipantLeft:in >>userName >>localHostName;participantLeft(userName,localHostName,time);break;case FileName:break;case Refuse:break;}}
}// 处理新用户加入
void Widget::newParticipant(QString userName, QString localHostName, QString ipAddress)
{//此处的findItems表示找到与内容localHostName匹配的item,其匹配是基于变体的匹配模式bool isEmpty = ui->userTableWidget->findItems(localHostName, Qt::MatchExactly).isEmpty();if (isEmpty) {    //没有找到相应的主机名//新建3个小的item,分别为user,host,ipQTableWidgetItem *user = new QTableWidgetItem(userName);QTableWidgetItem *host = new QTableWidgetItem(localHostName);QTableWidgetItem *ip = new QTableWidgetItem(ipAddress);ui->userTableWidget->insertRow(0);//先设置的是第0行,即新来的用户放在最上面ui->userTableWidget->setItem(0,0,user);//第0行的第1列...ui->userTableWidget->setItem(0,1,host);ui->userTableWidget->setItem(0,2,ip);ui->messageBrowser->setTextColor(Qt::gray);ui->messageBrowser->setCurrentFont(QFont("Times New Roman",10));//arg为返回后面文本的一个副本,%1表示输出的内容按照第1个.arg后面的输出?ui->messageBrowser->append(tr("%1 在线!").arg(userName));ui->userNumLabel->setText(tr("在线人数:%1").arg(ui->userTableWidget->rowCount()));//在线人数为条目的行数
sendMessage(NewParticipant);//该句的功能是让新来的用户也能收到其它在线用户的信息,可拥于更新自己的好友列表
    }
}// 处理用户离开
void Widget::participantLeft(QString userName, QString localHostName, QString time)
{//找到第一个对应的主机名int rowNum = ui->userTableWidget->findItems(localHostName, Qt::MatchExactly).first()->row();ui->userTableWidget->removeRow(rowNum);    //此句执行完后,rowCount()内容会自动减1ui->messageBrowser->setTextColor(Qt::gray);//设置文本颜色为灰色ui->messageBrowser->setCurrentFont(QFont("Times New Roman", 10));ui->messageBrowser->append(tr("%1 于 %2 离开!").arg(userName).arg(time));ui->userNumLabel->setText(tr("在线人数:%1").arg(ui->userTableWidget->rowCount()));
}// 获取ip地址,获取本机ip地址(其协议为ipv4的ip地址)
QString Widget::getIP()
{//QList是Qt中一个容器模板类,是一个数组指针?QList<QHostAddress> list = QNetworkInterface::allAddresses();//此处的所有地址是指ipv4和ipv6的地址//foreach (variable, container),此处为按照容器list中条目的顺序进行迭代foreach (QHostAddress address, list) {    if(address.protocol() == QAbstractSocket::IPv4Protocol)return address.toString();}return 0;
}// 获取用户名
QString Widget::getUserName()
{QStringList envVariables;//将后面5个string存到envVariables环境变量中envVariables << "USERNAME.*" << "USER.*" << "USERDOMAIN.*"<< "HOSTNAME.*" << "DOMAINNAME.*";//系统中关于环境变量的信息存在environment中QStringList environment = QProcess::systemEnvironment();foreach (QString string, envVariables) {//indexOf为返回第一个匹配list的索引,QRegExp类是用规则表达式进行模式匹配的类int index = environment.indexOf(QRegExp(string));if (index != -1) {//stringList中存的是environment.at(index)中出现'='号前的字符串QStringList stringList = environment.at(index).split('=');if (stringList.size() == 2) {return stringList.at(1);//at(0)为文字"USERNAME.",at(1)为用户名break;}}}return "unknown";
}// 获得要发送的消息
QString Widget::getMessage()
{QString msg = ui->messageTextEdit->toHtml();//转化成html语言进行发送
ui->messageTextEdit->clear();//发送完后清空输入框ui->messageTextEdit->setFocus();//重新设置光标输入焦点,即焦点保持不变return msg;
}// 发送消息
void Widget::on_sendButton_clicked()
{sendMessage(Message);
}
复制代码

main:

复制代码
#include <QtGui/QApplication>
#include "widget.h"
#include <QTextCodec>    //处理不同语言编码的类int main(int argc, char *argv[])
{QApplication a(argc, argv);QTextCodec::setCodecForTr(QTextCodec::codecForLocale());//对不同的文字选择不同的编码
    Widget w;w.show();return a.exec();
}
复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/433127.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

下列哪一项不是计算机网络的典型应用,09级计算机信息网络试卷A

…………密…………封…………线…………内…………请…………不…………要…………答…………题…………河北工程大学11~12学年第二学期期末考试试卷A一、填空题(每空1分&#xff0c;共10分)1、在因特网中&#xff0c;远程登录Telnet系统采用的应用工作模式为____客户机&#…

hdu 4452

今天模拟赛的一个模拟题&#xff1b; 每次看到这种题就感觉很繁琐&#xff1b; 这次静下心来写写&#xff0c;感觉还不错&#xff01;就是很多错误&#xff0c;浪费了一点时间&#xff1b; 代码&#xff1a; 1 #include<cstdio>2 #include<cstring>3 using namespa…

wincc怎么做数据库_wincc7.4 数据库的建立与查询

要是时间足够&#xff0c;建议学习以下内容&#xff1a;1&#xff0c;数据库基础&#xff0c;学习建库建表&#xff0c;以及基本的查询操作。如果数据需要处理的话也需要学习一下聚合函数。2&#xff0c;VBS脚本。做数据库的话建议用VBS&#xff0c;学起来和用起来简单一些。主…

uboot 的i2c_read

int i2c_read(u8 dev, //i2c设备地址 uint addr, //读i2c设备的数据起始地址&#xff0c;比如e2 int alen, //e2的地址位数&#xff0c;0为8位&#xff0c;1为16位&#xff0c;2为32位 u8 *data, //…

固定资产管理有关的计算机知识,计算机技术在固定资产管理中的具体应用.pdf...

计算机技术在固定资产管理中的具体应用2Ol6年 I2月 太 原 学 院 学 报 Vo1&#xff0e;34 No&#xff0e;4第 34卷 第 4期Dec&#xff0e; 2016计算机技术在固定资产管理中的具体应用赵丽敏(硅湖职业技术学院&#xff0c;江苏 昆山215300)摘 要 &#xff1a;固定资产 管理是 一…

弹出键盘,UIView 上移

声明欢迎转载&#xff0c;但是请尊重作者劳动成果&#xff0c;转载请保留此框内声明&#xff0c;谢谢。 文章出处&#xff1a;http://blog.csdn.net/iukey //--------------------------------------------------------------------------------------------------------------…

hdfs如何查找指定目录是否文件_在shell中如何判断HDFS中的文件目录是否存在

原标题&#xff1a;在shell中如何判断HDFS中的文件目录是否存在在Linux文件系统中&#xff0c;我们可以使用下面的Shell脚本判断某个文件是否存在&#xff1a;# 这里的-f参数判断$file是否存在if[ ! -f "$file"]; thenecho"文件不存在!"fi但是我们想判断HD…

用计算机MR,计算机上的【MC、MR、M

计算机上的【MC、MR、M2018-09-08计算机上的【MC、MR、M-、M】键各是什么意思?例举各自的作用&#xff1f;推荐内容计算机上的【MC、MR、M-、M】键各是什么意思?例举各自的作用&#xff1f;帮帮忙...帮帮忙问题库来自 匿名用户 的提问最佳答案由提问者推荐匿名用户M&#xff…

POJ 1328 Radar Installation 贪心

传送门http://poj.org/problem?id1328 题目大意&#xff1a;平面上有一些岛屿&#xff0c;现要求用一些圆心在x轴上的&#xff08;雷达&#xff09;来覆盖这些岛屿&#xff0c;问最少需要的雷达数目。 看了大神的思路&#xff1a; 把点按横坐标排序&#xff0c;然后把每个点的…

layui 怎么设置点击图片放大_layui等比例放大/缩小图片

HTMLJS$("#photo").click(function(){showImg($(this));});function showImg(imgData){var img new Image();img.src imgData.attr("src");var height img.height; // 原图片大小var width img.width; //原图片大小var winHeight $(window).height() …

移植ssh到arm

1. 下载需要的源码:mkdir -p ~/arm/fs ;mkdir -p ~/arm/source下载zlib: wget -c http://www.zlib.net/zlib-1.2.3.tar.gz下载ssl: wget -c http://www.openssl.org/source/openssl-0.9.8d.tar.gz下载ssh: wget -c http://mirror.mcs.anl.gov/openssh/portable/openssh-4.6p1…

学霸系统计算机天才,小欢喜:开局获得学霸系统

小欢喜&#xff1a;开局获得学霸系统连载中26万字||更新时间:2020-08-02 03:16:18莫名其妙穿越到小欢喜的世界当中&#xff0c;江辰成为了春风中学高三学生&#xff0c;和方一凡、季杨杨、乔英子等人成为了朝夕相伴的同学&#xff0c;并且一开局就获得了无敌的超级学霸系统。 …

openpyxl 绘制饼形图_好享学丨快速上手Pythonmatplotlib 箱线图绘制,学术人必备

好享学是高下制图推出的关于数据可视化经验分享栏目&#xff0c;我们将定期与您分享各界优秀人士的制图经验&#xff0c;一同学习。01. 引言箱线图(Boxplot) 是一种用作显示一组数据分散情况资料的统计图表&#xff0c;本期推文就如何使用matplotlib和seaborn 绘制出高度定制化…

揭开Socket编程的面纱

对TCP/IP、UDP、Socket编程这些词你不会很陌生吧&#xff1f;随着网络技术的发展&#xff0c;这些词充斥着我们的耳朵。那么我想问&#xff1a; 1. 什么是TCP/IP、UDP&#xff1f; 2. Socket在哪里呢&#xff1f; 3. Socket是什么呢&#xff1f; 4. 你会使用它们吗&#xff1f;…

flash地址 html,STM32 Flash读写;Flash地址对应的存储内容及方式

概念&#xff1a;计算机中最小的信息单位是bit&#xff0c;也就是一个二进制位&#xff0c;8个bit组成一个Byte&#xff0c;也就是1个字节&#xff0c;1个存储单元存放1个字节&#xff0c;每个存储单元对应一个32位(bit)地址&#xff0c;所以重要的话说三遍&#xff1a;对于32b…

socket中的函数遇见EINTR的处理

这几天&#xff0c;写服务器代码过程当中&#xff0c;遇见EINRT信号的问题&#xff0c;我是借鉴 《unp 》&#xff0c;采用continue或者goto again循环解决的。但是感觉这个还是很有必要记录一下。网络上查找到的信息很多。下面是我查找到的和EINTR有关的介绍&#xff1a;1 ht…

vue 如何获取图片的原图尺寸_公众号封面图片尺寸是多少?如何在公众号里制作封面图?...

公众号文章封面图是自己动手制作还是直接使用网络图片呢&#xff1f;在刚开始接触公众号运营时&#xff0c;我的大部分插图和封面图都是直接选择网络图片&#xff0c;后来才发现&#xff0c;自己制作的封面图更能传达文章的内容&#xff0c;阅读效果更好。其实很多做公众号的人…

主机名排序

主机名排序 主机名由多级域名组成&#xff0c;自右向左&#xff0c;依次是顶级域名、二级域名、三级域名…..以此类推 例&#xff0c;主机名&#xff1a;google.com.hkhk是顶级域名 com是二级域名 google是三级域名 现在我们需要实现一个主机名的排序功能 排序规则 1&#xff…

《计算机应用》实践考核,《管理系统中计算机应用》实践性环节考核方案

一、参考教材&#xff1a;《管理系统中计算机应用》&#xff0c;汪星明、周山芙主编&#xff0c;武汉大学出版社2004年版。二、考核时间&#xff1a;50分钟。三、考核内容及分数分布&#xff1a;1、Windows资源管理器的综合应用(10分)1)启动资源管理器。2)熟练掌握文件(夹)的建…

信号中断 与 慢系统调用

1. 术语 1.1. 慢系统调用&#xff08;Slow system call&#xff09; 该术语适用于那些可能永远阻塞的系统调用。永远阻塞的系统调用是指调用永远无法返回&#xff0c;多数网络支持函数都属于这一类。如&#xff1a;若没有客户连接到服务器上&#xff0c;那么服务器的accept调用…