分类: LINUX
2010-05-19 15:41:56
1) TCP协议用来在套接字与套接字之间实现双向对称管道流式数据交换. TCP用户数据流的每一字节对应于一个32位序列号, 数据流的顺序性用序列号的连续性来保证, TCP包头的seq字段给出了TCP数据区的起始序列号, ack_seq给出了源套接字已成功接收到的序列号. TCP连接过程就是套接字互相同步对方初始接收序列号的过程.
2) TCP连接过程分三步完成, 首先连接端向监听端发送序列同步请求报文(SYN), 将自已选取的初始序列号传递给对端, 对端接收到SYN报文后, 选取自已的初始序列号并将源端初始序列号的下一编号作为确认序列号以同步响应报文(SYN-ACK)回送给源端, 源端成功接收到同步响应报文后就进入连接状态, 同时向对端发送响应报文(ACK), 对端成功接收到ACK报文后也进入连接状态. 这样两端的套接字就获得了对方所使用的初始传输序列号, 这时处于连接状态的套接字从自已初始序列号的下一编号开始跟踪输出用户数据流, 对端套接字则从对方的初始序列号的下一编号开始跟踪输入用户数据流.
3) listen()系统调用将套接字置为监听状态(TCP_LISTEN), 所有的监听套接字被登记到监听套接字表中(tcp_listening_hash). 监听套接字的TCP参数结构(tcp_opt)(sk->tp_pinfo.af_tcp)具有TCP监听结构(tcp_listen_opt)(tcp_opt->listen_opt), 它包含连接请求索引表(tcp_listen_opt->syn_table), 所有向该监听套接字发送的连接请求报文被转换为连接请求结构(open_request)登记在该索引表中. 索引表的容量由tcp_listen_opt->max_qlen_log指数来限定, 其值至少为6, 即每个监听套接字至少可缓冲64个连接请求, 最多缓冲数不超过sysctl_max_syn_backlog系统配置值. 当连接请求表满时, 监听套接字不再回应SYN-ACK报文, 而是回应连接复位(RST)报文, 主动关闭对端连接请求. 当连接请求表非空时, 监听套接字的同步响应定时器(tcp_synack_timer)每隔0.2秒扫描一定数量的连接请求, 对未收到响应超时(3秒)的连接请求重发SYN-ACK报文, 重发次数可由sysctl_tcp_synack_retries设定, 缺省为5次, 超过重发次数则删除该请求.
4) 监听套接字成功接收到对端SYN-ACK的响应报文时, 从自已的连接请求表中提取出对应的请求结构, 同时复制自身, 生成TCP_SYN_RECV状态的连接子套接字并登记到连接套接字表(tcp_ehash). 连接套接字由连接两端的套接字地址和端口共同寻址, 连接子套接字继承监听套接字的端口号, 因此从监听套接字产生的连接子套接字是共享同一端口的. 连接子套接字绑定到请求结构后被转移到监听套接字连接接受队列中(tcp_opt->accept_queue). 接受队列的容量(sk->max_ack_backlog)由listen()的第2个参数设定. 当接受队列满时, sysctl_tcp_abort_on_overflow用来控制是继续接受连接请求还是拒绝连接请求.
5) accept_queue中连接套接字经过状态变换函数(tcp_rcv_state_process)从TCP_SYN_RECV状态变换为TCP_ESTABLISHED状态, 然后进行连接状态的各种初始化, 最后唤醒调用accept()在wait_for_connect()中睡眠的进程, 将连接套接字映射为文件描述符传递给用户.
6) 三步握手的大致流程为:
tcp_v4_rcv()->tcp_v4_do_rcv()->tcp_rcv_state_process()->tcp_v4_conn_request()->tcp_v4_send_synack()->tcp_v4_synq_add()
tcp_v4_rcv()->tcp_v4_do_rcv()->tcp_v4_hnd_req()->tcp_v4_check_req()->tcp_v4_syn_recv_sock()->tcp_create_openreq_child()->__tcp_v4_hash()->__tcp_interic_port()->tcp_acceptq_queue()->tcp_child_process()->tcp_rcv_state_process()->sock_def_readable()