导航

TCP

Posted on 2018-11-05 16:40  困或  阅读(243)  评论(0编辑  收藏  举报

1.TCP协议分析流程

  [1]要分析整个TCP协议,需要从外向里去分析,首先就是分析af_inet.c,这个里面就是inet的接口函数,例如bind()、listen()等等,首先就是弄清楚这些函数(函数的参数)是要干什么,不过在这之前先要分析最基本的socket接口和AF、PF。

  [2]之后继续分析TCP的握手流程。

  [3]之后继续分析TCP的数据传输流程。

  [4]之后继续分析TCP的断链流程。

  [5]之后继续分析TCP的拥塞控制流程。

  [6]之后继续分析TCP的其他分支流程。

2.AF_INET、PF_INET:

  AF表示address families 地址簇,表示某个地址属于哪个簇,例如结构体struct sockaddr_in addr,里面的sin_family就是AF_INET,表示变量addr的地址簇是AF_INET。sockaddr_in的in就是表示是inet类型的sockaddr。

  PF表示Protocol families 协议簇,协议栈处理的协议,例如创建socket使用参数PF_INET:

    socket(int family, int type, int protocol) 注意family是PF_INET表示该socket接收INET协议的报文(ipv4协议),protocol如果是IPPROTO_UDP则表示处理协议簇PF_INET上面的UDP协议。

  另外,struct sockaddr_in addr,从这个结构体就标识了addr这个地址是AF_INET类型,并且struct sockaddr_in的family固定是AF_INET,为什么里面还要有family字段?

  例如调用bind()时,里面传入的当然是sockaddr类型,而之后调用inet_bind()时:

    struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;

  然后判断addr->sin_family是不是AF_INET,其实也可以判断uaddr的sa_family,因此struct sockaddr_in也包括了family字段应该只是为了和通用结构体强转。

  总结:AF的宏其实就是表示了某个地址的类型,例如IPV4地址或IPV6的地址格式不一样,当然需要使用一个字段开标识,就是family字段。

     PF的宏其实就是表示了某个协议的类型,例如需要接收IPV4的报文,则要传入PF_INET参数

     而因为一个协议有其特定的地址格式,是一一对应关系,所以两个宏设置的值是一样的,可以互换使用。

3.socket 接口函数

  socket接口函数包括socket()、bind()、connect()、listen()、send()等函数,之后会继续调用具体协议的注册函数,例如如果是family是PF_INET,则socket()会继续调用inet的inet_create()函数。

  [1]socket(int family, int type, int protocol) 

    socket函数根据family找到对应的协议簇ops,对于PF_INET就是inet_family_ops,因此调用到inet的函数inet_create(struct net *net, struct socket *sock, int protocol,int kern),这个protocol参数,就是PF_INET里面支持的协议(例如TCP)。

    总之就是根据family字段找到该socket属于哪个PF(例如是PF_INET),之后再调用这个PF的创建函数,而protocol字段是这个PF里面的协议。

    最后就是这个type字段,type字段标识了这个socket本身的类型,例如inet某些函数在处理时不区分具体协议(tcp、udp等),而是根据socket的类型做一些处理。

    总结:socket()函数就是创建一个套接字进行通信,最简单的就是protocol参数,因为这个family内部的协议。 之后就是family参数,family表示这个socket处理哪个协议,IPV4(PF_INET)还是IPV6(PF_INET6)还是二层协议(PF_PACKET)。最后就是type参数,

       type参数用于指定这个socket是什么类型,如果是STREAM表示这个socket需要使用流式传输(有连接的传输),DGRAM表示数据包传输(无连接),RAW表示原始传输(意思是直接给收发就行,上面的东西不用处理)。

       例如PF_INET就支持这三种类型,如果参数是STREAM,那INET里面有TCP协议,如果参数是DGRAM,那INET里面有UDP、ICMP协议,如果是RAW,则直接上送IP报文。

       INET的STREAM里面默认的协议是TCP,DGRAM里面默认的协议是UDP,所以socket的第三个参数传0表示使用对于类型的默认协议。

       但是PF_PACKET就支持两种类型,因为它里面没有支持流式协议的上层协议。

       另外,所有的协议都支持RAW类型,因为这个类型的意思就是在当前协议收到包之后,直接上送或发送,而不用继续处理(INET协议有自己的IP_HDRINCL选项,不设置时,自己发送数据时不用填充IP头,当然收取到的是从IP头开始的)。

4.inet函数

  inet函数位于af_inet.c,里面是inet socket的接口函数,例如应用程序调用bind(),首先进入sockek.c调用__sys_bind(),之后会调用到inet_bind(),所以下面记录inet socket的接口函数。

  [1]int inet_listen(struct socket *sock, int backlog)

    之后调用inet_csk_listen_start(struct sock *sk, int backlog),这个函数位于inet_connection_sock.c,这表示inet的listen函数属于流式sock的处理流程,同理下面的accept函数也会进入到inet_csk_accept(struct sock *sk, int flags, int *err, bool kern)。

    listen函数主要就是backlog参数,下面记录backlog参数的意义:

    

  [2]int inet_accept(struct socket *sock, struct socket *newsock, int flags, bool kern)

 

 

 

3.TCP连接建立(三次握手)

  TCP的根本目的是可靠地传输数据,如果不需要可靠地传输,则不需要握手,直接发数据就可以(例如UDP)。

  而为了保证可靠地传输数据,便需要两端维护一些信息(序号、ACK号等等)。握手就是同步这些信息的过程,只有保证信息同步完成了,才能开始交互数据。

  [1]A向B发送SYN,A向B同步本端的连接信息(seq、MSS、SACK选项等等)。

  [2]B向A发送SYN ACK,SYN表示B向A同步本端的连接信息(seq、MSS、SACK选项等等),ACK表示对A的SYN的确认。

  [3]A向B发送ACK,对B的SYN的确认。(A发送出去后便进入到establish状态,而B需要成功收到ACK后进入establish状态)

  意思就是我有你的信息,我还知道你有我的信息(因为我收到了ACK),我便单方面宣布握手完成。

  另外解释为什么不是两次握手,其实握手的目的是为了正式传输数据之前同步信息,如果是前两次握手消息并不能保证信息确认同步完成(B向A发送了SYN ACK后不能直接进入establish状态,因为B不能保证A收到了自己的SYN报文)。

4.TCP连接释放

  可以看到连接建立是一个不同时发生的过程,即A先完成进入establish,而B要靠后进入establish。也就是谁主动发起连接谁先进入establish。

  而连接释放时,谁先主动fin,便要靠后进入close:

  [1]A向B发送FIN。    A进入FIN-WAIT1状态

  [2]B向A发送ACK。    B进入CLOSE-WAIT状态,A收到ACK后进入到FIN-WAIT2状态

  [3]B向A发送FIN ACK。   B进入LAST_ACK状态

  [4]A向B发送ACK。    A进入TIME_WAIT状态,并且等待2MSL后进入CLOSE状态

  连接释放的目的是关闭本端的连接(进入CLOSE状态),因为最后对B来说四次挥手全部完成,所以B收到最后的ACK后便可以直接关闭连接,而由于A发送ACK之后不会再收到后续报文,并且A无法保证最后的ACK被B收到,所以A要等待2MSL之后再关闭连接(进入CLOSE状态)。另外就是谁主动关闭连接谁进入CLOSE_WAIT,并不区分客户端还是服务器。

  另外,有一种情况,就是本端发送了FIN之后,没有收到ACK,而是收到了对方的FIN(同时FIN),则此时当然是给对方发送ACK,并且进入CLOSING状态,并且收到对方的ACK之后,进入到TIME_WAIT状态。

  另外,有一种情况,就是本端发送了FIN之后,没有收到ACK,而是收到了对方的FIN_ACK(同时FIN),则此时当然是给对方发送ACK,并且本端直接进入到TIME_WAIT状态。                       

 5.TCP有限状态机

  [1]线并不代表某端,而是表示流程(例如客户端可以主动打开(客户端发送SYN)但是被动关闭(服务器首先发送FIN)),粗实线表示主动流程,虚实线表示被动流程,普通实线表示其他流程,例如上面两行说明的。