一、流式服务和数据报服务
1、字节流服务:发送端send()只是将数据写到TCP发送缓冲区中,然后将发送缓冲区中的数据打包成报文段发送出去。接收端又将接收到的报文段写到缓冲区中,最后recv()直接取数据。
字节流服务特点:数据没有明确分割(由底层做分割),不分一定的报文段,什么时候想发便可将写入缓冲区的数据,进行打包再发送,即send()与recv()的次数不完全对等,没有必然联系。
2、数据报服务:发送端sendto()将数据直接打包成相对应的报文段发送。
数据报服务特点:数据有明确分割,拿数据按报文段拿。发几次接收端收几次。
上一节《TCP》中介绍了TCP报头,再来看一下UDP报头。
源端口号和目的端口号是必须要有的,接下来是16位数据包长度,指UDP报头和数据总长度。这一点和TCP是不同的。16位校验和检验UDP报头和数据的准确性,和TCP不同的是,UDP报头中校验和不是必须有的。
二、UDP编程步骤:
服务器:
1、 socket
2、 bind
3、 recvfrom/sendto
4、 close
客户端:
1、 socket
2、 sendto/recvfrom
3、 close
//客户端
void main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
assert(sockfd != -1);
struct sockaddr_in ser,cli;
ser.sin_family = AF_INET;
ser.sin_port = htons(6000);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
sendto(sockfd,"Hello World",strlen("Hello World"),0,(struct sockaddr*)&ser,sizeof(ser));
char buff[128] = {0};
recvfrom(sockfd,buff,127,0,NULL,NULL);
printf("recv::%s\n",buff);
close(sockfd);
}
//服务器
void main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
assert(sockfd != -1);
struct sockaddr_in ser,cli;
ser.sin_family = AF_INET;
ser.sin_port = htons(6000);
ser.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
assert(res != -1);
while(1)
{
char buff[128] = {0};
int len_cli = sizeof(cli);
recvfrom(sockfd,buff,127,0,(struct sockaddr*)&cli,&len_cli);
printf("IP:%s\n,D:%d\n,recv::%s\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port),buff);
sendto(sockfd,"ok",strlen("ok"),0,(struct sockaddr*)&cli,len_cli);
}
close(sockfd);
}
结果如下:
再详细介绍一下和TCP不同的两个函数:
ssize_t recvfrom( int sockfd, //通过哪个socket进行通信
void *buf, //缓冲区
size_t len, //缓冲区大小
int flags,//暂时设0
struct sockaddr *src_addr, //把数据发送给谁
socklen_t *addrlen );//地址结构体长度
ssize_t sendto( int sockfd,
const void *buf,
size_t len,
int flags,
const struct sockaddr *dest_addr,
socklen_t addrlen );
三、IP
介绍完传输层的两个协议,看看网络层的IP协议。IP协议为上层提供无状态、无连接、不可靠的服务。
无状态指IP通信双方不同步传输数据的状态信息,因此所有IP数据报的发送、传输和接收都是独相互立的、没有上下文关系的。最大的缺点就是无法处理乱序和重复的IP数据报。面向连接的协议,如TCP能够自己处理乱序的、重复的报文段,它交给上层的一定是有序的、正确的。无状态服务的优点也很明显:简单、高效。UDP协议和HTTP协议都是无状态协议。
其IPv4头部结构如下:
4为版本号:指定IP协议的版本,对IPv4来说是4.
4位头部长度:标识IP头部有多少个32位,这一点和TCP是一样的,最长60字节。
8位服务类型:包括一个3位的优先权字段(现已被忽略)、4位的TOS字段和一位保留字段(必须置0)。4位TOS字段分别表示:最小延时(例如直播)、最大吞吐量(例如FTP)、最高可靠性(例如银行系统)、最小费用(省流量)。这4位最多一个置1,也就是说是互斥的。
16位总长度:指整个IP数据报的长度(字节)。
16位标识:唯一地标识主机发送的每一个数据报。其初始值由系统随机生成:每发一个数据报,其值加1。TCP中的32位序号也是初始值随机生成,随后其值加1,但是TCP中的序号是排序用的,而IP中的16位标识是分片用的,同一个数据报中的所有分片都具有相同的标识符。
3位标志:第一位保留置0。第二位表示“禁止分片”。第三位表示“更多分片”,除最后一个分片外,其它都置1。
13位片偏移:分片相对原始IP数据报开始处(仅指数据部分)的偏移。实际的偏移值左移3位(乘8)得到。
8位生存时间(TTL):数据报到达目的地之前允许经过的路由器跳数。TTl被发送端设置(常见64),转发过程中每经过一个路由器其值减1。当TTL值减为0时,路由器将丢弃该数据报,并向源端发送一个ICMP差错报文。该值可防止数据报陷入路由循环。
8位协议:区分上层协议。ICMP是1,TCP是6,UDP是17。
16位头部校验和:由发送端填充,仅检验IP数据报头部。(IP数据报头部没错就可以正确转发,不检验数据,不可靠。)
32位源端IP地址和目的端IP地址:标识数据报的发送端和接收端。
再详细说一下IP分片。当IP数据报的长度超过帧的MTU(网络最大传输单元)时,它将被分片传输。分片可能发生在发送端也可能发生在中转路由上,但是只有在最终的目标机器上才会被内核的IP模块重新组装。IP数据报头部的三个字段给分片提供了足够的信息:数据报标识,标志,片偏移。下面是一个分片示意图