UDP服务器和客户端

发布于:2025-09-04 ⋅ 阅读:(19) ⋅ 点赞:(0)

什么是UDP服务器?

核心比喻:邮筒 vs 电话

理解UDP服务器最好的方式是与更常见的TCP服务器对比:

  • TCP服务器像打电话

    1. 客户必须先拨号(请求连接)。

    2. 服务器必须接听(接受连接)。

    3. 建立通话后,双方才能可靠地、按顺序地对话。

    4. 最后,一方说“再见”来挂断(关闭连接)。

  • UDP服务器像投递明信片

    1. 客户写一张明信片(数据包),上面写上收件人地址(服务器IP和端口)和寄件人地址(自己的IP和端口)。

    2. 把明信片扔进邮筒(通过网络发送出去)。不关心对方是否收到,也不会得到“明信片已投递”的通知。

    3. 服务器(一个专门收明信片的邮筒)每天会检查有没有新的明信片。

    4. 如果收到一张,它就看看内容,并且可以根据寄件人地址再写一张明信片寄回去。

    5. 整个过程没有“建立连接”和“挂断”的步骤。每一次通信都是独立的。


什么是UDP服务器?

一个 UDP服务器 就是一个在网络中某个特定地址(IP地址和端口号)上等待接收UDP数据包的程序。它的核心工作流程非常简单:

  1. 开门营业:创建一个UDP类型的“邮筒”(Socket),并把它固定在一个具体的地址和端口上(Bind)。

  2. 检查邮件:持续地、被动地检查这个“邮筒”里有没有任何人寄来的“明信片”(数据包)。

  3. 处理并回复:每当收到一张“明信片”,它就处理里面的信息,然后根据明信片上的寄件人地址,选择是否回复一张新的“明信片”。

UDP服务器的关键特性

  1. 无连接:服务器不与任何客户端建立长期的、专属的连接。一个服务器可以同时处理成千上万个不同客户端发来的数据包,每个数据包都是独立的。

  2. 不可靠:它不保证客户端的消息一定能送到自己这里,也不保证自己回复的消息一定能送到客户端。消息可能在中途丢失。服务器对此不知情。

  3. 简单高效:因为没有建立连接、维护连接、保证可靠性等开销,UDP服务器通常延迟更低、消耗的资源更少

  4. 面向数据包:数据是以一个个完整的“数据包”为单位发送和接收的。如果客户端发送了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;
}