当前位置: 首页 > news >正文

深入解析UDP服务器核心开发机制

目录

一、运行UDP服务器:从初始化到持续服务

1、服务器初始化阶段

1. 套接字创建

2. 地址绑定

2、服务器启动与运行机制

1. 无限循环架构

2. 无连接特性处理

3. 典型服务流程

3、服务器设计考量

4、完整示例框架(先了解,下面会讲)

二、核心函数:深入解析 recvfrom() 函数

1、函数原型

2、参数详解

1.sockfd

2.buf

3.len

4.flags

5.src_addr

6.addrlen

3、返回值

4、工作流程

5、典型使用场景

基本接收示例

非阻塞模式

6、重要注意事项

1. 缓冲区管理

2. 地址结构处理

3. 错误处理

4. 性能考虑

5. 与 recv() 的区别

7、高级特性(了解即可)

使用 recvmsg() 替代

辅助数据示例

8、常见问题解决方案

1. 如何处理部分接收?

2. 如何获取接收时间?

3. 如何处理多播/广播数据?

4. 如何实现超时接收?

9、最佳实践

三、服务器实现代码

四、引入命令行参数实现UDP服务器配置

1、命令行参数设计

参数说明

本地测试配置

2、完整代码实现

3、代码说明

4、std::stoi (C++) 和 std::atoi (C)

核心区别

对比表格

详细说明

1. std::stoi (String to Int)

2. atoi (ASCII to Int)

结论

5、网络状态监控

使用netstat命令查看服务器状态

命令选项说明:

输出字段解释:

示例输出分析

移除-n选项的效果

6、部署建议

1. 本地测试

2. 云服务器部署

3. 错误处理增强


一、运行UDP服务器:从初始化到持续服务

1、服务器初始化阶段

UDP服务器的初始化过程相对简洁高效,主要包含两个核心步骤:

1. 套接字创建

  • 使用系统提供的套接字API(如socket()函数)创建一个UDP类型的套接字。

  • 这一步骤会分配必要的系统资源,并返回一个用于后续操作的套接字描述符。

  • 在Unix/Linux系统中,通常使用SOCK_DGRAM参数指定UDP协议类型。

2. 地址绑定

  • 通过bind()函数将创建的套接字与特定的网络地址(IP地址和端口号组合)进行绑定。

  • 这一步骤确立了服务器在网络中的标识,使得客户端能够准确地将数据包发送到该服务端口。

  • 绑定操作可以绑定到特定IP地址(单播)或通配符地址(INADDR_ANY),后者允许服务器接收发送到所有本地网络接口的数据。

// 示例代码片段(C语言)
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP套接字
if (sockfd < 0) {perror("socket creation failed");exit(EXIT_FAILURE);
}struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;        // IPv4地址族
servaddr.sin_addr.s_addr = INADDR_ANY; // 接受任意IP地址的连接
servaddr.sin_port = htons(PORT);       // 指定服务端口if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {perror("bind failed");exit(EXIT_FAILURE);
}

2、服务器启动与运行机制

完成初始化后,服务器进入其核心功能阶段——持续提供服务。这一阶段具有以下关键特性:

1. 无限循环架构

        服务器程序的核心是一个精心设计的无限循环(常称为"事件循环"或"主循环"),它确保服务器能够持续运行而不退出。这种设计模式是服务器程序的标志性特征,区别于一次性执行完毕的客户端程序。

while (1) { // 典型的服务器主循环结构// 服务处理逻辑
}

2. 无连接特性处理

与TCP服务器不同,UDP服务器不需要建立连接(特点:不面向连接)即可直接处理数据:

  • 无连接优势:UDP的这种特性简化了服务器设计,无需维护连接状态表或处理连接建立/终止的开销

  • 直接数据接收:服务器启动后即可立即使用recvfrom()函数接收客户端数据,无需先执行accept()操作

  • 独立数据包处理:每个UDP数据包都被独立处理,服务器无需考虑数据包的顺序或完整性(这些责任由应用层承担)

3. 典型服务流程

一个完整的UDP服务循环通常包含以下步骤:

  1. 数据接收:使用recvfrom()阻塞等待客户端数据到达,该函数会返回发送方地址信息

  2. 业务处理:根据接收到的数据执行相应的业务逻辑(如计算、查询、格式转换等)

  3. 响应发送:使用sendto()将处理结果发送回客户端,指定目标地址为接收数据时的源地址

  4. 循环继续:处理完成后立即返回循环开始处,准备处理下一个数据包

char buffer[MAXLINE];
struct sockaddr_in cliaddr;
socklen_t len;
int n;while (1) {len = sizeof(cliaddr);// 接收客户端数据(阻塞式)n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len);buffer[n] = '\0'; // 确保字符串正确终止printf("Client : %s\n", buffer);// 业务处理(示例:简单回显)char *reply = "Message received";sendto(sockfd, reply, strlen(reply), MSG_CONFIRM, (const struct sockaddr *)&cliaddr, len);
}

3、服务器设计考量

在实际开发中,UDP服务器设计需要考虑多个重要因素:

  • 并发处理:虽然基本UDP服务器是顺序处理的,但可以通过多线程/多进程或I/O多路复用技术实现并发

  • 错误处理:需要妥善处理网络中断、端口占用、权限不足等异常情况

  • 性能优化:考虑使用非阻塞I/O、缓冲区管理、批处理等技术提高吞吐量

  • 安全性:实现基本的输入验证和访问控制,防止缓冲区溢出等攻击

  • 资源管理:合理设置套接字选项(如超时、缓冲区大小)和清理资源

4、完整示例框架(先了解,下面会讲)

以下是一个更完整的UDP服务器实现框架:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>#define PORT 8080
#define MAXLINE 1024int main() {int sockfd;char buffer[MAXLINE];struct sockaddr_in servaddr, cliaddr;// 1. 创建UDP套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&servaddr, 0, sizeof(servaddr));memset(&cliaddr, 0, sizeof(cliaddr));// 2. 配置服务器地址servaddr.sin_family = AF_INET; // IPv4servaddr.sin_addr.s_addr = INADDR_ANY;servaddr.sin_port = htons(PORT);// 3. 绑定套接字到地址if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}printf("UDP Server listening on port %d...\n", PORT);socklen_t len;int n;// 4. 主服务循环while (1) {len = sizeof(cliaddr);// 接收数据n = recvfrom(sockfd, (char *)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len);buffer[n] = '\0';printf("Received from client %s:%d: %s\n",inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buffer);// 这里可以添加业务处理逻辑// ...// 发送响应(示例:简单回显)sendto(sockfd, buffer, n, MSG_CONFIRM, (const struct sockaddr *)&cliaddr, len);}// 实际不会执行到这里close(sockfd);return 0;
}

这个框架展示了UDP服务器从初始化到持续服务的基本流程,实际开发中可以根据具体需求进行扩展和优化。


二、核心函数:深入解析 recvfrom() 函数

    recvfrom() 是 UDP 编程中最重要的系统调用之一,它负责从套接字接收数据并获取发送方的地址信息。下面我们将从多个角度详细解析这个函数。

1、函数原型

#include <sys/socket.h>ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

2、参数详解

1.sockfd

  • 类型:int

  • 作用:已绑定的 UDP 套接字描述符

  • 说明:必须是通过 socket() 创建并已绑定到本地地址的套接字

2.buf

  • 类型:void*

  • 作用:接收数据的缓冲区指针

  • 说明:数据将被存储在这个缓冲区中,可以是任何类型的指针(通常用 char[]

3.len

  • 类型:size_t

  • 作用:缓冲区的最大容量

  • 说明:指定最多接收多少字节的数据,防止缓冲区溢出

4.flags

  • 类型:int

  • 作用:控制函数行为的标志位

  • 常用选项:

    • 0:默认行为,阻塞接收

    • MSG_WAITALL:等待直到收到完整请求的数据量(对 UDP 通常无效,因为 UDP 是数据包导向的)

    • MSG_DONTWAIT:非阻塞模式

    • MSG_PEEK:查看数据但不从接收队列中移除

    • MSG_TRUNC:即使数据被截断也返回实际数据长度

5.src_addr

  • 类型:struct sockaddr*

  • 作用:返回发送方的地址信息

  • 说明:通常指向 struct sockaddr_in(IPv4)或 struct sockaddr_in6(IPv6)

6.addrlen

  • 类型:socklen_t*

  • 作用:输入/输出参数

  • 输入时:指定 src_addr 结构体的大小

  • 返回时:实际填充的地址结构体大小

3、返回值

  • 成功:返回实际接收的字节数

  • 错误:返回 -1 并设置 errno

  • 连接关闭(对 UDP 无意义,因为 UDP 无连接):返回 0

4、工作流程

  1. 阻塞等待(默认行为):

    • 如果没有设置非阻塞标志,recvfrom() 会阻塞直到有数据到达

    • 数据到达后,内核将数据从套接字接收缓冲区拷贝到用户缓冲区

    • 阻塞式IO:当对方未发送数据时,该函数(进程)将持续处于阻塞状态,其行为类似于scanf函数。

  2. 地址信息获取

    • 同时获取发送方的地址信息,填充到 src_addr 参数指向的结构体中

    • 这个地址信息可以用于后续的 sendto() 调用,实现回复功能

  3. 数据截断处理

    • 如果接收缓冲区空间不足,数据会被截断

    • 可以通过检查返回值与 len 的关系判断是否发生截断

5、典型使用场景

基本接收示例

struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
char buffer[1024];ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer), 0,(struct sockaddr *)&client_addr, &client_len);if (n == -1) {perror("recvfrom failed");// 错误处理
} else {buffer[n] = '\0'; // 添加字符串终止符printf("Received %zd bytes from %s:%d\n", n,inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
}

非阻塞模式

// 设置套接字为非阻塞
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);// 在非阻塞模式下使用
ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer), MSG_DONTWAIT,(struct sockaddr *)&client_addr, &client_len);if (n == -1) {if (errno == EAGAIN || errno == EWOULDBLOCK) {// 没有数据可读} else {perror("recvfrom error");}
} else {// 处理接收到的数据
}

6、重要注意事项

1. 缓冲区管理

  • 总是确保缓冲区足够大以容纳最大可能的 UDP 数据包(通常至少 64KB)

  • 考虑使用动态分配的缓冲区处理大尺寸数据

2. 地址结构处理

  • 在调用前初始化 addrlen 为地址结构体的大小

  • 检查返回后的 addrlen 以确保地址信息正确填充

3. 错误处理

  • 常见错误包括:EAGAIN/EWOULDBLOCK(非阻塞模式下无数据)、ECONNREFUSED(ICMP 错误)、EINTR(被信号中断)

4. 性能考虑

  • 在高频接收场景中,考虑使用 recvmsg() 替代,它提供更灵活的控制

  • 对于多线程应用,注意套接字的线程安全性

5. 与 recv() 的区别

  • recvfrom() 额外提供发送方地址信息

  • 对于 UDP 套接字,recv() 也能工作但无法获取源地址

7、高级特性(了解即可)

使用 recvmsg() 替代

struct msghdr msg;
struct iovec iov[1];
char buffer[1024];
struct sockaddr_in client_addr;iov[0].iov_base = buffer;
iov[0].iov_len = sizeof(buffer);msg.msg_name = &client_addr;
msg.msg_namelen = sizeof(client_addr);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;ssize_t n = recvmsg(sockfd, &msg, 0);

recvmsg() 提供了更强大的功能:

  • 分散/聚集 I/O(多个缓冲区)

  • 辅助数据接收(如时间戳)

  • 更精细的标志控制

辅助数据示例

// 启用时间戳接收
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt));// 接收消息
struct msghdr msg = {0};
struct iovec iov[1];
char buffer[1024];
char control[64];iov[0].iov_base = buffer;
iov[0].iov_len = sizeof(buffer);msg.msg_name = &client_addr;
msg.msg_namelen = sizeof(client_addr);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);ssize_t n = recvmsg(sockfd, &msg, 0);// 处理时间戳
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP) {struct timeval *tv = (struct timeval *)CMSG_DATA(cmsg);printf("Packet timestamp: %ld.%06ld\n", (long)tv->tv_sec, (long)tv->tv_usec);}
}

8、常见问题解决方案

1. 如何处理部分接收

  • UDP 是面向消息的协议,recvfrom() 通常会返回完整的数据包(除非被截断)

  • 如果返回值小于预期,可能是发送方发送了较小的数据包或发生了截断

2. 如何获取接收时间

  • 使用 recvmsg() + SO_TIMESTAMP 套接字选项(如上例所示)

  • 或者在接收后立即调用 gettimeofday()

3. 如何处理多播/广播数据

  • 接收方式与单播相同

  • 需要先通过 setsockopt() 加入多播组

4. 如何实现超时接收

  • 使用 select()/poll()/epoll() 设置超时

  • 或使用 alarm() 信号(不推荐)

  • 或设置套接字接收超时选项:

struct timeval tv;
tv.tv_sec = 5;  // 5秒超时
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

9、最佳实践

  • 始终检查返回值:不要假设接收操作一定成功或接收了完整数据

  • 合理设置缓冲区大小

    // 获取系统允许的最大UDP数据报大小
    int max_size;
    socklen_t len = sizeof(max_size);
    getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &max_size, &len);
    char *buffer = malloc(max_size);
  • 零拷贝考虑:对于高性能应用,考虑使用 recvmmsg() 批量接收多个数据包

  • 地址重用:在多进程/多线程环境中,可能需要设置 SO_REUSEADDR 选项

  • 错误处理宏:定义便捷的错误检查宏

    #define CHECK_RC(rc, msg) \if ((rc) == -1) { perror(msg); exit(EXIT_FAILURE); }

通过深入理解 recvfrom() 的工作原理和各种使用场景,可以构建出高效、可靠的 UDP 服务器应用程序。


三、服务器实现代码

        服务端通过recvfrom函数接收客户端数据时,可先将读取的数据视为字符串,并在其末尾添加'\0'终止符,这样就能直接输出接收到的数据内容。同时,可以一并输出客户端的IP地址和端口号信息。

需要注意的是:

  • 获取到的客户端端口号是网络字节序,需使用ntohs函数转换为本地主机字节序后再输出

  • 客户端IP地址以整型形式存储,需通过inet_ntoa函数转换为点分十进制字符串格式

#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>class UdpServer {
public:UdpServer(const std::string& ip, int port) : _ip(ip), _port(port) {}void InitServer() {// 创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0) {std::cerr << "socket error" << std::endl;exit(1);}// 绑定地址信息struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = inet_addr(_ip.c_str());if (bind(_sockfd, (struct sockaddr*)&local, sizeof(local)) < 0) {std::cerr << "bind error" << std::endl;exit(2);}}void Start() {const int SIZE = 128;char buffer[SIZE];for (;;) {struct sockaddr_in peer;socklen_t len = sizeof(peer);// 接收数据ssize_t size = recvfrom(_sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);if (size > 0) {buffer[size] = '\0'; // 字符串终止符// 转换端口和IP格式int port = ntohs(peer.sin_port);std::string ip = inet_ntoa(peer.sin_addr);// 输出接收到的信息std::cout << "[" << ip << ":" << port << "]# " << buffer << std::endl;} else {std::cerr << "recvfrom error, but continue..." << std::endl;continue; // 错误时不退出,继续服务}}}~UdpServer() {if (_sockfd >= 0) {close(_sockfd);}}private:int _sockfd = -1;int _port;std::string _ip;
};

注意:若 recvfrom 函数读取数据失败,仅需输出提示信息,切勿终止服务器运行。服务器不应因单个客户端数据读取失败而退出。


四、引入命令行参数实现UDP服务器配置

        在构建UDP服务器时,为了增强程序的灵活性和可配置性,我们引入命令行参数来指定服务器运行所需的端口号。虽然在实际云服务器部署时通常不需要指定IP地址(因为系统会自动绑定所有可用网络接口),但在本地开发和测试阶段,明确指定IP地址有助于调试和理解网络通信原理。

1、命令行参数设计

参数说明

  • 端口号:必需参数,用于指定服务器监听的端口

  • IP地址(可选):在本地测试时默认为127.0.0.1(本地环回地址),云部署时可省略

本地测试配置

  • 使用127.0.0.1(localhost)作为IP地址

  • 本地环回地址允许在同一台机器上进行网络通信测试

  • 测试通过后再部署到实际网络环境

2、完整代码实现

#include <iostream>
#include <string>
#include <cstdlib> // 用于atoi函数
#include "UdpServer.hpp" // 假设有UdpServer类的实现int main(int argc, char* argv[]) {// 参数检查:必须且只能有一个参数(端口号)if (argc != 2) {std::cerr << "Usage: " << argv[0] << " <port>" << std::endl;std::cerr << "Example: " << argv[0] << " 8080" << std::endl;return 1;}// 设置默认IP地址为本地环回地址const std::string ip = "127.0.0.1";// 将命令行参数(字符串)转换为整数端口号try {int port = std::stoi(argv[1]); // 使用更安全的stoi替代atoiif (port <= 0 || port > 65535) {std::cerr << "Error: Port number must be between 1 and 65535" << std::endl;return 1;}// 创建并初始化UDP服务器UdpServer* svr = new UdpServer(ip, port);svr->InitServer();// 启动服务器std::cout << "Starting UDP server on " << ip << ":" << port << std::endl;svr->Start();// 清理资源(实际应用中应有更完善的资源管理)delete svr;} catch (const std::invalid_argument& e) {std::cerr << "Error: Invalid port number - must be an integer" << std::endl;return 1;} catch (const std::out_of_range& e) {std::cerr << "Error: Port number out of range" << std::endl;return 1;}return 0;
}

3、代码说明

  • 参数验证:检查命令行参数数量是否正确、提供使用示例,因此在运行服务器的时候我们只需要传入端口号即可

  • IP地址处理

    • 硬编码为127.0.0.1用于本地测试

    • 实际部署时可修改为0.0.0.0以监听所有网络接口

  • 端口号处理

    • 使用std::stoi替代atoi,提供更好的错误处理

    • 验证端口号范围(1-65535)

    • 捕获可能的转换异常

  • 服务器生命周期:创建UdpServer实例、初始化服务器、启动服务器、清理资源

        需要特别说明的是,agrv数组中存储的是字符串类型数据,而端口号要求使用整型数值。因此,我们需要通过stoi函数将字符串转换为整数。随后,使用这个IP地址和端口号即可完成服务器的构建。服务器初始化完成后,调用Start函数即可启动服务。

运行程序时指定端口号后,就能观察到套接字成功创建并完成绑定。此时服务器已准备就绪,正在等待接收客户端发送的数据。

4、std::stoi (C++) 和 std::atoi (C)

核心区别

  • stoi: C++标准库函数,更安全、更现代,推荐在C++代码中使用。

  • atoi: C标准库函数,简单但危险,在C++中不推荐使用。

对比表格

特性std::stoi (C++)std::atoi (C)
所属语言C++C
头文件<string><cstdlib>
参数类型const std::string&const char*
错误处理会抛出异常 (std::invalid_argumentstd::out_of_range)无错误处理,失败返回0
安全性
使用场景现代C++代码,需要处理错误遗留代码或确定输入绝对安全的情况

详细说明

1. std::stoi (String to Int)

  • 用法: int stoi(const string& str, size_t* pos = 0, int base = 10);

  • 优点

    • 直接接受 std::string,方便。

    • 强大的错误处理能力。如果无法转换(如 "abc"),会抛出 std::invalid_argument 异常;如果转换后数值超出int范围(如 "12345678901234"),会抛出 std::out_of_range 异常。

    • 可以指定转换的进制(如2进制、16进制)。

  • 示例

    #include <string>
    #include <iostream>int main() {std::string str1 = "123";std::string str2 = "abc";std::string str3 = "12345678901234";int num1 = std::stoi(str1); // 成功,num1 = 123try {int num2 = std::stoi(str2); // 抛出 std::invalid_argument} catch (const std::exception& e) {std::cout << "错误: " << e.what() << std::endl;}try {int num3 = std::stoi(str3); // 抛出 std::out_of_range} catch (const std::exception& e) {std::cout << "错误: " << e.what() << std::endl;}
    }
2. atoi (ASCII to Int)

  • 用法: int atoi(const char* str);

  • 缺点

    • 接受C风格字符串 (const char*),对于 std::string 需要用 .c_str() 转换。

    • 没有错误处理!这是最大的问题。

      • 如果转换失败(如 "abc"),它直接返回 0

      • 如果字符串是 "0",它也返回 0。你无法区分是转换失败还是字符串本身就是"0"。

    • 如果转换后的值超出int范围,其行为是未定义的

  • 示例

    #include <cstdlib>
    #include <iostream>int main() {const char* str1 = "123";const char* str2 = "abc";const char* str3 = "0";int num1 = atoi(str1); // 成功,num1 = 123int num2 = atoi(str2); // 失败,但返回 0,无法知道是错误int num3 = atoi(str3); // 成功,返回 0std::cout << num2 << " " << num3; // 输出 "0 0",无法区分!
    }

结论

        在C++中,应始终优先使用 std::stoi。它更安全,能让你知道转换是否成功,避免了 atoi 在错误处理上的模糊性和潜在风险。只有在处理简单、受控且不需要错误检查的遗留代码时,才考虑使用 atoi

5、网络状态监控

使用netstat命令查看服务器状态

在服务器运行后,可以使用以下命令查看网络状态:

netstat -nlup

命令选项说明:
  • -n:禁用域名解析,直接显示IP地址

  • -l:仅显示监听状态的套接字

  • -t:显示TCP协议连接

  • -u:显示UDP协议连接

  • -p:显示进程信息

输出字段解释:
字段说明
Proto协议类型(UDP/TCP)
Recv-Q接收队列中的字节数
Send-Q发送队列中的字节数
Local Address本地地址和端口
Foreign Address远程地址和端口(UDP通常显示0.0.0.0:*)
State状态(UDP无状态,通常为空)
PID/Program name进程ID和程序名称

示例输出分析

Proto Recv-Q Send-Q Local Address    Foreign Address   State    PID/Program name
udp        0      0 127.0.0.1:8080   0.0.0.0:*                  539827/./Udp_Server
  • 表示进程539827(Udp_Server)正在127.0.0.1的8080端口监听UDP连接

  • Foreign Address为0.0.0.0:*表示接受来自任何IP和端口的连接

移除-n选项的效果

netstat -lup

此时Local Address中的IP地址会被解析为域名(如果可用),例如:

6、部署建议

1. 本地测试

  • 使用127.0.0.1进行初步测试

  • 验证基本功能是否正常

2. 云服务器部署

  • 修改IP地址为0.0.0.0以监听所有网络接口

  • 确保防火墙允许指定端口的入站连接

  • 考虑使用配置文件或环境变量替代硬编码的IP地址

3. 错误处理增强

  • 添加套接字创建和绑定的错误检查

  • 实现更优雅的服务器关闭机制

  • 添加日志记录功能

通过这种设计,我们实现了灵活的服务器配置方式,既适合本地开发测试,也能方便地部署到生产环境。

http://www.dtcms.com/a/602176.html

相关文章:

  • 阜阳做网站的公司网站开发前端跟后端的区别
  • MongoDB知识点与技巧总结
  • 企业网站 设计国外免费建站网站不用下载
  • LeetCode算法学习之数组中的第K个最大元素
  • 应急调度系统让每一次救援都精准到位
  • RL机器人人库使用简介
  • 北京网络公司建站百度爱采购优化排名软件
  • 长沙微网站开发重庆网站建设及优化公司
  • Java集合框架深度剖析 — 从源码看ArrayList、HashMap的设计与优化
  • 网站关键词排名快速提升thinkphp建站网址
  • JavaScript中??、、||、?.运算的区别
  • 微信公众号网站怎么做上海网站开发报价
  • Python压缩音乐文件大小
  • 用什么软件写网站荷城网站设计
  • 长治哪家公司做网站好怎么做网站教程视频
  • 跟踪导论(三)——滤波的释义位置信息的“观测+修正”
  • 一个电商网站开发周期是多久搜索引擎营销流程是什么?
  • 计算机做网站难吗网站建设费可以走办公费吗
  • 做公众号还是网站建网站哪家划算
  • 从App时代到智能体时代,如何打破“三堵墙”
  • jsp怎么拿到url参数
  • 有机蔬菜:清爽解腻的炖锅搭档
  • 网站的时间对齐应该怎么做wordpress中文评论插件
  • 515ppt网站建设岳麓区专业的建设网站公司
  • mysql第5次作业---hyx
  • LLM的“哥白尼革命”:物理AI与世界模型,AI的下一个战场!
  • VC软件编译C语言 | 详细教程与常见问题解答
  • 高职单招与统招比较及职业发展指南
  • Cursor vs Claude Code:AI编程工具深度对比与选择指南
  • php论坛网站源码下载大型购物网站设计