udp 协议

udp 是一种无连接的传输协议, 全称User Datagram Protocol. 由于其无连接的特性, 放弃数据的可靠性, 换取更简单的交互逻辑, 更简单的数据结构和更高的传输性能. 即使udp协议不可靠, 应该说是传输层的不可靠. 但通过应用层再次开发还是可以在一定程度上保证数据的可靠性.

upd的使用场景:

  • 需要资源少, 在网络情况较好的内网, 或对丢包不敏感的应用
  • 不需要一对一沟通, 建立连接, 而是可以广播的应用
  • 需要处理速度快, 时延低, 可以容忍少数丢包, 但要求即便网络拥塞, 也毫不退缩, 一往无前的时候
  • 需要多播, 应用层自己控制传输的地方: DHCP, VXLAN, QUIC

使用场景具体的例子:

  • 网页或者APP的访问
  • QUIC(Quick UDP Internet Connections, 快速Udp 互联网连接): 快速建立连接, 减少重传时延, 自适应拥塞控制
  • 流媒体的协议
  • 实时游戏
  • IoT 物联网: 物联网通信协议Thread, 基于Udp协议
  • 移动通信领域: GTP-U 协议, 基于Udp协议

udp 协议头

上图中有一个伪首部并不是udp协议的一部分, 而是用于计算udp首部校验和时使用的. 而整个udp首部仅有8个字节, 可见udp协议是如此的简单.

下面看一个udp客户端和服务端的实例:

  • 客户端
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define PORT 6023
#define ADDR "0.0.0.0"

int main(int argc, char *argv[]) {
    int sock = -1, ret = 0;
    struct sockaddr_in addrto;
    const char *smsg = "The msg from udp client";

    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        printf("socket error\n");
        return -1;
    }

    addrto.sin_family = AF_INET;
    addrto.sin_addr.s_addr = inet_addr(ADDR);
    addrto.sin_port = htons(PORT);

    while (1) {
        ret = sendto(sock, smsg, strlen(smsg), 0, (struct sockaddr *)&addrto, sizeof(addrto));
        if (ret < 0) {
            printf("send error....\n");
        } else {
            printf("send msg: %s\n", smsg);
        }

        sleep(1);
    }

    return 0;
}
  • 服务端
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define PORT 6023

int main() {
    struct sockaddr_in addr, from;
    int sock = -1, ret = 0;
    int len = sizeof(struct sockaddr_in);
    char smsg[100] = {0};

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(PORT);

    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        printf("socket error\n");
        return -1;
    }

    if (bind(sock, (struct sockaddr *)&(addr), sizeof(addr)) == -1) {
        printf("bind error...\n");
        return -1;
    }

    while (1) {
        ret = recvfrom(sock, smsg, sizeof(smsg) - 1, 0,
                       (struct sockaddr *)&from, (socklen_t *)&len);
        if (ret <= 0) {
            printf("read error....\n");
        } else {
            printf("recv msg: %s\n", smsg);
        }
    }

    return 0;
}
  • Makefile
all: cln svr

cln: cln.o
    gcc -o $@ $^

svr: svr.o
    gcc -o $@ $^

clean:
    rm -f *.o cln svr

如果觉得有帮助, 可以扫描右边的微信打赏码支持一下.

Leave a Reply

Your email address will not be published. Required fields are marked *