學(xué)習(xí)筆記:UDP用戶數(shù)據(jù)報(bào)協(xié)議
UDP是一個(gè)簡單的面向數(shù)據(jù)報(bào)的運(yùn)輸層協(xié)議:進(jìn)程的每個(gè)輸出操作都正好產(chǎn)生一個(gè)UDP數(shù)據(jù)報(bào),并組裝成一份待發(fā)送的IP數(shù)據(jù)報(bào)。這與面向流字符的協(xié)議不同,如TCP,應(yīng)用程序產(chǎn)生的全體數(shù)據(jù)與真正發(fā)送的單個(gè)IP數(shù)據(jù)報(bào)可能沒有什么聯(lián)系。RFC768是UDP的正式規(guī)范
1、UDP報(bào)文封裝
UDP首部報(bào)文格式如下
端口號(hào)表示發(fā)送進(jìn)程和接收進(jìn)程,UDP長度字段指的是UDP首部和UDP數(shù)據(jù)的字節(jié)長度。該字段的最小值為8字節(jié)(發(fā)送一份0字節(jié)的UDP數(shù)據(jù)報(bào)是OK的)。這個(gè)UDP長度是有冗余的。IP數(shù)據(jù)報(bào)長度指的是數(shù)據(jù)報(bào)全長,因此UDP數(shù)據(jù)報(bào)長度是全長減去IP首部的長度。
特點(diǎn):不可靠、易分片
UDP不提供可靠性:它把應(yīng)用程序傳給IP層的數(shù)據(jù)發(fā)送出去,但是并不保證它們能到達(dá)目的地。
分片:應(yīng)用程序必須關(guān)心IP數(shù)據(jù)報(bào)的長度。如果它超過網(wǎng)絡(luò)的MTU,那么就要對(duì)IP數(shù)據(jù)報(bào)進(jìn)行分片。如果需要,源端到目的端之間的每個(gè)網(wǎng)絡(luò)都要進(jìn)行分片,并不只是發(fā)送端主機(jī)連接***個(gè)網(wǎng)絡(luò)才這樣做。
2、IP分片
物理網(wǎng)絡(luò)層一般要限制每次發(fā)送數(shù)據(jù)幀的***長度。任何時(shí)候IP層接收到一份要發(fā)送的IP數(shù)據(jù)報(bào)時(shí),它要判斷向本地哪個(gè)接口發(fā)送數(shù)據(jù)(選路),并查詢該接口獲得其MTU。IP把MTU與數(shù)據(jù)報(bào)長度進(jìn)行比較,如果需要?jiǎng)t進(jìn)行分片。分片可以發(fā)生在原始發(fā)送端主機(jī)上,也可以發(fā)生在中間路由器上。把一份IP數(shù)據(jù)報(bào)分片以后,只有到達(dá)目的地才進(jìn)行重新組裝。
IP首部中部分字段用于報(bào)文分片組裝,具體參見tcp/ip協(xié)議學(xué)習(xí)筆記(3)Internet Protocol(IP)
對(duì)于發(fā)送端發(fā)送的每份IP數(shù)據(jù)報(bào)來說,其標(biāo)識(shí)字段都包含一個(gè)唯一值。該值在數(shù)據(jù)報(bào)分片時(shí)被復(fù)制到每個(gè)片中。標(biāo)志字段用其中一個(gè)比特來表示“更多的片”。除了***一片外,其他每個(gè)組成數(shù)據(jù)報(bào)的片都要把該比特置1,下面簡單抓一個(gè)分片ping包看一下
***片
第二片
在分片時(shí),除***一片外,其他每一片中的數(shù)據(jù)部分(除IP首部外的其余部分)必須是8字節(jié)的整數(shù)倍。
標(biāo)志字段中有一個(gè)比特稱作“不分片”位。如果將這一比特置1,IP將不對(duì)數(shù)據(jù)報(bào)進(jìn)行分片。相反把數(shù)據(jù)報(bào)丟棄并發(fā)送一個(gè)ICMP差錯(cuò)報(bào)文給起始端
盡管IP分片過程看起來是透明的,但有一點(diǎn)讓人不想使用它:即使只丟失一片數(shù)據(jù)也要重傳整個(gè)數(shù)據(jù)報(bào)。為什么會(huì)發(fā)生這種情況呢?因?yàn)镮 P層本身沒有超時(shí)重傳的機(jī)制——由更高層來負(fù)責(zé)超時(shí)和重傳(T C P有超時(shí)和重傳機(jī)制,但U D P沒有)
IP數(shù)據(jù)報(bào):是指IP層端到端的傳輸單元(在分片之前和重新組裝之后)
分組是指在IP層和鏈路層之間傳送的數(shù)據(jù)單元。一個(gè)分組可以是一個(gè)完整的IP數(shù)據(jù)報(bào),也可以是IP數(shù)據(jù)報(bào)的一個(gè)分片。
3、UDP數(shù)據(jù)報(bào)***長度
理論上,IP數(shù)據(jù)報(bào)的***長度是65535字節(jié),這是由IP首部16比特總長度字段所限制的。去除20字節(jié)的IP首部和8個(gè)字節(jié)的UDP首部, UDP數(shù)據(jù)報(bào)中用戶數(shù)據(jù)的最長長度為65507字節(jié)。但是,大多數(shù)實(shí)現(xiàn)所提供的長度比這個(gè)***值小。其中兩個(gè)限制因素:
***,應(yīng)用程序可能會(huì)受到其程序接口的限制。socket API提供了一個(gè)可供應(yīng)用程序調(diào)用的函數(shù),以設(shè)置接收和發(fā)送緩存的長度。對(duì)于UDP socket,這個(gè)長度與應(yīng)用程序可以讀寫的***U D P數(shù)據(jù)報(bào)的長度直接相關(guān)。現(xiàn)在的大部分系統(tǒng)都默認(rèn)提供了可讀寫大于8192字節(jié)的UDP數(shù)據(jù)報(bào)
第二個(gè)限制來自于TCP/IP的內(nèi)核實(shí)現(xiàn)。可能存在一些實(shí)現(xiàn)特性(或差錯(cuò)),使IP數(shù)據(jù)報(bào)長度小于65535字節(jié)#p#
4、UDP服務(wù)器的設(shè)計(jì)與實(shí)現(xiàn)
- /*********************************************************************************
- *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 服務(wù)器采用libevet進(jìn)行處理 */
- 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)





























