MTU为最大传输单元,它的含义实际上就是数据链路对IP包长度要求的最大值,以太网不得超过1500。MSS表示最大段的最大值,它是与TCP相关的一个概念,TCP的传输层是分段的,数据链路层期望的最大IP包长是1500字节,那么除去IP包头典型的20字节,再除去TCP包头典型的20字节(或者其他<=60字节的数据),可知,数据链路允许的最大TCP业务长度是1460字节。所以最大段(MSS)是1460字节。一帧不能超过1518字节,因为帧头占18字节,帧头包括:目的MAC(6)+源MAC(6)+Type(2)+CRC(4)。上述都是针对以太网而言。我下面测试用的是回环地址,它们以太网对应的MTU不一样。
回环地址对应的MTU:
MTU=65536,则MSS=65536-20-20=65492
此时回环地址的MSS=65528,当协议为TCP时,一次性发送的数据大于65528,TCP就会对数据进行分段发送。
服务端:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#define MAXLINE 4096
int main()
{
int listenfd,acceptfd,n;
socklen_t clilen;
char recvbuf[80000]={0};
struct sockaddr_in cliaddr,servaddr;
listenfd=socket(AF_INET,SOCK_STREAM,0);
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(8888);
servaddr.sin_addr.s_addr = INADDR_ANY;
bind(listenfd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr_in));
listen(listenfd,5);
clilen=sizeof(cliaddr);
acceptfd=accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);
recv(acceptfd,recvbuf,sizeof(recvbuf)-1,0);
printf("recvbuf=%s\n",recvbuf);
getchar();
close(acceptfd);
close(listenfd);
return 0;
}
客户端:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netinet/tcp.h>
#define MAXLINE 4096
int main()
{
int sockfd;
struct sockaddr_in servaddr;
char sendbuf[60000]={0};
sockfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(8888);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
write(sockfd,sendbuf,sizeof(sendbuf)+1);
getchar();
getchar();
close(sockfd);
return 0;
}
先启动服务端,再启动客户端,用tcpdump抓包
20:13:21.751121 IP 127.0.0.1.48148 > 127.0.0.1.ddi-tcp-1: Flags [S], seq 989177476, win 65495, options [mss 65495,sackOK,TS val 3584598474 ecr 0,nop,wscale 7], length 0
20:13:21.751136 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.48148: Flags [S.], seq 3566618469, ack 989177477, win 65483, options [mss 65495,sackOK,TS val 3584598474 ecr 3584598474,nop,wscale 7], length 0
20:13:21.751149 IP 127.0.0.1.48148 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 1, win 512, options [nop,nop,TS val 3584598474 ecr 3584598474], length 0
20:13:21.751210 IP 127.0.0.1.48148 > 127.0.0.1.ddi-tcp-1: Flags [.], seq 1:32742, ack 1, win 512, options [nop,nop,TS val 3584598474 ecr 3584598474], length 32741
20:13:21.751238 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.48148: Flags [.], ack 32742, win 1024, options [nop,nop,TS val 3584598474 ecr 3584598474], length 0
20:13:21.751250 IP 127.0.0.1.48148 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 32742:60002, ack 1, win 512, options [nop,nop,TS val 3584598474 ecr 3584598474], length 27260
20:13:21.790695 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.48148: Flags [.], ack 60002, win 862, options [nop,nop,TS val 3584598514 ecr 3584598474], length 0
6000个字节被分成了两个包,32741和27260两个。但是MSS>60000,为什么会被分段发送呢?这只是实现不同而已,在MSS的限制下,协议栈和系统具体进行怎样的限制,并无错误。下面我们将客户端改一下,将发送的6000字节改成32740,在抓包。
20:30:37.187347 IP 127.0.0.1.48156 > 127.0.0.1.ddi-tcp-1: Flags [S], seq 1517440416, win 65495, options [mss 65495,sackOK,TS val 3585633910 ecr 0,nop,wscale 7], length 0
20:30:37.187364 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.48156: Flags [S.], seq 3842770501, ack 1517440417, win 65483, options [mss 65495,sackOK,TS val 3585633910 ecr 3585633910,nop,wscale 7], length 0
20:30:37.187381 IP 127.0.0.1.48156 > 127.0.0.1.ddi-tcp-1: Flags [.], ack 1, win 512, options [nop,nop,TS val 3585633910 ecr 3585633910], length 0
20:30:37.187464 IP 127.0.0.1.48156 > 127.0.0.1.ddi-tcp-1: Flags [P.], seq 1:32742, ack 1, win 512, options [nop,nop,TS val 3585633910 ecr 3585633910], length 32741
20:30:37.187510 IP 127.0.0.1.ddi-tcp-1 > 127.0.0.1.48156: Flags [.], ack 32742, win 1024, options [nop,nop,TS val 3585633910 ecr 3585633910], length 0
看,没有分段了。
上面说的TCP,对于UDP,它是没有MSS这个概念的。但是它又最大发包大小,如果UDP发送的数据超过这个最大值,那么数据就会在IP层进行分片。IP包头有个16bit的长度,对于二进制最大值为2^16-1。除去IP包头20字节,UDP包头8字节,为65507。和TCP对比,TCP的MSS为1460,小于65507合理。我们说TCP没有最大发包大小,为啥呢?因为TCP会在传输层分段。对于UDP而言,发送超过65507字节的数据,就会发送失败。
参考地址:http://blog.csdn.net/stpeace/article/details/74152332