UDP协议详细讲解
UDP简介:
UDP是一种无连接的传输协议,他提供一种简单,不可靠的数据传输服务。因为该协议不面向连接,且不可靠,所以适用于一次传递少量数据,不适用于大量数据传输。
UDP属于网络协议的传输层,直接负责数据的传输和接收。
UDP协议特点:
一.无连接:
两台主机使用UDP进行通信的时候,不需要建立连接,只需要知道对端的IP和端口就可以把数据发送给对端主机。
二.不可靠
1.UDP协议没有向TCP那些机制,可以保证数据一定可以传输给对端,如果因为一些事故导致没有将消息成功发送给对端主机,比如说网络故障,或者对端的接收缓冲区已经打满,都可以导致消息发送失败,那丢失的这条消息只能是丢了,没有重传机制。
2.对端的接受顺序是不可靠的,不会因为你先发送,这条消息就先到,这个和网购很像,不会因为你先下单这个物件一定就是先到的,因为每个消息走的路线可能都是不一样的,锁已无法向TCP那样保证消息的有序性。
三.面向数据报
UDP在传输数据时,是以数据报的方式传递过去的,不是向TCP那样以字节流的方式传递过去的,二者的区别就是,面向数据报的话,应用层发送了多少次消息,那么就将每条消息都封装成数据报发送给对端,面向字节流则不然,而是把消息发送金发送端的发送缓冲区当中,等封装成报文的时候,则是能带多少数据由TCP决定,可能你上端发送了十次消息,每次10字节,那么这100字节都停留在发送缓冲区当中,可能TCP封装一次报文就将这些数据全部发送给对端,但是UDP的话一定要封装十次才可以。
UDP报文格式

UDP协议栈属于内核协议栈,而内核是用C语言写的,在底层UDP的报头是一个位段类型的结构体。
struct udp_header
{
//16位源端口号
unsigned int src_port:16;
//16位目的端口号
unsigned int des_port:16;
//16位UDP长度
unsigned int udp_len:16;
//16位UDP校验和
unsigned int udp_cheack:16;
}
UDP缓冲区
- UDP没有真正意义上的发送缓冲区。调用sendto会直接把数据交给内核,经过简单包装后,将内容传给网络协议层。
- UDP具有接收缓冲区。
- 这个接收缓冲区不能保证收到的·UDP报文和发送时得UDP报文顺序是一致的。因为它们走的路径不一样,最终可能导致到达终点的顺序不一样。
- 这个接收缓冲区,也无法保证收到的内容等于发送的内容,可能你这个发送缓冲区满了,就会丢弃新发送过来的报文,或中间网络出现问题,导致数据发送失败,都可能导致接收缓冲区接收数据失败。
基于UDP实现的用户层协议
- NFS:网络文件系统
- TFTP:简单文件传输协议
- DHCP:动态主机配置协议
- BOOTP:启动协议(用于无盘设备启动)
- DNS:域名解析协议
UDP报文各个字段
1.源端口号和目的端口号
1.1认识端口号
- IP:32位无符号整数,用来标识公网中唯一一台主机。
- 端口号:16位无符号整型,标识了主机中唯一一个进程。
在TCP/IP协议中,用源IP,目的IP,源端口号,目的端口号,协议号这样的五元组来标识一个通信。

补充知识:netstat命令介绍
语法:netstat [选项]
常用选项:
- n拒绝显示别名,能显示数字的全部以数字形式展现出来。
- l仅列出服务状态位Listen的进程。
- p显示网络连接 / 端口对应的 PID(进程 ID) 和 进程名称。
- t仅tcp相关选项。
- u仅udp相关选项。
- a显示所有选项,默认不显示Liten相关选项。
问1:端口号和进程的关系?
一个进程可以对应多个端口号。
一个端口号只能对应一个进程。
问2:为什么要这样?
因为这样可以通过端口号找到唯一的进程。
1.2端口号的范围划分
端口号一共有2^16 = 65536个,其中前面的端口号被分配给一些常用的协议。
- 知名端口号(0~1023):HTTP,FTP,SS等这些广为使用的应用层协议,它们的端口号一般都是固定的。
- 其它端口号(1024~65535):这个范围端口号是我们在使用时动态分配的
查看知名端口对应的服务的命令:
less/ etc/ services
1.3UDP中端口号的字段作用
- 源端口号:将自己端口号封装进去,用来告诉接收端,是哪个进程给它发送的信息,这样可以实现全双工通信。
- 目的端口号:可以通过该端口号确定将消息发送给哪个进程
问:UDP怎么实现报头和有效载荷分离的?
定长报头+自描述字段,16位UDP长度是总长度,而UDP报头长度是固定的8字节,所以总长度减去8字节剩余的就是数据大小。
2.UDP长度
UDP协议中包头中有一个内容为16位最大长度字段,该字段记录了UDP报文的总长度,也就是64K大小,在当今互联网时代,64K算得上是一个很小的数字。如果我们需要传递大于64K的数字需要在应用层手动分包,然后当接收端收到后手动合包。
3.UDP校验和
该校验和可以保证接收端接收到的数据一定是准确的,没有经过更改的,可以传输过来的顺序是不可靠的,传不传输成功也都是无所谓的,但是传输过来的内容一定是准确的,没有被更改的。因为报文在进行网络传输时,会经过各种传输线,路由器,信号放大器等传输介质,且随着网络状况不同,每个包的具体选择路径是不一样的。在网络传输中,受到外界的干扰,数据可能出错,比如某几位位置0变1,1变0,所以就需要在接收到数据之后需要校验一下,以保证接收到的数据的准确性。
UDP协议在网络传输中的使用案例
class Sockaddr_In
{
private:
struct sockaddr_in local;
socklen_t len;
public:
Sockaddr_In(int port)
{
local.sin_family = AF_INET;
//转换成大端字节序
local.sin_port(htons(port));
local.sin_addr_s_addr = INADDR_ANY;
}
struct sockaddr* Get_Sockaddr_In()
{
return &local;
}
socklen_t Get_Len()
{
return sizeof(local);
}
};
class Udp_Server
{
private:
int serverfd_;
int serverport_;
Sockaddr_In* local;
public:
Udp_Server(std::string serverport)
:serverip_(serverip),serverport_(atoi(srevrport))
{
Sockaddr_In local = new Sockaddr_In(sreverport_);
serverfd_ = socket(AF_INET,SOCK_STREAM,0);
int ret = bind(serverfd_,local.Get_Sockaddr_In(),local.Get_Len());
}
void Sendto(std::string content)
{
int n = sendto(serverfd_,content.c_str(),content.size(),local->Get_Sockaddr_In(),local->Get_Len());
if(n > 0)
//...发送成功
else
//发送失败
exit(1);
}
void RecvFrom(char* buffer,int sz)
{
int n = recvfrom(serverfd_,buffer,sz,local->Get_Sockaddr_In(),local->Get_Len());
if(n > 0)
//...读取成功
else
//读取失败
exit(1);
}
};
int main(int argc,char* argv)
{
if(argc < 2)
{
std::cout<<"man ip port"<<std::endl;
return 0;
}
Udp_Server server(argv[1]);
char buffer[1024];
srever.RecvFrom(buffer,sizeof(buffer)-1);
//将内容传递给上层,并进行解析
//把上层处理好的内容发送出去
std::string content;
srever.Sendto(content);
return 0;
}
更多推荐
所有评论(0)