目录
记得点赞!!!
UDP简介
UDP是一种面向数据报的协议,它不提供连接的稳定性和可靠性,而是专注于快速地将数据报从一个节点发送到另一个节点。相比于TCP,UDP更适用于对实时性要求较高、可以容忍少量数据丢失的场景,比如音频、视频传输等。
前置知识
在深入研究UDP服务器的代码之前,让我们先了解一些关键的网络编程概念:
-
套接字(Socket): 套接字是网络编程中的一个基本概念,用于在两个程序之间建立通信。套接字可以看作是一个端点,通过它程序可以收发数据。在本例中,我们使用了
socket
函数创建UDP套接字。 -
IP地址和端口号: IP地址是用于标识网络中计算机的一串数字,而端口号是用于标识一个程序的逻辑门户。在UDP通信中,IP地址和端口号一起构成了节点的地址。服务器需要绑定一个IP地址和端口号,以便客户端能够找到它。
-
地址族和
sockaddr_in
结构体: 地址族(Address Family)是用于定义套接字地址的一种机制。AF_INET
表示IPv4地址族。sockaddr_in
是一个结构体,用于存储IPv4地址和端口号等信息。
在网络编程的世界里,UDP(User Datagram Protocol)是一种轻量级的通信协议,为快速、实时的数据传输提供了一种高效的方式。在这篇博客中,我们将详细讨论如何使用C++编写一个简单而强大的UDP服务器,并提供了一个附加的UDP客户端以便测试。
步骤1:引入必要的头文件
首先,我们需要引入一系列头文件,这些头文件包含了我们在整个程序中将要使用的网络编程相关的函数和结构。
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
步骤2:创建UDP套接字
使用socket
函数创建一个UDP套接字,这是服务器用于与客户端进行通信的关键步骤。
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("Error creating socket");
exit(EXIT_FAILURE);
}
步骤3:填充服务器地址信息
在服务器端,我们需要指定一个IP地址和端口号,客户端将使用这些信息来连接到服务器。
struct sockaddr_in serverAddress;
bzero(&serverAddress, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(12345); // 服务器端口号
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
步骤4:绑定套接字
通过bind
函数将套接字绑定到指定的地址和端口。
if (bind(sockfd, (const struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
perror("Error binding socket");
close(sockfd);
exit(EXIT_FAILURE);
}
步骤5:接收和发送数据
在这一步,你可以添加代码来处理接收和发送数据的逻辑。使用recvfrom
接收来自客户端的数据,使用sendto
将数据发送回客户端。
while (true) {
char buffer[1024];
struct sockaddr_in clientAddress;
socklen_t clientLen = sizeof(clientAddress);
// 接收数据
ssize_t receivedBytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&clientAddress, &clientLen);
if (receivedBytes > 0) {
buffer[receivedBytes] = '\0';
std::cout << "Received message from client: " << buffer << std::endl;
// 发送数据(回应客户端)
sendto(sockfd, buffer, receivedBytes, 0, (struct sockaddr*)&clientAddress, clientLen);
}
}
步骤6:关闭套接字
最后,确保在程序结束时关闭已创建的套接字。
close(sockfd);
完整的UDP服务器代码
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
// 创建UDP套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("Error creating socket");
exit(EXIT_FAILURE);
}
// 填充服务器地址信息
struct sockaddr_in serverAddress;
bzero(&serverAddress, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(12345); // 服务器端口号
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
// 绑定套接字
if (bind(sockfd, (const struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
perror("Error binding socket");
close(sockfd);
exit(EXIT_FAILURE);
}
std::cout << "Server is waiting for incoming messages..." << std::endl;
// 接收和发送数据
while (true) {
char buffer[1024];
struct sockaddr_in clientAddress;
socklen_t clientLen = sizeof(clientAddress);
// 接收数据
ssize_t receivedBytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&clientAddress, &clientLen);
if (receivedBytes > 0) {
buffer[receivedBytes] = '\0';
std::cout << "Received message from client: " << buffer << std::endl;
// 发送数据(回应客户端)
sendto(sockfd, buffer, receivedBytes, 0, (struct sockaddr*)&clientAddress, clientLen);
}
}
// 关闭套接字
close(sockfd);
return 0;
}
UDP 客户端代码
#include <iostream>
#include <string>
#include <cstdlib>
#include <cassert>
#include <unistd.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: ./udpClient server_ip server_port" << std::endl;
exit(EXIT_FAILURE);
}
std::string serverIp = argv[1];
uint16_t serverPort = atoi(argv[2]);
// 创建UDP套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
assert(sockfd > 0);
// 填充服务器地址信息
struct sockaddr_in serverAddress;
bzero(&serverAddress, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(serverPort);
serverAddress.sin_addr.s_addr = inet_addr(serverIp.c_str());
std::string message;
while (true) {
std::cerr << "Please Enter Message (type 'exit' to quit): ";
std::getline(std::cin, message);
// 发送数据到服务器
sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
if (message == "exit") {
std::cout << "Exiting client..." << std::endl;
break;
}
char buffer[1024];
struct sockaddr_in serverResponse;
socklen_t serverResponseLen = sizeof(serverResponse);
// 接收服务器的回应
ssize_t receivedBytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&serverResponse, &serverResponseLen);
if (receivedBytes > 0) {
buffer[receivedBytes] = '\0';
std::cout << "Received response from server: " << buffer << std::endl;
}
}
// 关闭套接字
close(sockfd);
return 0;
}
这些代码提供了一个完整的UDP服务器和客户端的示例,展示了UDP通信的基本步骤。通过运行这两个程序,你可以深入理解UDP的工作原理,并通过输入消息来测试服务器的响应。希望这篇博客对你理解UDP服务器的实现和使用有所帮助。