tcp、http 学习小结

来源:互联网 发布:烂命鸳鸯知乎 编辑:程序博客网 时间:2024/06/11 15:51

tcp、http 学习小结

前言
最近因为cdn的一个问题,困扰了自己好久。因为需要统计网站访问的成功数,而且要求比较精确。目前的实现不能满足要求,因为没有区别访问成功与否,也没有对超时做处理。期间解决这个问题,走了不少弯路,现在在这里记录下来,为自己也为别人提供一个方便。

关键字

  • tcp
  • 1.tcp三次握手
  • 2.tcp序列号,确认号
  • 3.tcp 头部扩展option域
  • http
  • 1.http 1.0与http1.1
  • 2.get,response
  • 3.http 返回码
  • wireshark
  • 工具介绍

一.tcp小结

tcp头格式:
tcp头部格式
tcp结构体struct tcphdr

struct tcphdr {         __u16   source;         __u16   dest;         __u32   seq;         __u32   ack_seq;#if defined(__LITTLE_ENDIAN_BITFIELD)         __u16   res1:4,                 doff:4,                 fin:1,                 syn:1,                 rst:1,                 psh:1,                 ack:1,                 urg:1,                 ece:1,                 cwr:1;#elif defined(__BIG_ENDIAN_BITFIELD)         __u16   doff:4,                 res1:4,                 cwr:1,                 ece:1,                 urg:1,                 ack:1,                 psh:1,                 rst:1,                 syn:1,                 fin:1;#else#error  "Adjust your <asm/byteorder.h> defines"#endif           __u16   window;         __u16   check;         __u16   urg_ptr;};

tcp建立连接与关闭连接

tcp建立连接需要经过三次握手syn->syn/ack->ack,三次握手每一步都是必需的,在网络环境比较差的时候,会发生握手包丢失,tcp会自动重复发送握手包,直到连接建立或者超时。

tcp建立连接时候的初始序列号并不是0,而是经过一种算法得到的,具体百度。这可以保证tcp序列号不至于错乱,在wireshark中显示的序列号是相对序列号,wireshark把每次syn的序列号作为基准0,这样的目的是看起来更直观。
syn:
syn-seq

syn/ack:
这里写图片描述

ack:
这里写图片描述

步骤 flags 标志位 seq number ack number tsval tsecr syn 0x002 ack=1 0x92 0x68 0xa8 0x53 0 582971879 0 syn/ack 0x012 ack=1,syn=1 0xb2 0x29 0xde 0x5b 0x92 0x68 0xa8 0x54 776961681 582971879 ack 0x010 ack=1 0x92 0x68 0xa8 0x54 0xb2 0x29 0xde 0x5c 582971887 776961681

可以看出来初始序列号并不是0,每次的确认号表示的是期望下次从对方收到的序列号的值。序列号与确认号都是uint32_t类型,占据32位,达到最大又会从0开始。

类似的结束时候

步骤 flags 标志位 seq number ack number tsval tsecr fin/ack 0x011 fin=1,ack=1 0xb2 0x29 0xe4 0x68 0x92 0x68 0xa8 0x99 776961691 582971887 ack 0x010 ack=1 0x92 0x68 0xa8 0x99 0xb2 0x29 0xe4 0x69 582971898 776961691 fin/ack 0x011 fin=1,ack=1 0x92 0x68 0xa8 0x99 0xb2 0x29 0xe4 0x69 582971898 776961691 ack 0x010 ack=1 0xb2 0x29 0xe4 0x69 0x92 0x68 0xa8 0x9a 776961720 582971898

tcp关闭连接是经过四次握手,建立连接经过三次握手,值得提出的是建立和关闭连接并不消耗序列号,只有成功建立或者关闭序列号才会加一,具体可以自己抓包分析下。

tcp option头部

上面的tsval,与tsecr是在tcp头部的扩展字段(kind)。本身并不包含于tcphdr结构体中,因为这是由后续rfc文档建议添加的字段,在tcp模块编写之后提出的。因为tcp option的存在,所以tcp头的长度一般不是20(tcp->doff == 4),tcp头最小是20bytes。
下面是kind字段意义列表:

Kind Meaning Reference 0 End of Option List [RFC793] 1 No-Operation [RFC793] 2 Maximum Segment Size [RFC793] 3 WSOPT - Window Scale [RFC1323] 4 SACK Permitted [RFC2018] 5 SACK [RFC2018] 6 Echo (obsoleted by option 8) [RFC1072] 7 Echo Reply (obsoleted by option 8) [RFC1072] 8 TSOPT - Time Stamp Option [RFC1323] 9 Partial Order Connection Permitted [RFC1693] 10 Partial Order Service Profile [RFC1693] 11 CC [RFC1644] 12 CC.NEW [RFC1644] 13 CC.ECHO [RFC1644] 14 TCP Alternate Checksum Request [RFC1146] 15 TCP Alternate Checksum Data [RFC1146] 16 Skeeter [Knowles] 17 Bubba [Knowles] 18 Trailer Checksum Option [Subbu & Monroe] 19 MD5 Signature Option [RFC2385] 20 SCPS Capabilities [Scott] 21 Selective Negative Acknowledgements [Scott] 22 Record Boundaries [Scott] 23 Corruption experienced [Scott] 24 SNAP [Sukonnik] 25 Unassigned (released 12/18/00) 26 TCP Compression Filter [Bellovin]

其中我用到的是kind为8的字段,意思是在每个数据包上加上tcp时间戳。时间戳分为tsval与tsecr两个,分别由连接的两端进行维护,每次传输时需要在tsecr上对对方上次的数据包的时间戳进行确认,并在tsval加上自己最新的时间戳。
tcp时间戳并不是系统时间,通过date -d @776961691可以看到结果是Mon Aug 15 22:41:31 CST 1994,时间明显不对。tcp时间戳不采取系统时间的理由也很充分,每次同步时间都会导致时间突变,这就会导致时间戳也突变,这是不可取的。实际上tcp采用的是系统从开机以来的相对时间,这样只要不关机,就不会变化。系统默认每次HZ是250,这是在内核编译时指定的,也就是jiffies每增加一就是4ms的时间。因此可以通过两次tsval时间差计算出两次发包的时间间隔。
http://blog.csdn.net/zhandoushi1982/article/details/5536210

http

http是应用层协议,一般是建立在tcp之上的。也就是说http是面向用户,并不关心底层连接的实现。有句话叫做“http面向连接的,但是是无状态的”。这句话可以这么理解:基于tcp实现的http,每次发送请求都会先建立tcp连接,在连接建立之后发送http数据。然而http又是无记忆的,http并不知道自己已经发送过哪些数据,所以对于同样的请求,比如每次打开www.baidu.com,刷新页面都是重新发送的数据。http把这当作一次新的请求。
http数据是紧挨着tcp头之后的。
一个完整的包结构是这样的:
-{Ethernet | Ip | tcp/udp | HTTP}
http现在最流行的是http 1.1版本,相比于之前版本,效率提高了许多。http 1.0是短连接,每次传输数据会建立一个tcp连接,使用完毕立马关闭,下次使用重新建立连接。http 1.1则默认长连接,这在http头域connection选项中可以设置,值为close为短连接,keep-alive为长连接。
验证如下:

telnet www.baidu.com 80
输入:
GET / HTTP/1.1
两次回车,(这是因为http规定行末,以及头结束必须为\r\n)
GET / HTTP/1.0
http 1.0
这里写图片描述
http 1.1
这里写图片描述

先写这么多。。。
参考链接给在下面,懒得写了
http://blog.chinaunix.net/uid-8059407-id-2034302.html
http://www.cse.scu.edu/~dclark/am_256_graph_theory/linux_2_6_stack/linux_2tcp_8h-source.html
http://blog.csdn.net/mrwangwang/article/details/8537775
http://blog.csdn.net/zhandoushi1982/article/details/5536210

0 0