上一个
下一个

QT局域网通讯

0.前言

这是本学期的国产操作系统实验课的结课作业,虽然说是国产操作系统,但上的内容实际上是QT。本篇会先大致介绍一下我的界面是怎么搭的,然后介绍如何实现简单的局域网通讯。

1.界面设计

文件结构

chatmain:主界面,包括显示聊天记录,用户名和ip地址,修改昵称和刷新局域网与用户。

messageitem:在主界面中的聊天记录,双击打开一个聊天框。

chatwindow:聊天框。

mymsg和yourmsg:聊天框中的气泡。

chatmai.ui
messageitem.ui
chatwindow.ui
yourmsg.ui

2.获得局域网用户

基于UDP协议,我们这里分为两步走。第一步,向局域网广播自己的存在。第二步,获得局域网中的用户(注意要排除掉自己)。为了不重复生成很多消息记录,导致消息发送到错误的聊天框上,我们使用一个map来保存生成过的messageitem,刷新时发现同样的ip直接使用之前的messageitem就可以了,不用再生成新的了。

				
					  //chatmain.h
private:
    QHostAddress myIp;
    QUdpSocket * udp_socket;
    QTimer *tim;
    QMap<QString,MessageItem*> iplist;
				
			

以上变量需要添加到chatmain.h中,以下代码在chatmain的构造函数中。目的是获得本机ip。

				
					 //IP
    QString localHostName=QHostInfo::localHostName();   //获取主机名
    QHostInfo hostInfo=QHostInfo::fromName(localHostName);//本机的IP地址
    QList<QHostAddress> listAdress=hostInfo.addresses();  //IP地址列表
   if(!listAdress.isEmpty())
   {

       if(listAdress.size()>=1)
          myIp=listAdress[1];  //如果主机的IP列表不为空,则使用其第一个IP地址,我电脑要第二个
       else
           myIp=listAdress.first();  //如果主机的IP列表不为空,则使用其第一个IP地址
   }
   ui->ip->setText(myIp.toString());
				
			

紧接着上面代码,我们创建socket并用799端口向局域网宣告自己的存在,这里我们创建了一个计时器每个1秒广播一次。

				
					   udp_socket = new QUdpSocket(this);
   udp_socket->bind(799);
   udp_socket->writeDatagram(myIp.toString().toStdString().c_str(),QHostAddress::Broadcast,799); // 发送广播消息
   ui->avator->setStyleSheet(QString(defaultAvator)+"border-image:url(://"+QString("avator%1.jpg").arg(myIp.toIPv4Address()%20)+");");//设置头像
   tim = new QTimer();
   tim->setInterval(1000);
   connect(tim,SIGNAL(timeout()),this,SLOT(onTimeOut()));
   tim->start();
   loadMessageItem();//程序启动获得当前局域网内的用户
				
			

onTimeOut函数十分简单,就是广播自己的存在。

				
					void ChatMain::onTimeOut()
{
   udp_socket->writeDatagram(myIp.toString().toStdString().c_str(),QHostAddress::Broadcast,799); // 发送广播消息
}


				
			

接下来就来看看怎么获得局域网用户,这里messageItem需要传入自己的ip,对方的ip还有消息时间等参数,这和你自己怎么实现有关。此外QListWidgetItem是向chatmain中的ListWidget添加项的,这也和你自己的实现有关。

				
					void ChatMain::loadMessageItem(){
    while(udp_socket->hasPendingDatagrams())
    {
        //qDebug()<<"read";
        QByteArray data;
        data.resize(udp_socket->pendingDatagramSize());
        QHostAddress host;
        quint16 port;
        udp_socket->readDatagram(data.data(), data.size(), &host, &port);

        QString text = QString("[%1:%2]:%3").arg(host.toString()).arg(port).arg(QString(data));
        if(data==myIp.toString()) continue;
        //得到时间
        if(iplist.contains(data)){
            //qDebug()<<"有了";
            QListWidgetItem* pItem2 = new QListWidgetItem();
            pItem2->setSizeHint(QSize(200,80));
            ui->messageBox->insertItem(0, pItem2);
            ui->messageBox->setItemWidget(pItem2, iplist[data]);
        }
        else{
            //qDebug()<<"新建";
            QDateTime current_date_time =QDateTime::currentDateTime();
            QString current_date =current_date_time.toString("hh:mm");
            MessageItem *b= new MessageItem(this,myIp.toIPv4Address()%20,QString("avator%1.jpg").arg(host.toIPv4Address()%20),data,data,current_date,false);
            iplist.insert(data,b);
            QListWidgetItem* pItem2 = new QListWidgetItem();
            pItem2->setSizeHint(QSize(200,80));
            ui->messageBox->insertItem(0, pItem2);
            ui->messageBox->setItemWidget(pItem2, iplist[data]);
        }
    }
}
				
			

绑定好刷新键,这里清除会出现小问题,暂且不管了。

				
					void ChatMain::on_f5PushButton_clicked()
{
    //ui->messageBox->clear();
    loadMessageItem();
}
				
			

2.聊天通讯

和之前差不多,我们将其作为练习留给读者——开个玩笑。

以下是chatwindow的构造函数中。

				
					    //局域网通讯
    udp_socket = new QUdpSocket(this) ;
    //绑定
    udp_socket->bind(8888) ;

    //当对方成功发送数据过来
    //自动触发readyRead()
    connect(udp_socket, SIGNAL(readyRead()), this, SLOT(getMessage())) ;
				
			

在按下发送按钮的同时要向对方ip发送消息。

				
					   udp_socket->writeDatagram(ui->MessagetextEdit->toPlainText().toUtf8(), QHostAddress(hisIp), 8888);
				
			

最后得到消息了就添加消息记录——注意构造函数中的connect。

				
					void ChatWindow::getMessage()
{
    qDebug()<<"getMessage";
    char buf[1024] ={0};
    QHostAddress client_address;
    quint16 client_port;
    //读取对方发送的内容
    qint64 len = udp_socket->readDatagram(buf, sizeof(buf), &client_address, &client_port) ;
    qDebug()<<client_address.toString();
    //if(client_address.toString()!=hisIp) return;
    if(len >= 0)
    {
        QString str = QString("[%1:%2] %3").arg(client_address.toString())
        .arg(client_port).arg(buf) ;

        //添加项目
        //得到时间
        QDateTime current_date_time =QDateTime::currentDateTime();
        QString current_date =current_date_time.toString("hh:mm");
        //发送信号
        emit newLastMessage(buf,current_date);

        //设置该项高度
        QFont font;
        font.setFamily("Microsoft YaHei");
        font.setPointSize(8);
        QFontMetrics fm(font);
        QRect rec = fm.boundingRect(buf);
        qDebug()<<buf;
        //添加项目
        yourMsg *ysg = new yourMsg(this,this->myAvator,buf,rec.width());
        QListWidgetItem* pItem = new QListWidgetItem();
        pItem->setSizeHint(QSize(200,80));
        ui->messageShower->addItem(pItem);
        ui->messageShower->setItemWidget(pItem, ysg);
        ui->messageShower->scrollToBottom();
        //字符串所占的像素宽度,高度
        pItem->setSizeHint(QSize(500,rec.width()/205*10+80));
        //发送信号
    }
}
				
			
订阅评论
提醒
2 评论
最旧
最新 最多投票
内联反馈
查看所有评论
有时间就要学习——丁真
1 年 前

救命稻草😭

1 年 前

过于有能(喜

《QT局域网通讯》

2
0
希望看到您的想法,请您发表评论x