Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1330969
  • 博文数量: 284
  • 博客积分: 3251
  • 博客等级: 中校
  • 技术积分: 3046
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-26 17:23
文章分类

全部博文(284)

文章存档

2019年(2)

2018年(5)

2015年(19)

2014年(13)

2013年(10)

2012年(235)

分类: LINUX

2012-12-21 16:53:29

前面我们看到,建立一个TCP连接需要三次握手(SYN, SYN+ACK, ACK)。而终止一个连接要经过4次握手,下面我们会看到,严格来讲,是两个两次握手,即A端告知B端它终止从A端到B端的连接,即A端不会再往B端发送 数据了(通过向B端发送一个FIN标志)。A端的关闭即告完成,此时,我们说A到B的这条TCP连接处于半关闭状态(half-close)。
    但这时,B端还是可以向A端发送数据的,B端可以在将来的任一时间内向A端发送FIN来完成它这端的半关闭。此时,A端的socket可能已经不存在(超 时删除),但A主机的TCP/IP协议栈中有一个tcp control socket会代为完成一个ACK动作,完成第二个两次握手,从而彻底断开这条TCP连接。
    但在实际应用中,通常不可能一端执行半关闭后,另一端还继续发送数据,过一段时间后才完全关闭。通常是一端执行主动关闭后,另一方马上执行被动关闭。
    为了清晰起见,我们建立一个实验环境.172.16.48.13(以下简称13)上运行一个TCP客户端进程,172.16.48.1(以下简称1)上运 行一个TCP的服务端进程,在端口5002上进行侦听(关于侦听,以后会介绍)。13连向1的5002端口,发送一个TCP数据报后,进程退出,执行主动 关闭,向1发送一个FIN,1进行应答,连接进入半关闭状态,但1的进程不关闭socket,直到人为关闭进程时,才向13发送FIN,13应答,TCP 连接完全关闭。
    下面我们详细分析这整个流程,下面是13上的客户端进程的源代码:
    #include
    #include
    #include
    #include
    #include "my_inet.h"
    #include

    int main(void)
    {
        int fd, n, r;
        struct sockaddr_in srv, client;

        srv.sin_family = MY_AF_INET;
        srv.sin_port = htons(5002);
        inet_aton("172.16.48.1", &srv.sin_addr);

        client.sin_family = MY_AF_INET;
        client.sin_port = 0;
        client.sin_addr.s_addr = inet_addr("172.16.48.13");

        if( (fd = socket( MY_AF_INET, SOCK_STREAM, MY_IPPROTO_TCP) ) < 0 ){
            perror("socket");
            return -1;
        }

        if( bind(fd, (struct sockaddr *)&client, sizeof(client) ) < 0 ){
            perror("bind: ");
            return -1;
        }
        if( connect(fd, (struct sockaddr *)&srv, sizeof(srv)) < 0 ){
            perror("connect: ");
            return -1;
        }

        char send_buf[1024];
        strcpy( send_buf, "my first tcp packet!/n" );
        if( send(fd, send_buf, strlen(send_buf), 0 ) < 0 ){
            perror("send: ");
            return -1;
        }

        close(fd);
    }
    这里,客户端要执行一个地址绑定动作,因为13地址是本机eth0接口的一个从属地址,如果不执行绑定,系统会自动选择主地址172.16.48.2作为 发送地址,这样会跟内核中的TCP/IP协议栈造成冲突,而172.16.48.13地址对内核中的协议栈是不可见的(它由myinet模块专门设置)。
    我们在send系统调用后,直接执行了close,然后进程就结束了。在进程结束前,close系统调用会调用到内核函数myinet_release,然后到具体协议中的close函数,对TCP协议来说,就是mytcp_close。
    此时,socket处于TCP_ESTABLISHED状态,如果接收队列中还存在数据(struct sk_buff队列),则先清空数据,并进行相应的处理(我们现在假设一种最简单的状态,所以不会有数据)。将自己的状态标为 TCP_FIN_WAIT1,表示进入关闭的第一个状态,即等待对端回应自己的FIN数据报,同时向1发送一个FIN数据报,告知发送任务已完成,进入关 闭状态。下面是13向1发送的第一个FIN数据报内容(已剥去数据链路层的以太网首部和网络层的IP首部)
        数据内容                        含义
基本TCP首部
        80 26                           16位源端口号(32806)
        13 8a                           16位目的端口号(5002)
        00 00 07 d2                     32位序号
        7e 08 ba a0                     32位确认序号
        8                               首部长度(8*4=32)
        0 11                            标志位,ACK=1, FIN=1
        05 b4                           16位窗口大小(1460)
        9e 1c                           16位校验和
        00 00                           16位紧急指针
TCP选项
        01                              填充
        01                              填充
        08 0a 00 00 7e db 00 85 c7 30   时间戳
    从数据内容来看,这不单单是一个FIN,同时,它也承担了应答1的上一个TCP数据报的功能(ACK)。本次连接过程中,172.16.48.1的 socket随机生成的初始序号(SIN)为0x7E08BA9F,通讯过程中,SYN占去一位(三次握手协义建立连接的时候),那此时,13的ACK序 号应该是0x7E08BAA0,即希望下次接收到来自1的数据报的第一个序号。
    发完数据报,在函数mytcp_close中,socket进入一个超时等待队列,等待socket完全关闭。如果未设置超时时间,则直接关闭socket。
    172.16.48.1收到这个FIN数据,进行响应:
        数据内容                        含义
基本TCP首部
        13 8a                           16位源端口(5002)
        80 26                           16位目的端口(32806)
        7e 08 ba a0                     32位序号
        00 00 07 d3                     32位确认序号(因为FIN占一位)
        8                               首部长度
        0 10                            标志位, ACK=1
        05 a8                           16位窗口大小(1448)
        9d ff                           16位校验和
        00 00                           16位紧急指针
TCP选项
        01                              填充
        01                              填充
        08 0a 00 85 c7 59 00 00 7e db   时间戳
    13收到这个响应后,会进行一系例处理,从而完成一个半关闭,这个容后续再介绍。
阅读(1840) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~