目录
一、运行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服务循环通常包含以下步骤:
数据接收:使用recvfrom()阻塞等待客户端数据到达,该函数会返回发送方地址信息
业务处理:根据接收到的数据执行相应的业务逻辑(如计算、查询、格式转换等)
响应发送:使用sendto()将处理结果发送回客户端,指定目标地址为接收数据时的源地址
循环继续:处理完成后立即返回循环开始处,准备处理下一个数据包
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 1024
int 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; // IPv4
servaddr.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、工作流程
阻塞等待(默认行为):
-
如果没有设置非阻塞标志,recvfrom() 会阻塞直到有数据到达
-
数据到达后,内核将数据从套接字接收缓冲区拷贝到用户缓冲区
-
阻塞式IO:当对方未发送数据时,该函数(进程)将持续处于阻塞状态,其行为类似于scanf函数。
地址信息获取:
-
同时获取发送方的地址信息,填充到 src_addr 参数指向的结构体中
-
这个地址信息可以用于后续的 sendto() 调用,实现回复功能
数据截断处理:
-
如果接收缓冲区空间不足,数据会被截断
-
可以通过检查返回值与 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替代atoi
if (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++中不推荐使用。
对比表格
| 所属语言 | C++ | C |
| 头文件 | <string> | <cstdlib> |
| 参数类型 | const std::string& | const char* |
| 错误处理 | 会抛出异常 (std::invalid_argument, std::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 = 123
try {
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 = 123
int 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. 错误处理增强
-
添加套接字创建和绑定的错误检查
-
实现更优雅的服务器关闭机制
-
添加日志记录功能
通过这种设计,我们实现了灵活的服务器配置方式,既适合本地开发测试,也能方便地部署到生产环境。






