服務端代碼 (python)
import socketdef udp_server(host='0.0.0.0', port=12345):# 創建一個UDP套接字sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 綁定服務器的IP地址和端口號sock.bind((host, port))print(f"UDP服務器已啟動,監聽端口 {port}...")try:while True:# 接收來自客戶端的數據包data, addr = sock.recvfrom(1024) # 1024是緩沖區大小# 打印接收到的數據和客戶端地址print(f"接收到來自 {addr} 的數據: {data.decode()}")# 構造響應數據response = f"服務器已接收到來自 {addr} 的數據: {data.decode()}"# 向客戶端發送響應數據包sock.sendto(response.encode(), addr)# 打印發送響應的確認print(f"已向 {addr} 發送響應數據")except KeyboardInterrupt:print("服務器接收到中斷信號,正在關閉...")finally:# 關閉套接字sock.close()print("服務器已關閉")# 運行UDP服務器
udp_server()
在這個示例中,服務器使用recvfrom
方法接收數據包,該方法會阻塞直到接收到一個數據包。數據包包含兩部分:數據本身和客戶端的地址(IP地址和端口號)。
注意,雖然這個服務器代碼看起來是順序執行的,但實際上UDP套接字能夠同時接收多個數據包。當多個數據包同時到達時,它們會被放入操作系統的網絡緩沖區中,recvfrom
方法會依次處理這些數據包。因此,服務器能夠“同時”處理多個客戶端的請求,盡管在代碼層面上它是順序處理的。
對于UDP來說,由于每個數據包都是獨立的,并且沒有建立持久的連接,所以通常不需要像TCP那樣為每個客戶端創建單獨的線程或進程。
客戶端:
如果程序在發送數據之前沒有綁定端口,那么每次發送數據時,系統可能會分配一個新的端口號?。這是因為UDP協議本身不要求在通信雙方之間建立連接,因此每次發送數據時都可以使用不同的端口號。在接收端收到數據之后,系統又會自動釋放掉這個端口號?1。
但是,?如果程序在發送數據之前綁定了特定的端口,那么只要程序不退出,它就會一直使用這個綁定的端口來發送消息?。即使多次發送數據,端口號也不會改變,除非程序重新啟動或顯式地解綁并重新綁定到另一個端口?。
UDP通信通常不區分嚴格的客戶端和服務器角色,因為UDP是無連接的協議。不過,為了說明問題,我們可以將兩個實體分別稱為“發送者”(可以視作客戶端)和“接收者”(可以視作服務器),盡管它們在UDP中實際上是對等的。
以下是 QT代碼的實現
?客戶端(qt)
#include <QCoreApplication>
#include <QUdpSocket>
#include <QByteArray>
#include <QHostAddress>
#include <QTimer>
#include <QDebug>class UdpClient : public QObject {Q_OBJECTpublic:UdpClient(QObject *parent = nullptr): QObject(parent), udpSocket(new QUdpSocket(this)) {// 定時器用于定期發送數據QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &UdpClient::sendDatagram);timer->start(1000); // 每秒發送一次// 連接接收信號到槽函數connect(udpSocket, &QUdpSocket::readyRead, this, &UdpClient::readPendingDatagrams);}private slots:void sendDatagram() {QByteArray datagram = "Hello from Client!";udpSocket->writeDatagram(datagram, QHostAddress::LocalHost, 12345);qDebug() << "Sent datagram:" << datagram;}void readPendingDatagrams() {while (udpSocket->hasPendingDatagrams()) {QByteArray datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress sender;quint16 senderPort;udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);qDebug() << "Received datagram from" << sender.toString() << ":" << senderPort;qDebug() << datagram;}}private:QUdpSocket *udpSocket;
};int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);UdpClient client;return a.exec();
}#include "main.moc"
服務端(qt)?
#include <QCoreApplication>
#include <QUdpSocket>
#include <QByteArray>
#include <QDebug>class UdpServer : public QObject {Q_OBJECTpublic:UdpServer(QObject *parent = nullptr): QObject(parent), udpSocket(new QUdpSocket(this)) {// 綁定接收端口udpSocket->bind(12345);// 連接接收信號到槽函數connect(udpSocket, &QUdpSocket::readyRead, this, &UdpServer::readPendingDatagrams);}private slots:void readPendingDatagrams() {while (udpSocket->hasPendingDatagrams()) {QByteArray datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress sender;quint16 senderPort;udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);qDebug() << "Received datagram from" << sender.toString() << ":" << senderPort;qDebug() << datagram;// 發送響應給客戶端QByteArray response = "Hello from Server!";udpSocket->writeDatagram(response, sender, senderPort);qDebug() << "Sent response:" << response;}}private:QUdpSocket *udpSocket;
};int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);UdpServer server;return a.exec();
}#include "main.moc"
FR:徐海濤(hunkxu)