《TCP/IP详解卷1:协议》笔记--2MSL等待状态

TCP的状态变迁图如下:


其中的TIME_WAIT状态也成为2MSL等待状态。每个具体的TCP实现必须选择一个报文段最大生存时间MSL(maximum

segment lifetime)。它是任何报文段被丢弃前在网络内的最长时间 。我们知道这个时间是有限的,因为TCP报文段以IP

数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段。

在RFC793指出MSL为2分钟,然而,实现中的常用值是30秒,1分钟或2分钟。

对一个具体实现锁定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该链接必须在TIME_WAIT

状态停留的时间为2倍的MSL。这样可以让TCP再次发送最后的ACK以防止这个ACK丢失。(另一端超时并重发最后的FIN)。

这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的socket(客户的IP地址和端口号,服务器

的IP地址和端口号不能被使用)。这个连接只能在2MSL结束后才能被使用。

遗憾的是,大多数TCP实现强加了更为严格的限制,在2MSL等待期间,插口中使用的本地端口在默认情况下不能再被使用。

注意:

1.一个socket对在它处理2MSL等待时,将不能再被使用。尽管许多具体的实现中允许一个进程重新使用仍处于2MSL等待的

端口(通常是设置选项SO_REUSEADDR),但TCP不能允许一个新的连接建立在相同的socket对上。

2.一个端口可同时绑定在UDP和TCP服务上,但是如果一个端口要同时绑定在两个TCP或者UDP服务上,则会出现端口已占用

的错误。因为TCP和UDP的端口在逻辑上是分离的。


实践:

server.c

#include<stdio.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>

#define MAX_SIZE 256

int main(void){
        int listenfd,connfd;
        struct sockaddr_in servaddr,clientaddr;
        int n,addrlen = sizeof(struct sockaddr_in);
        char addr_p[16];
        char rbuf[MAX_SIZE];
        char sbuf[MAX_SIZE] = "i am server.";

        if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){
                perror("inet_pton");
                return -1;
        }
        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5555);

        if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
                perror("listen");
                return -1;
        }

        if(bind(listenfd, (struct sockaddr*)&servaddr,sizeof(servaddr)) == -1){ 
                perror("bind");
                return -1;
        }

        if(listen(listenfd, 10) == -1){
                perror("listen");
                return -1;
        }

        while(1){
                if((connfd = accept(listenfd, (struct sockaddr*)&clientaddr,(socklen_t*)&addrlen)) == -1){
                        perror("accept");
                        continue;
                }
                break;
        }

        if(inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr,addr_p,(socklen_t)sizeof(addr_p)) == NULL){
                perror("inet_ntop");
                return -1;
        }
        printf("connect client ip:%s\n",addr_p);

        close(connfd);
        close(listenfd);
        return 0;
}

client.c

#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>

#define MAX_SIZE 256

int main(void){
        int sockfd,n;
        char rbuf[MAX_SIZE],sbuf[MAX_SIZE]="i am client.";
        struct sockaddr_in servaddr;

        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5555);
        if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){
                perror("inet_pton");
                return -1;
        }

        if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
                perror("socket");
                return -1;
        }

        if(connect(sockfd,(struct sockaddr*)&servaddr,(socklen_t)sizeof(servaddr))<0){
                perror("connect");
                return -1;
        }

        if((n =recv(sockfd,rbuf,MAX_SIZE,0)) < 0){
                perror("send");
                return -1;
        }
        rbuf[n]='\0';
        printf("%s\n",rbuf);
        close(sockfd);

        return 0;
}
运行结果:

先一台机器上先运行server,然后在另一台上运行client。

server:

root@virtual-machine:~# ./a.out
connect client ip:192.168.18.25
root@virtual-machine:~# ./a.out
bind: Address already in use

root@virtual-machine:~# ./a.out
^C


client:

yan@vm:~$ ./a.out


client连接到server后,一直阻塞读,但是此时server主动关闭了连接,server进入了2MSL等待状态,此时再运行server,提示

地址正在使用,等待了1分钟后再次运行,提示消失。说明已经不在2MSL等待状态。

展开阅读全文

没有更多推荐了,返回首页