UDP是一个简单的面向数据报的运输层协议:进程的每个输出操作都正好产生一个UDP数据报,并组装成一份待发送的IP数据报。这与面向流字符的协议不同,如TCP,应用程序产生的全体数据与真正发送的单个IP数据报可能没有什么联系。RFC768是UDP的正式规范
1、UDP报文封装
UDP首部报文格式如下
0 7 8 15 16 23 24 31
+—---—----+----------+-----------+----------+ ——
| Source Port | Destination Port | ↑
+-----------+----------+-----------+----------+ 8byte
| Length | Checksum | ↓
+-----------+----------+-----------+----------+ ——
| data octets ... |
+---------------------------------------------+
User Datagram Header Format
端口号表示发送进程和接收进程,UDP长度字段指的是UDP首部和UDP数据的字节长度。该字段的最小值为8字节(发送一份0字节的UDP数据报是OK的)。这个UDP长度是有冗余的。IP数据报长度指的是数据报全长,因此UDP数据报长度是全长减去IP首部的长度。
特点:不可靠、易分片
UDP不提供可靠性:它把应用程序传给IP层的数据发送出去,但是并不保证它们能到达目的地。
分片:应用程序必须关心IP数据报的长度。如果它超过网络的MTU,那么就要对IP数据报进行分片。如果需要,源端到目的端之间的每个网络都要进行分片,并不只是发送端主机连接第一个网络才这样做。
2、IP分片
物理网络层一般要限制每次发送数据帧的最大长度。任何时候IP层接收到一份要发送的IP数据报时,它要判断向本地哪个接口发送数据(选路),并查询该接口获得其MTU。IP把MTU与数据报长度进行比较,如果需要则进行分片。分片可以发生在原始发送端主机上,也可以发生在中间路由器上。把一份IP数据报分片以后,只有到达目的地才进行重新组装。
IP首部中部分字段用于报文分片组装,具体参见tcp/ip协议学习笔记(3)Internet Protocol(IP)
对于发送端发送的每份IP数据报来说,其标识字段都包含一个唯一值。该值在数据报分片时被复制到每个片中。标志字段用其中一个比特来表示“更多的片”。除了最后一片外,其他每个组成数据报的片都要把该比特置1,下面简单抓一个分片ping包看一下
第一片
第二片
在分片时,除最后一片外,其他每一片中的数据部分(除IP首部外的其余部分)必须是8字节的整数倍。
标志字段中有一个比特称作“不分片”位。如果将这一比特置1,IP将不对数据报进行分片。相反把数据报丢弃并发送一个ICMP差错报文给起始端
尽管IP分片过程看起来是透明的,但有一点让人不想使用它:即使只丢失一片数据也要重传整个数据报。为什么会发生这种情况呢?因为I P层本身没有超时重传的机制——由更高层来负责超时和重传(T C P有超时和重传机制,但U D P没有)
IP数据报:是指IP层端到端的传输单元(在分片之前和重新组装之后)
分组是指在IP层和链路层之间传送的数据单元。一个分组可以是一个完整的IP数据报,也可以是IP数据报的一个分片。
3、UDP数据报最大长度
理论上,IP数据报的最大长度是65535字节,这是由IP首部16比特总长度字段所限制的。去除20字节的IP首部和8个字节的UDP首部, UDP数据报中用户数据的最长长度为65507字节。但是,大多数实现所提供的长度比这个最大值小。其中两个限制因素:
第一,应用程序可能会受到其程序接口的限制。socket API提供了一个可供应用程序调用的函数,以设置接收和发送缓存的长度。对于UDP socket,这个长度与应用程序可以读写的最大U D P数据报的长度直接相关。现在的大部分系统都默认提供了可读写大于8192字节的UDP数据报
第二个限制来自于TCP/IP的内核实现。可能存在一些实现特性(或差错),使IP数据报长度小于65535字节
4、UDP服务器的设计与实现
/********************************************************************************* *Author : wph *Version : 1.0 *Date : 2014/03/01 *Description: udp server *Others : *History : **********************************************************************************/ #include<stdio.h> #include<string.h> #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<stdlib.h> #include<netinet/in.h> #include<arpa/inet.h> #include<event2/event.h> #include "errocode.h" #include "basetype.h" #define INVALID_FD -1 #define PORT 1234 #define MAXDATASIZE 512 STATIC INT g_iudpFd = INVALID_FD; VOID udp_callback(evutil_socket_t fd, short what, void *arg) { struct sockaddr_in client; socklen_t addrlen; int num; char buf[MAXDATASIZE]; memset(buf, 0, MAXDATASIZE); num = recvfrom(fd, buf, MAXDATASIZE, 0, (struct sockaddr*)&client, &addrlen); if (num < 0) { perror("recvfrom() error\n"); exit(1); } printf("You got a message (%s) from client.\nIt‘s ip is%s, port is %d.\n", buf, inet_ntoa(client.sin_addr), htons(client.sin_port)); sendto(fd, buf, num, 0, (struct sockaddr *)&client, addrlen); } ULONG udp_init(VOID) { int sockfd; struct sockaddr_in server; /* The caller has already set up fd1, fd2 somehow, and make them nonblocking. */ sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(INVALID_FD == sockfd) { perror("Creatingsocket failed."); exit(1); } bzero(&server, sizeof(server)); server.sin_family = AF_INET; server.sin_port= htons(PORT); server.sin_addr.s_addr= htonl(INADDR_ANY); if(-1 == bind(sockfd, (struct sockaddr *)&server, sizeof(server))) { perror("Bind()error."); exit(1); } g_iudpFd = sockfd; return EROOR_SUCCESS; } VOID udp_fini(VOID) { int sockfd = g_iudpFd; if (INVALID_FD != sockfd) { close(sockfd); } } VOID main_loop(VOID) { INT ifd = g_iudpFd; struct event *ev1; struct timeval five_seconds = {5,0}; struct event_base *base = event_base_new(); /* upd 服务器采用libevet进行处理 */ ev1 = event_new(base, ifd, EV_TIMEOUT|EV_READ|EV_PERSIST, udp_callback, NULL); event_add(ev1, &five_seconds); event_base_dispatch(base); return ; } INT main() { if(EROOR_SUCCESS != udp_init()) { return -1; } main_loop(); udp_fini(); return 0; }
/********************************************************************************* *Copyright(C),2010-2011, *Author : wph *Version : 1.0 *Date : 2014/03/01 *Description: udp client *Others : *History : **********************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include "errocode.h" #include "basetype.h" #define INVALID_FD -1 #define PORT 1234 #define MAXDATASIZE 512 INT main(INT argc, CHAR *argv[]) { INT isockfd = INVALID_FD; UINT uinum = 0; char buf[MAXDATASIZE]; socklen_t addrlen; struct hostent *he; struct sockaddr_in server; struct sockaddr_in peer; if (3 != argc) { printf("Usage: %s <IP Address><message>\n", argv[0]); exit(1); } if (NULL == (he=gethostbyname(argv[1]))) { printf("gethostbyname()error\n"); exit(1); } if (INVALID_FD == (isockfd = socket(AF_INET, SOCK_DGRAM,0))) { printf("socket() error\n"); exit(1); } bzero(&server,sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr= *((struct in_addr *)he->h_addr); sendto(isockfd, argv[2],strlen(argv[2]),0,(struct sockaddr *)&server,sizeof(server)); addrlen=sizeof(server); while (1) { if((uinum=recvfrom(isockfd, buf, MAXDATASIZE, 0, (struct sockaddr *)&peer, &addrlen))== -1) { printf("recvfrom() error\n"); exit(1); } if (addrlen != sizeof(server) || memcmp((const void *)&server, (const void *)&peer, addrlen) != 0) { printf("Receive message from otherserver.\n"); continue; } buf[uinum]=‘\0‘; printf("Server Message:%s\n", buf); break; } close(isockfd); }
演示:
[root@localhost server]# ./udpserver &
[1] 1389
[root@localhost server]# ./udpclient 127.0.0.1 "i like you"
You got a message (i like you) from client.
It‘s ip is127.0.0.1, port is 34340.
Server Message:i like you
[root@localhost server]# netstat -aun
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
udp 0 0 0.0.0.0:68 0.0.0.0:*
udp 0 0 0.0.0.0:111 0.0.0.0:*
udp 0 0 0.0.0.0:631 0.0.0.0:*
udp 0 0 0.0.0.0:123 0.0.0.0:*
udp 0 0 192.168.1.255:137 0.0.0.0:*
udp 0 0 192.168.1.105:137 0.0.0.0:*
udp 0 0 0.0.0.0:137 0.0.0.0:*
udp 0 0 192.168.1.255:138 0.0.0.0:*
udp 0 0 192.168.1.105:138 0.0.0.0:*
udp 0 0 0.0.0.0:138 0.0.0.0:*
udp 0 0 0.0.0.0:17585 0.0.0.0:*
udp 0 0 0.0.0.0:1234 0.0.0.0:*
udp 0 0 0.0.0.0:816 0.0.0.0:*
udp 0 0 0.0.0.0:323 0.0.0.0:*
udp6 0 0 :::111 :::*
udp6 0 0 :::123 :::*
udp6 0 0 :::816 :::*
udp6 0 0 :::323 :::*
udp6 0 0 :::45117 :::*
tcp/ip协议学习笔记(7)UDP用户数据报协议,布布扣,bubuko.com
原文:http://blog.csdn.net/wangpeihuixyz/article/details/20230109