tcp要点学习-建立连接

准备:

 

     在这里本文将遵循上一篇文章的风格,只提TCP协议中的要点,这样我觉得可以更容易地掌握TCP。或者根本谈不上掌握,对于这种纯理论的东西,即使你现在掌握了再多的细节,一段时间后也会淡忘。

 

     在以后各种细节中,因为我们会涉及到分析一些TCP中的数据报,因此一个协议包截获工具必不可少。在<TCP/IP详解>中一直使用tcpdump。这里因为我的系统是windows,所以只好使用windows平台的tcpdump,也就是WinDump。在使用WinDump之前,你需要安装该程序使用的库WinpCap

 

      关于WinDump的具体用法你可以从网上其他地方获取,这里我只稍微提一下。要让WinDump开始监听数据,首先需要确定让其监听哪一个网络设备(或者说是网络接口)。你可以:

 

windump - D

 

      获取当前机器上的网络接口。然后使用:

 

windump - i 2  

 

      开始对网络接口2的数据监听。windump如同tcpdump(其实就是tcpdump)一样支持过滤表达式,windump将会根据你提供的过滤表达式过滤不需要的网络数据包,例如:

 

windump - i 2 port 4000  

 

      那么windump只会显示端口号为4000的网络数据。

 

序号和确认号:

 

      要讲解TCP的建立过程,也就是那个所谓的三次握手,就会涉及到序号和确认号这两个东西。翻书到TCP的报文头,有两个很重要的域(都是32位)就是序号域和确认号域。可能有些同学会对TCP那个报文头有所疑惑(能看懂我在讲什么的会产生这样的疑惑么?),这里我可以告诉你,你可以假想TCP的报文头就是个C语言结构体(假想而已,去翻翻bsd对TCP的实现,肯定没这么简单),那么大致上,所谓的TCP报文头就是:

 

      那么,这个序号和确认号是什么?TCP报文为每一个字节都设置一个序号,觉得很奇怪?这里并不是为每一字节附加一个序号(那会是多么可笑的编程手法?),而是为一个TCP报文附加一个序号,这个序号表示报文中数据的第一个字节的序号,而其他数据则是根据离第一个数据的偏移来决定序号的,例如,现在有数据:abcd。如果这段数据的序号为1200,那么a的序号就是1200,b的序号就是1201。而TCP发送的下一个数据包的序号就会是上一个数据包最后一个字节的序号加一。例如efghi是abcd的下一个数据包,那么它的序号就是1204。通过这种看似简单的方法,TCP就实现了为每一个字节设置序号的功能(终于明白为什么书上要告诉我们‘为每一个字节设置一个序号’了吧?)。注意,设置序号是一种可以让TCP成为’可靠协议‘的手段。TCP中各种乱七八糟的东西都是有目的的,大部分目的还是为了’可靠‘两个字。别把TCP看高深了,如果让你来设计一个网络协议,目的需要告诉你是’可靠的‘,你就会明白为什么会产生那些乱七八糟的东西了。

 

      接着看,确认号是什么?因为TCP会对接收到的数据包进行确认,发送确认数据包时,就会设置这个确认号,确认号通常表示接收方希望接收到的下一段报文的序号。例如某一次接收方收到序号为1200的4字节数举报,那么它发送确认报文给发送方时,就会设置确认号为1204。 大部分书上在讲确认号和序号时,都会说确认号是序号加一。这其实有点误解人,所以我才在这里废话了半天(高手宽容下:D)。

 

开始三次握手:

 

      如果你还不会简单的tcp socket编程,我建议你先去学学,这就好比你不会C++基本语法,就别去研究vtable之类。

三次握手开始于客户端试图连接服务器端。当你调用诸如connect的函数时,正常情况下就会开始三次握手。随便在网上找张三次握手的图:

connection

      如前文所述,三次握手也就是产生了三个数据包。客户端主动连接,发送SYN被设置了的报文(注意序号和确认号,因为这里不包含用户数据,所以序号和确认号就是加一减一的关系)。服务器端收到该报文时,正常情况下就发送SYN和ACK被设置了的报文作为确认,以及告诉客户端:我想打开我这边的连接(双工)。客户端于是再对服务器端的SYN进行确认,于是再发送ACK报文。然后连接建立完毕。对于阻塞式socket而言,你的connect可能就返回成功给你。

 

      在进行了铺天盖地的罗利巴索的基础概念的讲解后,看看这个连接建立的过程,是不是简单得几近无聊? 我们来实际点,写个最简单的客户端代码:

      主要就是connect。运行程序前我们运行windump:

 

windump - i 2 host 220.181 . 37.55  

 

00 : 38 : 22.979229 IP noname.domain. 4397   >   220.181 . 37.55 . 80 : S 2523219966 : 2523219966 ( 0 ) win 65535   < mss 1460 ,nop,nop,sackOK >
00 : 38 : 23.024254 IP 220.181 . 37.55 . 80   > noname.domain. 4397 : S 1277008647 : 1277008647 ( 0 ) ack 2523219967 win 2920   < mss 1440 ,nop,nop,sackOK >
00 : 38 : 23.024338 IP noname.domain. 4397   >   220.181 . 37.55 . 80 : . ack 1 win 65535  

 

      如何分析windump的结果,建议参看<tcp/ip详解>中对于tcpdump的描述。

建立连接的附加信息:

 

      虽然SYN、ACK之类的报文没有用户数据,但是TCP还是附加了其他信息。最为重要的就是附加的MSS值。这个可以被协商的MSS值基本上就只在建立连接时协商。如以上数据表示,MSS为1460字节。

 

连接的意外:

 

      连接的意外我大致分为两种情况(也许还有更多情况):目的主机不可达、目的主机并没有在指定端口监听。当目的主机不可达时,也就是说,SYN报文段根本无法到达对方(如果你的机器根本没插网线,你就不可达),那么TCP收不到任何回复报文。这个时候,你会看到TCP中的定时器机制出现了。TCP对发出的SYN报文进行计时,当在指定时间内没有得到回复报文时,TCP就会重传刚才的SYN报文。通常,各种不同的TCP实现对于这个超时值都不同,但是据我观察,重传次数基本上都是3次。例如,我连接一个不可达的主机:

 

12 : 39 : 50.560690 IP cd - zhangmin. 1573   >   220.181 . 37.55 . 1024 : S 3117975575 : 3117975575 ( 0 ) win 65535   < mss 1460 ,nop,nop,sackOK >
12 : 39 : 53.538734 IP cd - zhangmin. 1573   >   220.181 . 37.55 . 1024 : S 3117975575 : 3117975575 ( 0 ) win 65535   < mss 1460 ,nop,nop,sackOK >
12 : 39 : 59.663726 IP cd - zhangmin. 1573   >   220.181 . 37.55 . 1024 : S 3117975575 : 3117975575 ( 0 ) win 65535   < mss 1460 ,nop,nop,sackOK >

 

      发出了三个序号一样的SYN报文,但是没有得到一个回复报文(废话)。每一个SYN报文之间的间隔时间都是有规律的,在windows上是3秒6秒9秒12秒。上面的数据你看不到12秒这个数据,因为这是第三个报文发出的时间和connect返回错误信息时的时间之差。另一方面,如果连接同一个网络,这个间隔时间又不同。例如直接连局域网,间隔时间就差不多为500ms。 (我强烈建议你能运行windump去试验这里提到的每一个现象,如果你在ubuntu下使用tcpdump,记住sudo :D) 出现意外的第二种情况是如果主机数据包可达,但是试图连接的端口根本没有监听,那么发送SYN报文的这方会收到RST被设置的报文(connect也会返回相应的信息给你),例如:

 

13 : 37 : 22.202532 IP cd - zhangmin. 1658   > 7AURORA - CCTEST. 7100 : S 2417354281 : 2417354281 ( 0 ) win 65535   < mss 1460 ,nop,nop,sackOK >
13 : 37 : 22.202627 IP 7AURORA - CCTEST. 7100   > cd - zhangmin. 1658 : R 0 : 0 ( 0 ) ack 2417354282 win 0
13 : 37 : 22.711415 IP cd - zhangmin. 1658   > 7AURORA - CCTEST. 7100 : S 2417354281 : 2417354281 ( 0 ) win 65535   < mss 1460 ,nop,nop,sackOK >
13 : 37 : 22.711498 IP 7AURORA - CCTEST. 7100   > cd - zhangmin. 1658 : R 0 : 0 ( 0 ) ack 1 win 0
13 : 37 : 23.367733 IP cd - zhangmin. 1658   > 7AURORA - CCTEST. 7100 : S 2417354281 : 2417354281 ( 0 ) win 65535   < mss 1460 ,nop,nop,sackOK >
13 : 37 : 23.367826 IP 7AURORA - CCTEST. 7100   > cd - zhangmin. 1658 : R 0 : 0 ( 0 ) ack 1 win 0  

 

      可以看出,7AURORA-CCTEST.7100返回了RST报文给我,但是我这边根本不在乎这个报文,继续发送SYN报文。三次过后connect就返回了。(数据反映的事实是这样)

 

关于listen:

 

      TCP服务器端会维护一个新连接的队列。当新连接上的客户端三次握手完成时,就会将其放入这个队列。这个队 列的大小是通过listen设置的。当这个队列满时,如果有新的客户端试图连接(发送SYN),服务器端丢弃报文, 同时不做任何回复。

 

总结:

 

      TCP连接的建立的相关要点就是这些(or more?)。正常情况下就是三次握手,非正常情况下就是SYN三次超时,以及收到RST报文却被忽略。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值