UDP的首部格式
- 源端口号(Source Port)
表示发送端端口号,字段长16位.该字段是可选项,有时可能不会设置源端口号,没有端口号的时候该字段的只设置为0,可用于不需要返回的通信中
- 目标端口号(Distination Port)
表示接收端端口号,字段长度为16位
- 包长度
该字段保存了UDP首部的长度跟数据的长度之和,单位为字节 - 校验和
校验和是为了提供可靠的UDP首部和数据而设计
- 其中用户数据报UDP有两个字段:数据字段和首部字段
- 首部字段有8个字节,由4个字段组成,每个字段都是2个字节
- 注意
在计算校验和时,临时把 “伪首部” 和UDP用户数据报连接在一起,伪首部仅仅时为了计算校验和
- 那么校验和计算中计算UDP伪首部的原因是什么呢?
TCP/IP中识别一个进行通信的应用需要5大元素,他们分别是源IP地址、目标IP地址、源端口、目标端口、协议号。然而在UDP首部中只包含了他们当中的两项(源端口和目标端口),余下的3项都包含在IP首部中,假如其他三项遭到破坏的话,可能会导致对端收不到正确的包.因此,在了校验和的计算中引入了伪首部的概念,用来提供可靠的通信传输
UDP的主要特点
-
1.UDP是无连接的,发送数据之前不需要建立连接,因此减少了开销和发送数据之前的时延
-
2.UDP使用尽最大努力交付,既不保证可靠交付,因此主机不需要维护复杂的连接状态表
-
3.UDP是面向报文的,UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界.UDP一次交付一个完整的报文
-
4.UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低,这对某些实时应用很重要的,很是个多媒体通信的要求
-
5.UDP支持一对一,一对多,多对一和多对多的交互通信
-
6.UDP的首部开销小,只有8个字节,比TCP的20个自己的首部要短
-
UDP的传输的大致过程(简略图,只是为了叙述方便)
-
用户A给用户B发送数据的时候,UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界.UDP一次交付一个完整的报文给网络层
-
而在接收端的用户B在传输层拿到UDP报文之后,去掉UDP首部字段后原封不动的将UDP用户数据报的数据部分交付给应用层一次交付一个完成的报文
UDP应用场景
- 包总量较少的通信(DNS\SNMP)
- 视频、音频等多媒体通信(即时通信)
- 限定于LAN等特定网络中的应用通信
- 广播通信(广播、多播)
UDP编程(回显服务器的实现)
UDP版本的服务器的编写
- 缩影版
socket();
bind();
while(true){
recvfrom();
sendto();
}
close();
- 整体实现版本
#include<cstdio>
#include<cstring>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main(){
int sock=socket(AF_INET,SOCK_DGRAM,0);
if(sock<0){
perror("socket!");
return 1;
}
//准备工作
sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(9090);
addr.sin_addr.s_addr=inet_addr("0.0.0.0");
int ret=bind(sock,(sockaddr*)& addr,sizeof(addr));
if(ret<0){
perror("bind!");
return 1;
}
printf("server is OK!\n");
while(true){
sockaddr_in peer;
char buf[1024]={0};
socklen_t len=sizeof(peer);
ssize_t n=recvfrom(sock,buf,sizeof(buf)-1,0,(sockaddr*)& peer,&len);
if(n<0){
perror("recvfrom!");
continue;
}
buf[n]='\0';
//根据请求计算响应,此处回显服务器
//返回计算结果
n=sendto(sock,buf,strlen(buf),0,(sockaddr*)&peer,len);
if(n<0){
perror("sendto!");
continue;
}
printf("[%s:%d] res:%s\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port),buf);
}
close(sock);
return 0;
}
UDP版本的客户端的实现
- 缩影版
sock();
while(true){
sendto();
recvfrom();
}
- 整体实现版本
#include<cstdio>
#include<cstring>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main(int argc,char* argv[]){
int sock=socket(AF_INET,SOCK_DGRAM,0);
if(sock<0){
perror("socket!");
return 1;
}
//客户端不需要绑定指定的固定的端口号
//用来和服务器进行交流,系统会自动分配
sockaddr_in server_addr;
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=inet_addr(argv[1]);
server_addr.sin_port=htons(9090);
while(true){
char buf[1024]={0};
printf("请输入内容:");
scanf("%s",buf);
fflush(stdout);
sendto(sock,buf,strlen(buf),0,(sockaddr*)& server_addr,sizeof(server_addr));
char server_put[1024]={0};
recvfrom(sock,server_put,strlen(server_put)-1,0,NULL,NULL);
printf("server resp:%s\n",server_put);
}
return 0;
}
- 交互结果