什么是UDP服务器?
核心比喻:邮筒 vs 电话
理解UDP服务器最好的方式是与更常见的TCP服务器对比:
TCP服务器像打电话:
客户必须先拨号(请求连接)。
服务器必须接听(接受连接)。
建立通话后,双方才能可靠地、按顺序地对话。
最后,一方说“再见”来挂断(关闭连接)。
UDP服务器像投递明信片:
客户写一张明信片(数据包),上面写上收件人地址(服务器IP和端口)和寄件人地址(自己的IP和端口)。
把明信片扔进邮筒(通过网络发送出去)。不关心对方是否收到,也不会得到“明信片已投递”的通知。
服务器(一个专门收明信片的邮筒)每天会检查有没有新的明信片。
如果收到一张,它就看看内容,并且可以根据寄件人地址再写一张明信片寄回去。
整个过程没有“建立连接”和“挂断”的步骤。每一次通信都是独立的。
什么是UDP服务器?
一个 UDP服务器 就是一个在网络中某个特定地址(IP地址和端口号)上等待接收UDP数据包的程序。它的核心工作流程非常简单:
开门营业:创建一个UDP类型的“邮筒”(Socket),并把它固定在一个具体的地址和端口上(Bind)。
检查邮件:持续地、被动地检查这个“邮筒”里有没有任何人寄来的“明信片”(数据包)。
处理并回复:每当收到一张“明信片”,它就处理里面的信息,然后根据明信片上的寄件人地址,选择是否回复一张新的“明信片”。
UDP服务器的关键特性
无连接:服务器不与任何客户端建立长期的、专属的连接。一个服务器可以同时处理成千上万个不同客户端发来的数据包,每个数据包都是独立的。
不可靠:它不保证客户端的消息一定能送到自己这里,也不保证自己回复的消息一定能送到客户端。消息可能在中途丢失。服务器对此不知情。
简单高效:因为没有建立连接、维护连接、保证可靠性等开销,UDP服务器通常延迟更低、消耗的资源更少。
面向数据包:数据是以一个个完整的“数据包”为单位发送和接收的。如果客户端发送了3次数据,服务器就会收到3个独立的数据包,边界清晰。
UDP服务器通常用于哪些场景?
正因为有以上特性,UDP服务器不适合网页浏览、文件传输、电子邮件等需要可靠性的服务,但它非常适合以下场景:
域名查询:当你访问
www.google.com
时,你的电脑会向DNS服务器(一个UDP服务器)发送一个简短的查询包,服务器会迅速回复一个IP地址。如果没收到回复,客户端再问一次就好了,很简单。音视频流媒体:在线视频、语音通话(如VoIP)。丢失一两个数据包只会导致画面轻微模糊或声音稍有杂音,但如果为了重传丢失的包而延迟后续的包,反而会导致视频卡顿、声音断续,体验更差。速度比完美更重要。
在线游戏:特别是多人实时对战游戏。玩家的位置、动作等信息需要以极高的频率(每秒几十次)发送和接收。丢失一个位置信息没关系,只要下一秒的最新信息能马上到来即可。
广播/多播:一个服务器可以向网络中的多个客户端同时发送同一个数据包(例如时间同步服务)。
简单总结
特性 | UDP服务器 | TCP服务器 |
---|---|---|
连接 | 无连接 | 面向连接 |
可靠性 | 不可靠,可能丢失 | 可靠,保证送达 |
顺序 | 不保证顺序 | 保证顺序 |
数据模式 | 数据包(明信片) | 数据流(水流) |
速度 | 快,延迟低 | 相对慢,有开销 |
典型应用 | DNS、游戏、视频流 | Web、Email、文件传输 |
所以,一个 UDP服务器 就是一个简单、高效、专注于快速接收和响应独立数据包的网络程序,它用“轻量”和“速度”换取了“可靠性”。
详细代码:
UDP服务器:
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<strings.h>
#include<unistd.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
#include<pthread.h>
#define SERV_PORT 8000
#define MAXLINE 80
#define prrerxit(msg){\
perror(msg); \
exit(1); \
}
int main(){
int sockfd;
struct sockaddr_in servaddr,cliaddr;
char buff[MAXLINE];
char str[INET_ADDRSTRLEN];
socklen_t cliaddr_len;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
// servaddr.sin_port=htons(SERV_PORT);
servaddr.sin_port=htons(SERV_PORT);
bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
printf("udp server ready ~\n");
int n,i ;
while(1){
cliaddr_len =sizeof(cliaddr);
n=recvfrom(sockfd,buff,MAXLINE,0,(struct sockaddr *)&cliaddr,&cliaddr_len);
if(n<0){
prrerxit("recvfrom");
}
printf("received from %s : %d\n",
inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),
ntohs(cliaddr.sin_port));
for(i=0;i<n;i++){
buff[i]=toupper(buff[i]);
}
if(sendto(sockfd,buff,n,0, (struct sockaddr *)&cliaddr,sizeof(cliaddr))<0){
prrerxit("sendto");
}
}
close(sockfd);
return 0;
}
UDP客户端:
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<strings.h>
#include<unistd.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
#define SERV_PORT 8000
#define MAXLINE 80
int main(){
struct sockaddr_in servaddr,server_response_addr;
socklen_t addr_len=sizeof(server_response_addr);
int sockfd;
int n ;
char str[INET_ADDRSTRLEN];
char buff[MAXLINE];
sockfd=socket(AF_INET,SOCK_DGRAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(SERV_PORT);
inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);
printf("UDP Client started ,Type message and press Enter :\n");
while(1){
printf("Input: ");
fflush(stdout);
if(fgets(buff,MAXLINE,stdin)==NULL){
break;
}
n=strlen(buff);
if(n<=0)break;
//去掉换行符
if(buff[n-1]=='\n'){
buff[n-1]='\0';
n--;
}
if(n==0)continue;
n = sendto(sockfd,buff,n,0,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(n<0){
perror("sendto");
break;
}
// n = recvfrom(sockfd,buff,MAXLINE,0,NULL,NULL);
n=recvfrom(sockfd,buff,MAXLINE,0,(struct sockaddr *)&server_response_addr,&addr_len);
if(n<0){
perror("recvfrom");
break;
}
printf("received from %s : %d\n",inet_ntop(AF_INET,&server_response_addr,str,sizeof(str)),
ntohs(server_response_addr.sin_port)
);
if(n<0){
perror("recvfrom");
break;
}
printf("Server response: %s\n",buff);
fflush(stdout);
}
close(sockfd);
return 0;
}