什么是协议栈? 用户态协议栈设计(udp协议栈)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 什么是协议栈? 用户态协议栈设计(udp协议栈)

1.png

什么是协议栈呢?


协议栈(Protocol Stack)是计算机网络和通信系统中的一个重要概念,它指的是一组协议层的层次结构,这些协议层一起协同工作,以便在不同计算机或设备之间实现数据通信和交换。每个协议层都有特定的功能和责任,从物理层到应用层,每一层都在不同的抽象级别上处理数据和通信任务)友情提示,请阅读代码的注释


通过mmap可以将网卡里的数据映射到内存中去


这里是零拷贝,指的是cpu指令没有参与,但并不是没有拷贝,这是一种DMA的方式


实现协议栈有几种方式,如raw-socket、netmap、dpdk等,这里用netmap实现


如果不这样实现的话,会多拷贝一次,下面看原理图


. 获取原始数据


获取原始数据的三种方法介绍


不经过网络协议栈解析,拿到原始数据sk_buff;


  1. 使用原始套接字raw socket , tcpdump和wireshark就是使用这个做的,raw socket主要用来抓包。


  1. dbdk


  1. netmap是用于用户层应用程序收发原始网络数据的高性能框架,本文使用netmap进行数据的收发。


1、netmap 原理


网卡即不在物理层,也不在数据链路层,是在这两层之间做转换。


数据传输的流程


网卡将物理层的光电信号转换为数字信号(0101010)。给到网卡驱动,然后把这个数据(通过sk_buff(搬运工)) 拷贝迁移到协议栈。 然后协议栈解析完数据之后将数据拷贝放入recv buffer,然后应用程序通过系统调用就能得到这个数据。

 

netmap 采用 mmap 的方式,将网卡驱动的 ring 内存空间映射到用户空间。这样用户态可以直接操作内存,获取原始的数据,避免了内核和用户态的两次拷贝(网卡 -> 内核协议栈 -> 内存)

2.png3.png

如果不用netmap走内核协议栈的话,我们在驱动和协议栈之间拷贝一次,在协议栈和应用层拷贝一次,那么就走了两次,当大量数据到来的话就会造成 传输速度下降,因为我们的IO操作


其实是很费时间的,所以我们就拷贝一次,大大的缩短了时间


2、netmap 环境搭建


安装 netmap

# 安装 netmap
git clone https://github.com/luigirizzo/netmap.git
cd netmap/LINUX
./configure
make && make install
# 将头文件拷贝到 /usr/include/net
cd ./netmap/sys/net/ # netmap 头文件位置
cp * /usr/include/net  

启动 netmap

# 开启 netmap
insmod netmap.ko 
ls /dev/netmap -l
# 关闭 netmap
rmmod netmap.ko
3、udp 协议栈的实现


3.1.以太网协议头格式

4.png

struct ethhdr {
  unsigned char h_dst[ETH_ALEN];//源mac,6字节
  unsigned char h_src[ETH_ALEN];//目的mac,6字节
  unsigned short h_proto;//协议类型,2字节
};
3.2.ip协议头格式

5.png

struct iphdr {
  unsigned char hdrlen:4,  //为什么跟报头看着不一样呢,那是因为我们的网络字节序是大端的
          version:4; // 0x45     
  //我们的协议报头的时候,低位是版本号,高位是报头长度
    //那么编程的时候,低位的报头长度,高位是版本号
    //那么在转到网络上去之后就是低位是版本号       
    //字节序问题,请去百度一下大小端问题
  unsigned char tos;//type of service
  unsigned short totlen;//total length
  //ip包总大小 - 首部大小等于数据大小
  unsigned short id;//16位标识
    //标识分片的包,因为网络层向下传的时候
    //会受mtu的大小,进行分片
    //所以要想确保数据是正常的,就需要设置一个标识,标识完整的数据包
    //也以便ip上传到传输层后续重组
  unsigned short flag_offset; //3位标志+13位片偏移
    //3位标识一个是df为1表示数据包不可以分片,0表示告知可以分片,
    //mf标识是否有更多分片,为0就表示最后一个分片了
    //那么我们在收到包的时候可以根据这个标志位和16位标识以及片偏移量重组数据了
    //
  unsigned char ttl; //time to live 生存周期(比如:每经过一个网关ttl-1)
  // 0x1234// htons
  unsigned char type;//8位协议  用于指明IP的上层协议.传输层的报头没有协议
  unsigned short check;//16位首部校验和
  unsigned int sip;//源ip,标识发送方主机
  unsigned int dip;//目的ip,标识接收方主机
}; // 20字节
3.3 udp协议头

6.png

//udp协议头
struct udphdr {
  unsigned short sport;//源端口
  unsigned short dport;//目的端口
  unsigned short length;//udp长度
  unsigned short check;//校验值
}; // 8字节
3.4 arp协议头

7.png

struct arphdr {
  unsigned short h_type;//硬件类型
  unsigned short h_proto;//协议类型
    //真正的地址是mac地址,
    //ip地址是逻辑地址,mac地址是物理地址,唯一标识一台主机的
  unsigned char h_addrlen;
  unsigned char h_protolen;
  unsigned short oper;//操作码,在发送arp包的时候,会
    //用到操作码,arp响应2和arp请求1
    //有了这个操作码,我们就知道是请求获取我的mac地址还是
    //我的arp请求已经到了(响应)
    //因为刚开始发arp包的时候,只携带自身的mac地址和arp请求
    //发送过后,再回发arp响应,将mac地址填上,此时收到的
    //arp包的源mac地址就是我们之前广播的主机的mac地址
    //然后做一个映射
  unsigned char smac[ETH_ADDR_LENGTH];
  unsigned int sip;
  unsigned char dmac[ETH_ADDR_LENGTH];
  unsigned int dip;
};

ICMP协议头我就不实现了,主要是用来进行ping命令的


3.5 各层数据包格式


我们还得定义一下OSI七层模型的数据包,因为网络层的数据包从上到下是解包和封包的过程

8.png

越下面的层,会封装上面的层的协议头

struct udppkt {
  struct ethhdr eh;
  struct iphdr ip;
  struct udphdr udp;
  unsigned char data[0];//用户数据
    //柔性数组,这样就可以在结构体末尾动态地分配内存空间。
    //不会发生越界情况
};
//定义完以太网包,ip包和udp包之后
//我们还需要定义一个arp包
//为什么呢,因为arp缓存
//在我们xshell连接上之后会将
//eh0这张网卡的mac地址和ip地址做一个映射关系
//过一段时间之后这个mac和ip地址的映射关系就会消失
//所以我们需要自己搞一个arp包或者自己设置arp缓存
//或者静态的
//没有设置也没有静态arp缓存的话客户端就会发一次arp包
//那么既然我们是用netmap的方式接收的包,那么就需要自己接收
//到包,封装包,
struct arppkt {
  struct ethhdr eh;
  struct arphdr arp;
};

其他的包就不写了,其实就是在上层的包那里,添加下当前网络层的协议头

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/poll.h>
#include <arpa/inet.h>
#define ETH_ADDR_LENGTH   6
#define NETMAP_WITH_LIBS
#include <net/netmap_user.h> 
#pragma pack(1)
//内存对齐设置为1,如果不设置为1的话
//会出问题,参考我的博客里的内存对齐篇
#define ETH_ALEN  6
#define PROTO_IP  0x0800
#define PROTO_ARP 0x0806
#define PROTO_UDP 17
#define PROTO_ICMP  1
#define PROTO_IGMP  2
struct ethhdr {
  unsigned char h_dst[ETH_ALEN];//源mac,6字节
  unsigned char h_src[ETH_ALEN];//目的mac,6字节
  unsigned short h_proto;//协议类型,2字节
};
struct iphdr {
  unsigned char hdrlen:4,  //为什么跟报头看着不一样呢,那是因为我们的网络字节序是大端的
          version:4; // 0x45     
  //我们的协议报头的时候,低位是版本号,高位是报头长度
    //那么编程的时候,低位的报头长度,高位是版本号
    //那么在转到网络上去之后就是低位是版本号       
    //字节序问题,请去百度一下大小端问题
  unsigned char tos;//type of service
  unsigned short totlen;//total length
  //ip包总大小 - 首部大小等于数据大小
  unsigned short id;//16位标识
    //标识分片的包,因为网络层向下传的时候
    //会受mtu的大小,进行分片
    //所以要想确保数据是正常的,就需要设置一个标识,标识完整的数据包
    //也以便ip上传到传输层后续重组
  unsigned short flag_offset; //3位标志+13位片偏移
    //3位标识一个是df为1表示数据包不可以分片,0表示告知可以分片,
    //mf标识是否有更多分片,为0就表示最后一个分片了
    //那么我们在收到包的时候可以根据这个标志位和16位标识以及片偏移量重组数据了
    //
  unsigned char ttl; //time to live 生存周期(比如:每经过一个网关ttl-1)
  // 0x1234// htons
  unsigned char type;//8位协议  用于指明IP的上层协议.传输层的报头没有协议
  unsigned short check;//16位首部校验和
  unsigned int sip;//源ip,标识发送方主机
  unsigned int dip;//目的ip,标识接收方主机
}; // 20字节
//udp协议头
struct udphdr {
  unsigned short sport;//源端口
  unsigned short dport;//目的端口
  unsigned short length;//udp长度
  unsigned short check;//校验值
}; // 8字节
struct udppkt {
  struct ethhdr eh;
  struct iphdr ip;
  struct udphdr udp;
  unsigned char data[0];//用户数据
    //柔性数组,这样就可以在结构体末尾动态地分配内存空间。
    //不会发生越界情况
};
//定义完以太网包,ip包和udp包之后
//我们还需要定义一个arp包
//为什么呢,因为arp缓存
//在我们xshell连接上之后会将
//eh0这张网卡的mac地址和ip地址做一个映射关系
//过一段时间之后这个mac和ip地址的映射关系就会消失
//所以我们需要自己搞一个arp包或者自己设置arp缓存
//或者静态的
//没有设置也没有静态arp缓存的话客户端就会发一次arp包
//那么既然我们是用netmap的方式接收的包,那么就需要自己接收
//到包,封装包,
struct arphdr {
  unsigned short h_type;//硬件类型
  unsigned short h_proto;//协议类型
    //真正的地址是mac地址,
    //ip地址是逻辑地址,mac地址是物理地址,唯一标识一台主机的
  unsigned char h_addrlen;
  unsigned char h_protolen;
  unsigned short oper;//操作码,在发送arp包的时候,会
    //用到操作码,arp响应2和arp请求1
    //有了这个操作码,我们就知道是请求获取我的mac地址还是
    //我的arp请求已经到了(响应)
    //因为刚开始发arp包的时候,只携带自身的mac地址和arp请求
    //发送过后,再回发arp响应,将mac地址填上,此时收到的
    //arp包的源mac地址就是我们之前广播的主机的mac地址
    //然后做一个映射
  unsigned char smac[ETH_ADDR_LENGTH];
  unsigned int sip;
  unsigned char dmac[ETH_ADDR_LENGTH];
  unsigned int dip;
};
struct arppkt {
  struct ethhdr eh;
  struct arphdr arp;
};
//icmp我就不封装了,有icmp协议我们才能用ping命令,否则用ping命令是
//ping不通的,可以用wireshark抓包看一下有没有icmp协议
int str2mac(char *mac, char *str) {
  char *p = str;
  unsigned char value = 0x0;
  int i = 0;
  while (*p != '\0') {
    if (*p == ':') {
      mac[i++] = value;
      value = 0x0;
    } else {
      unsigned char temp = *p;
      if (temp <= '9' && temp >= '0') {
        temp -= '0';
      } else if (temp <= 'f' && temp >= 'a') {
        temp -= 'a';
        temp += 10;
      } else if (temp <= 'F' && temp >= 'A') {
        temp -= 'A';
        temp += 10;
      } else {  
        break;
      }
      value <<= 4;
      value |= temp;
    }
    p ++;
  }
  mac[i] = value;
  return 0;
}
void echo_arp_pkt(struct arppkt *arp, struct arppkt *arp_rt, char *mac) {
  //把源和目的 的ip换一下就行了,然后补个mac地址
  memcpy(arp_rt, arp, sizeof(struct arppkt));
  memcpy(arp_rt->eh.h_dst, arp->eh.h_src, ETH_ADDR_LENGTH);
  str2mac(arp_rt->eh.h_src, mac);
  arp_rt->eh.h_proto = arp->eh.h_proto;
  arp_rt->arp.h_addrlen = 6;
  arp_rt->arp.h_protolen = 4;
  arp_rt->arp.oper = htons(2);
  str2mac(arp_rt->arp.smac, mac);
  arp_rt->arp.sip = arp->arp.dip;
  memcpy(arp_rt->arp.dmac, arp->arp.smac, ETH_ADDR_LENGTH);
  arp_rt->arp.dip = arp->arp.sip;
}
//就是解析完arp包后再发过去,目的mac和源mac什么的变一下
// void echo_arp_pkt(struct arppkt *arp, struct arppkt *arp_rt, char *mac):
//这是一个函数定义,它接受三个参数,其中 arp 是指向输入ARP数据包的指针,arp_rt 
//是指向输出ARP数据包的指针,mac 是一个字符数组,可能用于存储MAC地址。
// // memcpy(arp_rt, arp, sizeof(struct arppkt)):
// 这行代码将从输入ARP数据包 arp 复制整个数据包的内容到输出ARP数据包 arp_rt 中,
// 复制的字节数为 sizeof(struct arppkt)。
// // memcpy(arp_rt->eh.h_dst, arp->eh.h_src, ETH_ADDR_LENGTH):
// 这行代码将输入ARP数据包中的目标MAC地址(eh.h_dst)复制到输出ARP数据包的源MAC地址(eh.h_src),
// 以交换它们的值。 ETH_ADDR_LENGTH 可能是一个常量,表示MAC地址的长度。
// // str2mac(arp_rt->eh.h_src, mac):
// 这行代码似乎是将 mac 中的MAC地址数据复制到输出ARP数据包的源MAC地址字段(eh.h_src)中。
// // arp_rt->eh.h_proto = arp->eh.h_proto:
// 这行代码将输出ARP数据包的以太网协议类型字段(eh.h_proto)设置为与输入ARP数据包相同的值,以保持协议类型不变。
// // arp_rt->arp.h_addrlen = 6 和 arp_rt->arp.h_protolen = 4:
// 这两行代码设置输出ARP数据包的地址长度字段和协议地址长度字段。
// // arp_rt->arp.oper = htons(2):这行代码将输出ARP数据包的操作码字段(oper)设置为2,这表示ARP响应。
// // str2mac(arp_rt->arp.smac, mac):这行代码将 mac 中的MAC地址数据复制到
// 输出ARP数据包的发送方MAC地址字段(smac)中。
// // arp_rt->arp.sip = arp->arp.dip:这行代码将输出ARP数据包的发送方IP地址字段(sip)
// 设置为输入ARP数据包的目标IP地址字段(dip)的值。
// // memcpy(arp_rt->arp.dmac, arp->arp.smac, ETH_ADDR_LENGTH):这行代码将输入ARP数据包的源MAC地址(smac)
// 复制到输出ARP数据包的目标MAC地址字段(dmac),以交换它们的值。
// // arp_rt->arp.dip = arp->arp.sip:这行代码将输出ARP数据包的目标IP地址字段(dip)
// 设置为输入ARP数据包的发送方IP地址字段(sip)的值。
// // 总的来说,这个函数接受一个ARP请求数据包,将其内容复制到一个ARP响应数据包中,
// 同时交换了源和目标的MAC地址和IP地址,以制作一个相应的ARP响应数据包,用于回应原始ARP请求。
// 这种操作通常用于网络通信中,以满足地址解析的需求。函数的实现可能依赖于其他未提供的函数或数据结构,
// 如 struct arppkt 和 str2mac。
//
int main() {
  struct nm_pkthdr h;//ringbuffer的头
  struct nm_desc *nmr = nm_open("netmap:eth0", NULL, 0, NULL);
  if (nmr == NULL) return -1;
  //把fd放入pollfd中,如果fd可读,就去操作数据,不可读就不操作
  struct pollfd pfd = {0};
  pfd.fd = nmr->fd;
  pfd.events = POLLIN;
  while (1) {
    int ret = poll(&pfd, 1, -1);//第一个参数:pollfd,第二个参数:fd个数,第三个参数:-1代表一直阻塞,直到数据过来
    if (ret < 0) continue;
    if (pfd.revents & POLLIN) {//有数据来了
      unsigned char *stream = nm_nextpkt(nmr, &h);//取数据(因为已经在内存中了,不能用读,由于是环形ringbuffer,因此取数据叫next package)
      struct ethhdr *eh = (struct ethhdr *)stream;//把stream中的第一个部分转换为以太网头
      if (ntohs(eh->h_proto) ==  PROTO_IP) { //取出来的上层协议是IP协议
        struct udppkt *udp = (struct udppkt *)stream;//转化为udp帧数据格式
        if (udp->ip.type == PROTO_UDP) { //udp包
          int udplength = ntohs(udp->udp.length);
          udp->data[udplength-8] = '\0'; //udp总长度-8个字节长度的udp头  就是upd数据部分的长度。  末尾加上字符串结尾'\0'
          printf("udp --> %s\n", udp->data);
        } else if (udp->ip.type == PROTO_ICMP) {
        }
      } else if (ntohs(eh->h_proto) ==  PROTO_ARP) {//ARP包
        struct arppkt *arp = (struct arppkt *)stream;
        struct arppkt arp_rt;
               //eth0的ip地址,eth0是网卡接口
        if (arp->arp.dip == inet_addr("10.0.4.12")) { //如果接受到的广播arp是本机的就回复 (如果不进行判断就是ARP攻击了,不管是什么arp请求,都回复,会导致它们的arp表更新错误的信息)
                    //eth0的mac地址
          echo_arp_pkt(arp, &arp_rt, "52:54:00:d5:c3:82");//创建一个arp回复的包(源和目的互换,补充上mac地址(ifconfig可以查看))
          nm_inject(nmr, &arp_rt, sizeof(arp_rt));//发送arp应答
          printf("arp ret\n");
        }
      }
    }
  }
}

使用nm_open()函数时,需要指定的是物理网卡名eth0是物理显卡名,ens33是虚拟网卡名。


修改网卡名字:

sudo vim /etc/default/grub
//修改GRUB_CMDLINE_LINUX为如下,主要是增加 net.ifnames=0 biosdevname=0 这句
GRUB_CMDLINE_LINUX="find_presend=/presend.cfg noprompt net.ifnames=0 biosdevname=0 default_hugepagesz=1G hugepagesz=2M hugepages=1024 isolcpus=0-2"

启动程序后刚开始可以接收udp包,过一段时间后就接受不到了


1.原因:程序把网卡的数据发送到了共享内存,不经过协议栈。而局域网内所有机器每隔一段时间会发送arp协议告知局域网内其他机器自己的IP和MAC地址,如果一段时间内没有收到对方的arp协议,那么本机就会把arp表对应的arp协议信息(IP和MAC地址)删掉。


因此,因为一开始发送udp包对方的时候,还知道对方的IP和MAC地址。对方因为没有走协议栈,对方就会不发arp协议给我,那么过段时间后,我的arp表就会把对方的IP和MAC地址信息删掉,我就没办法知道对方的IP和MAC地址,因此后面就无法发送upd包给到对方了。


没开启进程前,可以ping通进程所在的机器,过段时间后无法ping通。


2.原因:程序把网卡的数据发送到了共享内存,不经过协议栈。而ping协议的反馈是走ICMP协议的


因此,因为ping对方的时候,对方因为没有走协议栈,对如果对方处理网卡信息的时候,没有实现对ICMP协议的解析和回复,那么我ping对方就没办法收到对方的反馈。


解决方法:


1.ping命令只需要实现一下icmp包就行


2.怎么保存arp缓存呢


2.1 自己添加一下arp,设置成静态的,那么数据包就知道发到局域网的哪台主机了,


  因为路由器保存着局域网内的arp缓存表,arp缓存表的ip地址是局域网内部的私有ip


  地址,添加了之后,路由器就有一条arp缓存,当数据包到来的时候,路由器识别到


  的是公网ip地址,然后路由器收到数据包之后,根据arp缓存表,找到对应的mac地址


  和,先去发到mac层,判断是否符合数据包的mac地址,再发到网络层,判断ip是否


  是数据包的ip,是的话,就向传输层传输


 2.2 自己写arp包,收到arp请求的时候,填充我们的eth0的ip地址和mac地址,重新发送


 过去

相关文章
|
2月前
|
存储 网络协议 算法
UDP 协议和 TCP 协议
本文介绍了UDP和TCP协议的基本结构与特性。UDP协议具有简单的报文结构,包括报头和载荷,报头由源端口、目的端口、报文长度和校验和组成。UDP使用CRC校验和来检测传输错误。相比之下,TCP协议提供更可靠的传输服务,其结构复杂,包含序列号、确认序号和标志位等字段。TCP通过确认应答和超时重传来保证数据传输的可靠性,并采用三次握手建立连接,四次挥手断开连接,确保通信的稳定性和完整性。
88 1
UDP 协议和 TCP 协议
|
8天前
|
网络协议 SEO
TCP连接管理与UDP协议IP协议与ethernet协议
TCP、UDP、IP和Ethernet协议是网络通信的基石,各自负责不同的功能和层次。TCP通过三次握手和四次挥手实现可靠的连接管理,适用于需要数据完整性的场景;UDP提供不可靠的传输服务,适用于低延迟要求的实时通信;IP协议负责数据包的寻址和路由,是网络层的重要协议;Ethernet协议定义了局域网的数据帧传输方式,广泛应用于局域网设备之间的通信。理解这些协议的工作原理和应用场景,有助于设计和维护高效可靠的网络系统。
19 4
|
2月前
|
网络协议
UDP 协议
UDP 协议
119 58
|
1月前
|
网络协议 网络性能优化 C#
C# 一分钟浅谈:UDP 与 TCP 协议区别
【10月更文挑战第8天】在网络编程中,传输层协议的选择对应用程序的性能和可靠性至关重要。本文介绍了 TCP 和 UDP 两种常用协议的基础概念、区别及应用场景,并通过 C# 代码示例详细说明了如何处理常见的问题和易错点。TCP 适用于需要可靠传输和顺序保证的场景,而 UDP 适用于对延迟敏感且可以容忍一定数据丢失的实时应用。
28 1
|
1月前
|
网络协议 算法 数据格式
【TCP/IP】UDP协议数据格式和报文格式
【TCP/IP】UDP协议数据格式和报文格式
119 3
|
1月前
|
存储 网络协议 算法
更深层次理解传输层两协议【UDP | TCP】【UDP 缓冲区 | TCP 8种策略 | 三次握手四次挥手】
UDP和TCP各有所长,UDP以其低延迟、轻量级的特点适用于对实时性要求极高的应用,而TCP凭借其强大的错误检测、流量控制和拥塞控制机制,确保了数据的可靠传输,适用于文件传输、网页浏览等场景。理解它们的工作原理,特别是UDP的缓冲区管理和TCP的8种策略,对于优化网络应用的性能、确保数据的高效和可靠传输至关重要。开发者在选择传输层协议时,应根据实际需求权衡利弊,合理利用这两项关键技术。
63 5
|
1月前
|
JavaScript 安全 Java
谈谈UDP、HTTP、SSL、TLS协议在java中的实际应用
下面我将详细介绍UDP、HTTP、SSL、TLS协议及其工作原理,并提供Java代码示例(由于Deno是一个基于Node.js的运行时,Java代码无法直接在Deno中运行,但可以通过理解Java示例来类比Deno中的实现)。
65 1
|
2月前
|
监控 网络协议 网络性能优化
如何办理支持UDP协议的网络
在当今网络环境中,UDP(用户数据报协议)因传输速度快、延迟低而广泛应用于在线游戏、视频流媒体、VoIP等实时服务。本文详细介绍了办理支持UDP协议网络的方法,包括了解UDP应用场景、选择合适的ISP及网络套餐、购买支持UDP的设备并进行优化设置,以及解决常见问题的策略,帮助用户确保网络稳定性和速度满足实际需求。
|
2月前
|
网络协议
UDP协议在网络通信中的独特应用与优势
UDP(用户数据报协议)作为关键的传输层协议,在网络通信中展现出独特优势。本文探讨UDP的无连接性及低开销特性,使其在实时性要求高的场景如视频流、在线游戏中表现优异;其不保证可靠交付的特性赋予应用程序自定义传输策略的灵活性;面向报文的高效处理能力及短小的包头设计进一步提升了数据传输效率。总之,UDP适用于高速、实时性强且对可靠性要求不高的应用场景,为网络通信提供了多样化的选择。
|
2月前
|
网络协议 视频直播 C语言
C语言 网络编程(三)UDP 协议
UDP(用户数据报协议)是一种无需建立连接的通信协议,适用于高效率的数据传输,但不保证数据的可靠性。其特点是无连接、尽力交付且面向报文,具备较高的实时性。UDP广泛应用于视频会议、实时多媒体通信、直播及DNS查询等场景,并被许多即时通讯软件和服务(如MSN/QQ/Skype、流媒体、VoIP等)采用进行实时数据传输。UDP报文由首部和数据部分组成,首部包含源端口、目的端口、长度和校验和字段。相比TCP,UDP具有更高的传输效率和更低的资源消耗。