QT下UDP的实现方法及源代码讲解

  I widget.h的代码:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtNetwork/QUdpSocket>
#include<QtNetwork/QHostAddress>
#include <QMessageBox>
#include <QHostInfo>
#include <QNetworkInterface>

 

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
   
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    QHostAddress *localHostAddr;       //本地的ip地址;
     QHostAddress *remoteHostAddr;   //对方的IP地址;
    QString localIpStr;
    QString remoteIpStr;

   QString getIp();
    void autoScroll();

private slots:
    void send();
    void receive();    
    void on_clearButton_clicked();

    void on_configButton_clicked();

   void on_exitButton_clicked();

private:
    Ui::Widget *ui;
    QUdpSocket *udpSocket1;  
    bool configFlag;
};

#endif // WIDGET_H

         II widget.cpp里的核心代码及说明:

首先初始化如下:ui->setupUi(this);
    configFlag = false;        //
初始化连接参数为未连接
    ui->getTextEdit->ensureCursorVisible();
    ui->sendTextEdit->setFocus();      //
程序启动时,焦点停在发送对话框
    ui->ipEdit->setText("192.168.2.211");   //
设置默认的远程端Ip
    ui->portEdit->setText("6665");           //
设置默认端口号

一、快捷键设置:

 ui->udpSendButton->setShortcut(tr("Alt+F"));  

这点重点说下,不知道为什么想用Ctrl+Enter,但用不了,用其他快捷键就可以,这里用Alt+F

二、本地Ip的查找

      这块是最曲折的。最初是用

//    QHostInfo info = QHostInfo::fromName(QHostInfo::localHostName());
//    hostaddr1 = info.addresses().takeFirst();

这样查询出的hostaddr1其实是本地回环Ip地址,hostaddrStr = hostaddr1.toString();,可以用qDebug()<<hostaddrStr打印一下,发现结果是127.0.0.1.并不是真正的本地Ip地址。localHostAddr = new QHostAddress(localIpStr);如果这里用new QhostAddrresshostaddr1),用这个地址的话可以自己给自己发,但和局域网其他机器通讯时是根本不可能的。

这里提供一下查询本地Ip的函数:

QString Widget::getIp()
{
    QList<QHostAddress> list = QNetworkInterface::allAddresses();
    foreach (QHostAddress address, list)
    {
        if(address.protocol() == QAbstractSocket::IPv4Protocol)   //
我们使用IPv4地址
        {
            if(address.toString().contains("127.0."))
                continue;
            qDebug()<<"
本机Ip"<<address.toString();
            return address.toString();
        }
    }
    return 0;

}

只有这个函数,打印出来的Ip地址才是你机器上真正的Ip

所以我们

 localIpStr = getIp();

 localHostAddr = new QHostAddress(localIpStr);

首先获得本地Ip地址,用这个Ip地址也初始化QHostAddress变量。这是我们的本地Ip.

   udpSocket1 = new QUdpSocket(this);
    bool bindFlag = udpSocket1->bind(*localHostAddr, 6665, QUdpSocket::ShareAddress);

上面是初始化一个udpsocket,将他和本地Ip开放的端口号绑定在一起。

接下来判断绑定失败与否:

 if(!bindFlag)
    {
        QMessageBox box;
        box.setText(tr("
初始化绑定socket错误!"));
        box.exec();
    }
    else
    {
        connect(udpSocket1, SIGNAL(readyRead()), this, SLOT(receive()));
        connect(ui->udpSendButton, SIGNAL(clicked()), this, SLOT(send()));
    }

绑定成功了,就连接槽函数,第一个receive()是udpSocket1在接收到数据时触发的,第二个send()是当按下发送按键时,往对端机器发信息的。接收数据和发送数据时同一个Socket!大家注意了,网上其他人用一个socket接收,一个socket发送。可能也可以把,这里用一个。

 this->setWindowTitle(tr("基于QtUDP聊天界面-------顶礼准提佛母")); 设置标题。

   

三、发送数据的槽函数-----解决发送中文乱码的关键

 

void Widget::send()
{
    autoScroll();
    QString sendStr = ui->sendTextEdit->toPlainText();
    QByteArray sendByteArray = sendStr.toAscii();
    QMessageBox box;
    if(sendStr.length()==0)
    {
        box.setText(tr("
请输入发送内容"));
        box.exec();
    }
    else if(configFlag)
    {

       udpSocket1->writeDatagram(sendByteArray, sendByteArray.length(), *remoteHostAddr, 6665);

       //本地发送信息再信息交互窗口的显示
        QDateTime time;
        QString timeStr = time.currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd");
        ui->getTextEdit->setTextColor(QColor("red"));
        ui->getTextEdit->insertPlainText("
本机" + localIpStr + ": " + timeStr + " ");
        ui->getTextEdit->setTextColor(QColor("black"));
        ui->getTextEdit->insertPlainText( sendStr +" ");
        ui->sendTextEdit->clear();          //
点击发送后,发送编辑框内清零
        ui->sendTextEdit->setFocus();      //
焦点停留在发送编辑框
    }
    else if(!configFlag)
    {
        box.setText("
请您先点击确认按钮!");
        box.exec();
    }
}

          这里有几点说说:

 1,发送数据的实现

         QString sendStr = ui->sendTextEdit->toPlainText();
         
QByteArray sendByteArray = sendStr.toAscii();
         udpSocket1->writeDatagram(sendByteArray, sendByteArray.length(), *remoteHostAddr, 6665);

         能否正确显示中文就在这一句,必须把QString转成QByteArray再发送,接收端有好几种实现方法,但发送端不这么搞的话,就解析不了中文。这块让我搞了一整天!

2,本地时间显示

      QDateTime time;
        QString timeStr = time.currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd");
要包含一个头文件,#include <qdatetime.h>,奇怪的是这个头文件包含在widget.h里就不行,非要搞到widget.cpp里才中。

3,显示文本的时候 ui->getTextEdit->insertPlainText( sendStr +" ");
        ui->sendTextEdit->clear();          //
点击发送后,发送编辑框内清零
        ui->sendTextEdit->setFocus();      //
焦点停留在发送编辑框

一定要搞成insertPlainText,如果搞成setText,就会把显示窗口搞成只有一句话,这个函数是不销毁以前的内容,插入在以前的文本内容之后的函数。

4,自动滚屏的实现,函数实现为:

void Widget::autoScroll()
{
    QTextCursor cursor = ui->getTextEdit->textCursor();
    cursor.movePosition(QTextCursor::End);
    ui->getTextEdit->setTextCursor(cursor);
}

    这里插一句,getEdit,的属性里设成enablesread Only,designer里,点控件,设置属性里这么设置。如果不设自动滚屏是什么效果呢??

        只有拖动右边滚动鼠标才能显示,所以一定要设自动滚屏。而且在两个地方,一个是receive里调用这个函数,有东西显示的时候,滚屏到最后显示!点发送的时候,也要调用这个滚屏函数,如果您聊天的时候,把光标停留在信息交互栏里某个位置A,下一条信息就会显示在A光标后,而不是我们希望的每次都挨着上一条信息后显示。所以点发送按钮后,要先调用,目的在此!

四、接收槽函数的实现

 

 void Widget::receive()
{
    while(udpSocket1->hasPendingDatagrams())
    {
       
QTextCodec *tc=QTextCodec::codecForName("UTF-8"); //UTF-8
        QDateTime time;
        QString timeStr = time.currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd");

       QByteArray data;
        data.resize(udpSocket1->pendingDatagramSize());
        udpSocket1->readDatagram(data.data(), data.size());
//       
QString dataStr =  QString::fromUtf8(data.data());   //这样写也是正确的
        QString dataStr = tc->toUnicode(data);
        ui->getTextEdit->setTextColor(QColor("red"));
        ui->getTextEdit->insertPlainText("
远程"  + remoteIpStr+": "+ timeStr +" " );
        ui->getTextEdit->setTextColor(QColor("black"));
        ui->getTextEdit->insertPlainText(dataStr  + " " );
        autoScroll();

   }

}

     为了显示中文,关键在三句

    QTextCodec *tc=QTextCodec::codecForName("UTF-8");

    QString dataStr = tc->toUnicode(data);
如果不写这两句,用这句也可以实现:QString dataStr =  QString::fromUtf8(data.data());  

五,点确定按钮的槽函数:

void Widget::on_configButton_clicked()
{
     remoteIpStr = ui->ipEdit->text();
    QString port = ui->portEdit->text();
    qDebug()<<"
远程端Ip"<<remoteIpStr<<"端口号:"<<port;
    remoteHostAddr = new QHostAddress(remoteIpStr);

   QMessageBox box;
    if(remoteIpStr.length()==0 || port.length()==0 || port.toInt()<1024)
    {
        configFlag = false;
        box.setText("
请正确设置远程端Ip地址和端口号!");
        box.exec();
    }
    else
    {
        configFlag = true;
        box.setText("
您设置的远程端Ip" + remoteIpStr+"端口号:"+port);
        box.exec();
    }

}

用来初始化远程Ip地址!

此代码不是完全的代码,但是每个函数都是可以运行的!!

你可能感兴趣的:(linux,qt,UDP,局域网,发送消息)