qt的udp的初步使用(转)_weixin_30576859的博客-程序员宅基地

技术标签: 网络  ui  

该程序实现的功能是:局域网内,每个用户登录到聊天软件,则软件界面的右端可以显示在线用户列表,分别显示的是用户名,主机名,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_OBJECT

public:
    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为uint
        in >> 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,ip
        QTableWidgetItem *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()内容会自动减1
    ui->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();
}

转载于:https://www.cnblogs.com/Pond-ZZC/p/7069174.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30576859/article/details/98431392

智能推荐

计算机专业的毕业设计论文如何写作 & python毕业设计题目_计算机毕业论文python_专注大学计算机毕设和教育的博客-程序员宅基地

计算机专业的毕业设计论文如何写作 & python毕业设计题目推荐 论文是毕设中很重要的部分!但很多人不知道怎么去写,其实主要是不了解毕设论文的结构(共多少章,每章多少节,每章每节的内容写什么),下面我们来了解一下。1 扉页扉页也指在论文封面之后、正文之前的一页。是论文翻开后的第一页(即论文的第二页)。扉页上一般印有论文题目、专业班级、学生姓名、指导教师、日期等。2 摘要摘要是对课题的概括,又称概要、内容提要。分为中文摘要和英文摘要,以及关键词。一般在300字左右!..._计算机毕业论文python

r语言导入ggplot2_R语言入门--画图(一)--ggplot2_一曲歌长安的博客-程序员宅基地

先写一些需要用到的知识点,比如包、函数dplyr 很好用的包 经常与ggplot2连用mutate:用于对数据框的列进行重新处理,或者用处理的结果添加新列数据清洗:1、na.omit() #去除数组当中的空值newdataggplot:1、在ggplot的里面添加直线:geom_hline()添加水平线geom_viline()添加垂直线 #geom_hl..._r语言geom_hline函数

mysql中如何使用INSERT一次性插入多条记录_xiewenbo的博客-程序员宅基地

看到这个标题也许大家会问,这有什么好说的,调用多次INSERT语句不就可以插入多条记录了吗!但使用这种方法要增加服务器的负荷,因为,执行每一次 SQL服务器都要同样对SQL进行分析、优化等操作。 幸好MySQL提供了另一种解决方案,就是使用一条INSERT语句来插入多条记录, 这并不是标准的 SQL语法,因此只能在MySQL中使用。   INSERT INT

对es6中块级作用域的理解_块级作用域怎么理解-程序员宅基地

作用域决定了一个变量的生命周期,可以保证这个作用域没有执行完,这个变量就不会被销毁,常用在有回调函数的块中,使用let关键字或者闭包都可以实现块级作用域,延伸作用域的效果,往往一个循环可以产生多个块,也就是为每个变量开辟一个内存空间,没有调用完不会被销毁,因此在常见面试题,比如点击得到当前li的索引号,可以使用let或者闭包;..._块级作用域怎么理解

一文读懂遗传算法(附python)_遗传算法 basic csdn-程序员宅基地

遗传算法 (Genetic Algorithms,简称GA)是人工智能的重要新分支,是基于达尔文进化论,在计算机上模拟生命进化机制而发展起来的一门新学科。它根据适者生存、优胜劣汰等自然进化机制来进行搜索计算和问题求解。本文主要介绍遗传算法的原理,包括其定义及其实现步骤,以及遗传算法的研究现状和未来发展趋势。..._遗传算法 basic csdn

python类带参数_Python的参数传递:对象引用(pass by object reference)-程序员宅基地

1. Python中的变量名是没有类型信息的,这样增加了python的灵活性。a=[1,2,3]a="Hello"对象[1,2,3]是List类型的,对象"Hello"是String类型的,而变量a是没有类型的,变量a​仅仅是一个对象的引用(一个指针),既可以指向List类型对象,也可以指向String类型的对象。​2.Python的参数传递既不是传值(pass-by-value),也不是传引用(..._python 参数传递 object

随便推点

PowerDesign16.5( 32位) 数据库建模_powerdesigner32位下载_小小程序员1986的博客-程序员宅基地

新建model选择物理模型650) this.width=650;" title="捕获.PNG" alt="wKioL1WCPBjSl9xkAAVMRMC4mP8061.jpg" src="http://s3.51cto.com/wyfs02/M01/6E/A8/wKioL1WCPBjSl9xkAAVMRMC4mP8061.jpg" />配置mysql数据源修改DBMS类型,选择mysql5.0_powerdesigner32位下载

ubuntu18安装ros-melodic_橘长_的博客-程序员宅基地

记录Ubuntu18安装ros系统一、换阿里源换阿里源链接二、配置软件和更新设置为了能顺利更新这里我电脑连接手机开的网络热点三、开始安装参考wiki进行安装ros安装官网注意这里安装过程全程用手机热点Setup your sources.list sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_...

使用FileReader和FileWriter读取写入文件内容_哈里森沃尔夫的博客-程序员宅基地

java 中的字节流不能直接操作Unicode字符,要想直接操作字符输入/输出要使用几个字符输入/输出类。字符流层次结构的顶层是Reader和Writer抽象类。1、Reader Reader是定义java的流式字符输入模式的抽象类。错误异常为IOException。 主要方法有: abstract void close() 关闭输入流,关闭后读取将会产生...

如何使用Charles进行APP抓包_charles app抓包_米兰老鼠的博客-程序员宅基地

如何使用Charles进行APP抓包注意事项:由于现在7.0以上的版本不再信任用户安装证书,所以建议使用安卓系统版本低于7.0的设备进行配置抓包。Charles链接:https://pan.baidu.com/s/1jtGlhWNtwGDzILrxZ_6sIA提取码:b94b安装Charles同意协议下一步这里可以修改软件的安装路径,我这里保持默认路径,下一步点击 install 等待安装完成点击 Finish 完成安装破解Charles将压缩包内的 charles.jar _charles app抓包

为何C语言如此强大?到底可以做什么?_C语言学习部落的博客-程序员宅基地

C语言几乎是所有语言的实现基础,所以不存在做不了的事情,只是相对来说谁做更合适,编写游戏和软件都不是问题。为何C语言如此强大?1、C语言是许多高级计算机语言的基础,学好C语言能更好的学习其他高级语言,为以后的学习打基础;往深学C语言的话那就是学到C在Linux里的应用,Linux十分强大。2、C语言是一种计算机程序设计语言。具有高级语言的特点,又具有汇编语言的特点。C语言可作为...

大型网站系统架构实践(四)http层负载均衡之haproxy实践篇(一)_九月刑天的博客-程序员宅基地

方案上篇文章讲到了负载均衡的相关理论知识,这篇文章我打算讲讲实践方法以及实践中遇到的问题方案:haproxy http层负载均衡安装一个haproxy服务,两个web服务haproxy:192.168.1.227:80web1 http://192.168.1.226:8081/loginweb2 http://192.168.1.246:8888/

推荐文章

热门文章

相关标签