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

qt的udp的初步使用(转)的更多相关文章

  1. 使用 Qt 获取 UDP 数据并显示成图片(2)

    本文首发于 BriFuture 的 个人博客 在我的前一篇文章 使用 Qt 获取 UDP 数据并显示成图片 中,我讲了如何用 Python 模拟发送数据,如何在 Qt 中高效的接收 UDP 数据包并将 ...

  2. 使用 Qt 获取 UDP 数据并显示成图片

    一个项目,要接收 UDP 数据包,解析并获取其中的数据,主要根据解析出来的行号和序号将数据拼接起来,然后将拼接起来的数据(最重要的数据是 R.G.B 三个通道的像素值)显示在窗口中.考虑到每秒钟要接收 ...

  3. QT下UDP套接字通信——QUdpSocket 简单使用

    QT下UDP套接字通信--QUdpSocket QUdpSocket类提供一个UDP套接字. UDP(用户数据报协议)是一种轻量级.不可靠.面向数据报.无连接的协议.它可以在可靠性不重要的情况下使用. ...

  4. JSON - 使用cJSON 解析Qt通过UDP发送的JSON数据

    1,cJSON支持在C程序中创建和解析JSON数据,其提供多种方法供C程序使用,最直接的是将cJSON.c和cJSON.h加入到C工程中,源代码:https://github.com/DaveGamb ...

  5. QT之UDP通信

    前言:前一篇讲了TCP通信,这篇来看看UDP通信. 这里说明一下,UDP通信中分为三种通信分别为单播.组播和广播,下面将一一为大家介绍. 同样的我们都需要在工程文件中添加network QT += c ...

  6. QT的UDP组播技术

    一 UDP介绍 UDP是一种简单轻量级的传输层协议,提供无连接的,不可靠的报文传输.适合下面4种情况: 网络数据大多为短消息. 拥有大量客户端. 对数据安全性无特殊要求 网络负担非常重,但对响应速度要 ...

  7. Qt - 基于UDP的网络编程

    UDP(用户数据报协议 User Data Protocol) 轻量级.不可靠.面向数据报.无连接  的传输层协议. 适用情况: 网络数据大多为短消息: 拥有大量客户端: 对数据安全无特殊要求: 网络 ...

  8. Qt通过UDP传图片,实现自定义分包和组包

    一.包头结构体 //包头 struct PackageHeader { //包头大小(sizeof(PackageHeader)) unsigned int uTransPackageHdrSize; ...

  9. Qt中OpenGL的初步使用

    结果预览: 一.代码5个文件 //glwidget.h #ifndef GLWIDGET_H #define GLWIDGET_H #include <QGLWidget> class G ...

随机推荐

  1. Easyui 基于kindeditor的扩展

    源码 /** * Author : ____′↘夏悸 * Easyui KindEditor的简单扩展. * 有了这个之后,你就可以像使用Easyui组件的方式使用KindEditor了 * 前提是你 ...

  2. 去除Android打开软件出现的红边框

    /********************************************************************** * 去除Android打开软件出现的红边框 * 说明: ...

  3. 嵌入式开发之赛灵思 xilinx Zynq芯片简介---Zynq-7000 EPP (XC7Z010 and XC7Z020)

    (1)企业简介 作为DSP 和视频应用领域的头号 FPGA 供应商,赛灵思致力于通过其目标设计平台提供业内领先的 DSP 开发工具.方法.IP 和技术支持.赛灵思面向 DSP 的目标设计平台将这些元素 ...

  4. Unity3D学习笔记——NGUI之UIButton

    前言:用于接受点击,悬停,触摸等事件.UIButton还有重要的用途,就是改变控件不同状态下的颜色. 一:使用方式: 1.在UI Root中右键创建一个Sprite 2.为其添加一个碰撞组件,就添加B ...

  5. day13迭代器与生成器

    三个作业: # 1.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码 login_dic = {'alex':False} def ...

  6. POJ1365 Prime Land【质因数分解】【素数】【水题】

    题目链接: http://poj.org/problem?id=1365 题目大意: 告诉你一个数的质因数x的全部底数pi和幂ei.输出x-1的质因数的全部底数和幂 解题思路: 这道题不难.可是题意特 ...

  7. ios 蓝牙相关

      ios蓝牙开发项目实战 -(附小米手环实例)   前言 最近一直在开发关于蓝牙的功能,本来是不想写这一篇文章,因为网上关于ios蓝牙开发的文章实在太多了,成吨成吨的文章出现,但是很遗憾都只是一些皮 ...

  8. dos下查找进程,如果找到echo find并结束该进程

    @echo offset var=chromedriver.exetasklist | findstr "%var%" && echo findtaskkill / ...

  9. 关于微信小程序下拉出现三个小点

    包子这天看美团外卖的小程序,再瞅瞅自己的背景色,发现,美团下拉的时候有三个小点,但是我自己的校车徐下拉的时候没有三个小点,很是郁闷,于是各种的找各种的找,发现,这三个小点是微信小程序自带的,你只需要设 ...

  10. 《UNIX网络编程》 -- 第五章

    str_cli 和 str_echo 函数 需要先弄清楚 3.9 readn.writen 和 readline 函数 str_cli void str_cli(FILE *fp, int sockf ...