現列出幾個概念:
①本地IP。對於運行在PC機上的程序,本地IP就是PC的IP,以windows爲例,直接在命令行中執行ipconfig命令,即可查到本機IP,如下圖:
②遠程IP。我們寫的程序要把數據發送到哪個IP的哪個端口上,這個IP就是指的遠端(遠程)IP
③端口號。本質上也屬於協議地址的一部分,可以認爲是某個IP下更細分的地址。可以這樣理解,IP代表了一棟樓的樓號,那麼端口號就代表了某棟樓中的某一門牌號,相當於端口號是一個更詳細的地址。
④bind函數。socket編程中常用的一個函數。在UDP通信中,需要接收數據的一端必須要用bind 函數。發送數據時,直接向遠端的【IP::端口】發即可,而接收數據時,需要監聽(或者叫綁定)【自己的IP:端口號】,而不是監聽別人的IP:端口號。UDP通信並不區分服務端和客戶端。
一個典型的應用場景:
在同一局域網中的兩臺PC,【電腦1】的IP爲192.168.2.61(我寫的QT程序就在這臺電腦上),【電腦2】的IP爲192.168.2.19(我在上面打開了一個網絡調試助手,sscom網上能下載到)。
【電腦2】上的sscom配置如下:
【電腦1】上自己寫的QT程序如下:
核心程序如下:
在pro文件中修改:
QT += core gui network
//在頭文件中添加私有成員
QUdpSocket *udpSocket;
QString localIpStr;//記錄ui中用戶輸入的IP
uint16_t localPort;
QString remoteIpStr;
uint16_t remotePort;
QHostAddress localIp;//程序中IP都是QHostAddress類型的
QHostAddress remoteIp;
//CPP如下
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <qDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
connect(udpSocket,SIGNAL(readyRead()), this, SLOT(when_udp_readyRead()));//收到別的電腦發來的消息
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_ptnSendRoute_clicked()//點擊該按鈕,可以遠端IP發送字符串數據
{
char frame[] = "123456789";
qint64 len = udpSocket->writeDatagram(frame, sizeof(frame), remoteIp, remotePort);
if(len > 0)
qDebug() << "send ok: " << len;
}
void MainWindow::on_ptnComm_clicked()
{
bool ok;
localIpStr = ui->lineEdit_localIp->text();
localPort = ui->lineEdit_localPort->text().toInt(&ok);
if(!ok) qDebug() << "你輸入的本地端口號有誤";
remoteIpStr = ui->lineEdit_remoteIp->text();
remotePort = ui->lineEdit_remotePort->text().toInt(&ok);
if(!ok) qDebug() << "你輸入的遠程端口號有誤";
if(!localIp.setAddress(localIpStr))
qDebug() << "你輸入的本地IP有誤";
if(!remoteIp.setAddress(remoteIpStr))
qDebug() << "你輸入的遠程IP有誤";
if(!udpSocket->bind(localIp, localPort))
qDebug() << "無法監聽(bind)本地UDP端口:" << localIp << localPort;//解決方案見下文
}
void MainWindow::when_udp_readyRead()
{
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress msgIp;
uint16_t msgPort;
udpSocket->readDatagram(datagram.data(), datagram.size(), &msgIp, &msgPort);
qDebug() << "收到來自這個IP/PORT的消息:" << msgIp.toString() << msgPort;
QString msg = datagram.data();
qDebug() << "消息內容爲" << msg;
}
void MainWindow::on_ptnCloseComm_clicked()
{
udpSocket->close();
qDebug() << "已關閉,不再接收UDP消息";
}
點擊發送按鈕,即可在【電腦2】的sscom中看到數據。在SSCOM中發送數據,也可以在QT的debug窗口收到數據。
注意事項:程序中bind某個IP/PORT之後,再次調用bind會失敗,QT的debug窗口中會報錯:
QNativeSocketEngine::bind() was not called in QAbstractSocket::UnconnectedState
解決方法是,在bind之前,先關掉socket,在重開socket,代碼很簡單:
udpSocket->close();
udpSocket->open(QIODevice::ReadWrite);
if(!udpSocket->bind(localIp, localPort))
qDebug() << "無法監聽本地UDP端口:" << localIp << localPort;
爲什麼要再次bind?其實這種需求很常見,比如用戶想修改監聽的端口號