Socket网络编程——UDP协议
本文最后更新于 31 天前,其中的信息可能已经有所发展或是发生改变。

在写代码开始之前,我们先介绍以下UDP的概念,如果对socket套接字有疑惑的话,可以去TCP协议那篇文章看看。

概念

UDP(User Datagram Protocol 用户数据报协议),是一种无连接、不可靠、基于数据报的传输层协议。它不建立连接,直接把数据包发给对方,不管对方有没有收到。以下是几个核心特点:

  1. 无连接:不用 connect、listen、accept,想发就发。
  2. 不可靠:不保证数据一定到达、不保证顺序、不保证不丢失。
  3. 基于数据报:有消息边界,发几次收几次,不会粘包
  4. 开销小、速度快:没有握手、没有确认、没有重传。
  5. 支持一对一、一对多、多对多:广播、组播都靠 UDP。

从这些特点可以看出,TCP协议与UDP协议存在着一些明显的区别,UDP相比于TCP传输更快,但丢包率高,数据发送过去如果没收到就再也无法找回了(就像直播一样,要求传输速率要快,但是如果网络卡了,我们就无法看到前面发生了什么)。

对比项TCPUDP
连接面向连接无连接
可靠性可靠(不丢、不乱、重传)不可靠(可能丢包)
传输方式字节流数据报
粘包会粘包不会粘包
效率低(有握手、确认、重传)高(直接发)
头部开销大(20 字节)小(8 字节)
适用场景文件传输、聊天、网页视频、直播、语音、DNS

编程流程

我们先了解一下UDP需要使用的两个关键函数,一个是接收数据recvfrom(),一个是发送数据sendto()

recvfrom(sockfd,buf,len,0,(struct sockaddr *)&src_addr,&addr_len);
//(struct sockaddr *)&src_addr为发送方的地址
sendto(sockfd,buf,len,0,(struct sockaddr *)&dest_addr,addr_len);
//(struct sockaddr *)&dest_addr为目标地址

服务端(server)

1. 定义相关变量并填写信息

    struct sockaddr_in server_addr, client_addr;

    memset(&server_addr, 0, sizeof(server_addr));
    memset(&client_addr, 0, sizeof(client_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVERPORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

2. socket():创建UDP套接字

    int sockfd = socket(AF_INET,SOCK_DGRAM,0);

需要注意的是,这里第二个参数填写的是SOCK_DGRAM,表明使用UDP协议(SOCK_STREAM为TCP协议)。

3. bind():绑定自己的IP+端口

    socklen_t server_addr_len = sizeof(server_addr);
    socklen_t client_addr_len = sizeof(client_addr);
    int ret = bind(sockfd,(struct sockaddr *)&server_addr,server_addr_len);

4. recvfrom():阻塞等待接受客户端数据

这里是重点!由于UDP使用的是无连接方式,绑定后服务端并没有客户端的地址信息,客户端必须先发送一条信息给服务端,服务端才能获取到客户端的地址,否则无法进行网络通信!

    char buffer[1024];
    //必须先接收客户端消息,再发送消息给客户端
    memset(buffer,0,sizeof(buffer));

    ssize_t bytes_recv = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr *)&client_addr,&client_addr_len);
    handle_error("recvfrom",bytes_recv);
    printf("收到来自%s:%d客户端消息: %s\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port),buffer);
    if(strcmp(buffer,"exit") == 0)
    {
        printf("收到退出请求,服务器即将关闭...\n");
        break;
     }

5. sendto():给客户端回复消息

获取到了客户端的地址信息存储到client_addr后,我们可以给客户端发信息了。

    printf("请输入发送的消息:");
    fflush(stdout);
    fgets(buffer,sizeof(buffer),stdin);
    buffer[strcspn(buffer,"\n")] = 0; //去掉换行符
    ssize_t bytes_sent = sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr *)&client_addr,client_addr_len);
    handle_error("sendto",bytes_sent);
    if(strcmp(buffer,"exit") == 0)
    {
        printf("发送退出请求,服务器即将关闭...\n");
        break;
     }

6. close():关闭套接字

至此,我们就完成了服务端代码的编写。

客户端(client)

1. 定义相关变量并填写目标服务端信息

    struct sockaddr_in server_addr, client_addr;

    memset(&server_addr, 0, sizeof(server_addr));
    memset(&client_addr, 0, sizeof(client_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVERPORT);
    server_addr.sin_addr.s_addr = inet_addr(SERVERIP);

2. socket():创建UDP套接字

    int sockfd = socket(AF_INET,SOCK_DGRAM,0);

3. sendto():发送数据给服务端

将数据发给服务端后,服务端才可以获取到客户端的地址。

    socklen_t server_addr_len = sizeof(server_addr);
    socklen_t client_addr_len = sizeof(client_addr);
    char buffer[1024];

    //必须先发送消息给服务端,再接收服务端消息
    printf("请输入发送的消息:");
    fflush(stdout);
    fgets(buffer,sizeof(buffer),stdin);
    buffer[strcspn(buffer,"\n")] = 0; //去掉换行符
    ssize_t bytes_sent = sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr *)&server_addr,server_addr_len);
    handle_error("sendto",bytes_sent);
    if(strcmp(buffer,"exit") == 0)
    {
        printf("发送退出请求,客户端即将关闭...\n");
        break;
    }

4. recvfrom():接收服务端回复

    memset(buffer,0,sizeof(buffer));
    ssize_t bytes_recv = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr *)&server_addr,&server_addr_len);
    handle_error("recvfrom",bytes_recv);
    if(strcmp(buffer,"exit") == 0)
    {
        printf("收到退出请求,客户端关闭...\n");
        break;
    }
    printf("收到来自%s:%d服务端消息: %s\n",inet_ntoa(server_addr.sin_addr),ntohs(server_addr.sin_port),buffer);

5. close():关闭套接字


运行结果

先启动server程序,再启动client程序,客户端发送一条消息,服务端回复一条消息。

客户端或服务端发送一条exit信息,关闭服务端和客户端。

感谢阅读!如有侵权,请联系作者

感谢支持!

评论

  1. zheng
    1 月前
    2026-6-03 18:30:33

    hello

    • 博主
      zheng
      1 月前
      2026-6-03 18:31:45


      #include

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇