Chinaunix首页 | 论坛 | 博客
  • 博客访问: 35853
  • 博文数量: 17
  • 博客积分: 608
  • 博客等级: 中士
  • 技术积分: 265
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-07 00:56
文章分类
文章存档

2012年(17)

我的朋友

分类: 虚拟化

2012-07-18 12:40:31

   TCP提供一种面向连接的、可靠的字节流服务。通常是一个客户和一个服务器在彼此交换数据。TCP提供了过多的事务特征,而UDP提供的则不够。通常应用程序使用UDP来构造(避免TCP连接的开销),而许多需要的特征(如动态超时和重传、拥塞避免等)被放置在应用层设计和实现。

   说实在的看了几天资料,还是似懂非懂,或许水平有限,或许学院派的协议过于高深!没法、只好按自己的想法来做了。APO的TCP协议实现目标:

1、集成100GBPS的以太网接口(考虑到未来的技术发展可能会做到),相当于10GB/S的字节流速率,算平均每个数据包1KB吧,即相当于10M包/S,即每秒1千万个数据包的速度。考虑到同时在线用户的通信时间差,估计可支持10亿同时在线的用户。APO使用一种“硬件钩挂提交”技术,可在单周期指令实现一个数据包的提交。算平均每个数据包1KB,CPU速度1G吧,那APO内部网络带宽达到1TB/S。即是,1G数据包/秒。现有的TCP协议不太好,实现的方法也不行。APO有1K核,同一个端口可以有1K个进程,每进程可自动创建64K个线程(连接)。速度重要!现有的TCP协议实现方法,我都不知如何批评好。ACK应答包及所有的TCP包本来就应是应用进程处理,TCP协议层又多余的去管,多CPU支持反而不考虑。

当服务器在创建一个新的进程或操作系统正忙于处理优先级更高的进程时,到达多个连接请求。TCP是如何处理这些呼入的连接请求?在伯克利的TCP实现中采用以下规则:真是麻烦,怪不得源码又臭又长

1) 正等待连接请求的一端有一个固定长度的连接队列,该队列中的连接已被TCP接受(即三次握手已经完成),但还没有被应用层所接受。注意区分TCP接受一个连接是将其放入这个队列,而应用层接受连接是将其从该队列中移出。主人都不知情,下面就乱搞。

2) 应用层将指明该队列的最大长度,这个值通常称为积压值( backlog )。它的取值范围是0 ~ 5之间的整数,包括0和5(大多数的应用程序都将这个值说明为5)。多余。

3) 当一个连接请求(即SYN)到达时,TCP使用一个算法,根据当前连接队列中的连接数来确定是否接收这个连接。我们期望应用层说明的积压值为这一端点所能允许接受连接的最大数目,但情况不是那么简单。被TCP接受而等待应用层接受的最大连接数。这个积压值对系统所允许的最大连接数,或者并发服务器所能并发处理的客户数,并无影响。有几个连接?搞笑。

4) 如果对于新的连接请求,该TCP监听的端点的连接队列中还有空间,TCP模块将对SYN进行确认并完成连接的建立。但应用层只有在三次握手中的第三个报文段收到后才会知道这个新连接时。另外,当客户进程的主动打开成功但服务器的应用层还不知道这个新的连接时,它可能会认为服务器进程已经准备好接收数据了(如果发生这种情况,服务器的TCP仅将接收的数据放入缓冲队列)。这又有新问题。

5) 如果对于新的连接请求,连接队列中已没有空间, TCP将不理会收到的SYN。也不发回任何报文段(即不发回RST)。如果应用层不能及时接受已被TCP接受的连接,这些连接可能占满整个连接队列,客户的主动打开最终将超时。咳,真晕了。

2、学院派的协议不考虑实际编程需要!没法。只好做个兼容的系统调用库吧。
不建议新的服务器程序使用TCP。


 

16位源端口号

 16位目的端口号

32位数据包序列号

32位数据包确认序列号

4位部首长度

保留(6)

URG

ACK

PSH

RST

SIN

FIN

16位 窗口大小

16位效验和

16位紧急指针

选项

数据

TCP通过下列方式来提供可靠性:

•  应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据报长度将保持不变。由TCP传递给IP的信息单位称为报文段或段(segment)。

• 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。自适应的超时及重传策略。这应该在应用进程做系统调用实现!

• 当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟0.X秒。这应该在应用进程做系统调用实现!

• TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段(等发端超时并重发)。这应该在应用进程做系统调用实现!

• 既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要, TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。  这应该在应用进程做系统调用实现!

• 既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。这应该在应用进程做系统调用实现!

• TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。这应该在应用进程做系统调用实现!每个TCP段都包含源端和目的端的端口号,用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接。一个IP地址和一个端口号也称为一个插口(socket),序号用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节。实际上就是数据包的序号!序号是32 bit的无符号数,当建立一个新的连接时, SYN标志变1。序号字段包含由这个主机选择的该连接的初始序号ISN(Initial Sequence Number)。该主机要发送数据的第一个字节序号为这个ISN加1,因为SYN标志消耗了一个序号。既然每个传输的字节都被计数,确认序号包含发送确认的一端所期望收到的下一个序号。因此,确认序号应当是上次已成功收到数据字节序号加1。只有ACK标志为1时,确认序号字段才有效。发送ACK无需任何代价,因为32
bit的确认序号字段和ACK标志一样,总是TCP首部的一部分。因此,我们看到一旦一个连接建立起来,这个字段总是被设置, ACK标志也总是被设置为1。如果1~1024字节已经成功收到,它所能做的就是发回一个确认序号为1025的ACK。它也无法对一个报文段进行否认。

URG 紧急指针( urgent pointer)有效。没用的东西

ACK 确认序号有效。

PSH 接收方应该尽快将这个报文段交给应用层。没用的东西。

RST 重建连接。

SYN 同步序号用来发起一个连接。

FIN 发端完成发送任务。

    检验和覆盖了整个的TCP报文段: TCP首部和TCP数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。这应该在应用进程做系统调用实现!
    建立一个连接需要三次握手,而终止一个连接要经过4次握手。这由TCP的半关闭(half-close)造成的。既然一个TCP连接是全双工(即数据在两个方向上能同时传递),因此每个方向必须单独地进行关闭。这原则就是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向连接。当一端收到一个FIN,它必须通知应用层另一端几经终止了那个方向的数据传送。发送FIN通常是应用层进行关闭的结果。收到一个FIN只意味着在这一方向上没有数据流动。一个TCP连接在收到一个FIN后仍能发送数据。而这对利用半关闭的应用来说是可能的,尽管在实际应用中只有很少的TCP应用程序这样做。编程接口必须为应用程序提供一种方式来说明:我已经完成了数据传送,因此发送一个文件结束(FIN)给另一端,但我还想接收另一端发来的数据,直到它给我发来文件结束(FIN)。
    当一个新的连接请求到达服务器时,服务器接受这个请求,并调用一个新进程来处理这个新的客户请求。不同的操作系统使用不同的技术来调用新的服务器进程。一个并发服务器调用一个新的进程来处理每个客户请求,因此处于被动连接请求的服务器应该始终准备处理下一个呼入的连接请求。那正是使用并发服务器的根本原因。但仍有可能出现当服务器在创建一个新的进程时,或操作系统正忙于处理优先级更高的进程时,到达多个连接请求。
最可靠的方式就是只要不得到确认,就重新发送数据报,直到得到对方的确认为止。

1.连接的建立
    在建立连接的时候,客户端首先向服务器申请打开某一个端口(用SYN段等于1的TCP报文),然后服务器端发回一个ACK报文通知客户端请求报文收到,客户端收到确认报文以后再次发出确认报文确认刚才服务器端发出的确认报文,至此,连接的建立完成。这就叫做三次握手。如果打算让双方都做好准备的话,总共要发送三次报文。可以想到,如果再加上TCP的超时重传机制,那么TCP就完全可以保证一个数据包被送到目的地。

2.结束连接
    TCP有一个特别的概念叫做half-close,这个概念是说,TCP的连接是全双工(可以同时发送和接收)连接,因此在关闭连接的时候,必须关闭传和送两个方向上的连接。客户机给服务器一个FIN为1的TCP报文,然后服务器返回给客户端一个确认ACK报文,并且发送一个FIN报文,当客户机回复ACK报文后(四次握手),连接就结束了。

3.最大报文长度
      在建立连接的时候,通信的双方要互相确认对方的最大报文长度(MSS),以便通信。一般这个SYN长度是MTU减去固定IP首部和TCP首部长度。对于一个以太网,一般可以达到1460字节。当然如果对于非本地的IP,这个MSS可能就只有536字节,而且,如果中间的传输网络的MSS更佳的小的话,这个值还会变得更小。

4.  2MSL等待状态
   说的是在TIME_WAIT2发送了最后一个ACK数据报以后,要进入TIME_WAIT状态,这个状态是防止最后一次握手的数据报没有传送到对方那里而准备的(注意这不是四次握手,这是第四次握手的保险状态)。这个状态在很大程度上保证了双方都可以正常结束,但是,问题也来了。由于插口的2MSL状态(插口是IP和端口对的意思,socket),使得应用程序在2MSL时间内是无法再次使用同一个插口的,对于客户程序还好一些,但是对于服务程序,例如httpd,它总是要使用同一个端口来进行服务,而在
2MSL时间内,启动httpd就会出现错误(插口被使用)。为了避免这个错误,服务器给出了一个平静时间的概念,这是说在2MSL时间内,虽然可以重新启动服务器,但是这个服务器还是要平静的等待2MSL时间的过去才能进行下一次连接。多余啊

5.   FIN_WAIT_2状态
   这是在关闭连接时,客户端和服务器两次握手之后的状态。在这个状态下,应用程序还有接受数据的能力,但是已经无法发送数据,但是也有一种可能是,客户端一直处于FIN_WAIT_2状态,而服务器则一直处于WAIT_CLOSE状态,而直到应用层来决定关闭这个状态。多余啊

6. RST,同时打开和同时关闭
    RST是另一种关闭连接的方式,应用程序应该可以判断RST包的真实性,即是否为异常中止。而同时打开和同时关闭则是两种特殊的TCP状态,发生的概率很小。

7. TCP给出两个策略来提高发送效率和减低网络负担:
(1)捎带ACK。(2)Nagle算法(一次尽量多的发数据)。 这应该在应用进程做系统调用实现!

APO的网络管理者实现TCP/IP:

1、 MAC协议中的“类型”将改为CPU号,只有一种IP数据包类型。接收到的IP数据包如果CPU号为0表示该数据包还没分配到具体的CPU来运作。网络管理者应以公平,均衡原则为其分配CPU号。对于一些已经预定义的端口号(如小于1K的端口号),是预先就分配了CPU号的。只有那些并行多核的服务器进程的客户端IP数据包需由网络管理者为其自动分配CPU号。

 BU48

 BU48

  BU16

数据包(IP)

BU32

目 的 地 址

源 地 址

CPU

23――750字符Z

CRC

 

2、网络管理者的主要工作是:1)、从以太网端口接收IP数据包,分配到相应的CPU与端口对应的网络进程。2)、循环从各个网络进程端口接收IP数据包,并从以太网端口发送出去。

3、 TCP,SOCKET协议层是在用户进程的时间片中执行,不在网络管理者的内核层执行。

4、每个CPU核最多有64K个连接线程,分配总共1GZ(2GB)的共享缓存空间。2GB可有4M个包缓存,平均每个连接线程有64个包缓存(发送,接收各32个包缓存)。其实一个端口进程的所有线程是共享同一个端口进程的发送或接收包缓存区的。一个服务端口进程有一千个连接就有64000个包缓存。

应用程序层:

•  作为服务器程序,应可支持上万个连接,连接可以是多种协议如TCP,UDP, WSNP等。服务器进程应是多线程的并行的非阻塞的。当同时有100个UDP连接,200个TCP连接时,如果有200个连接是对约10MB的不同文件的读写;公平起见,不应处理完一个连接,才处理下一个连接。而应在处理一个连接的假如10个数据包吧,就转去处理下一个连接的10个数据包。。。。。。我的理解这叫非阻塞的线程调度。一个进程可以有是服务器的连接线程,也可有客户端的连接线程。所以,服务器程序与客户端程序不会有太多的说法,当然,专业点会清楚些。

•  作为应用层,只是收发数据包,协议数据包是由Socket插口层来执行。其实,耗时间的是应用层数据包的装配读写。就算数据包的大小是1.5KB,15MB的文件也需10000包。APO使用的技术是装配好的发送数据包直传到网络管理者,上万个数据包也是瞬间发送,而无需协议层。作为单纯的服务器进程,通常是接收包小和少,发送包则多而大。所以发送线程弄多点,而接收线程一个就行;至于ACK,重发等协议层能快而好的处理。客户端进程则反之。一个进程只需初始化一个Socket系统调用就可实现上万个连接!建立上万个连接等协议工作仅需一个Socket线程。所以应用程序跟网络相关的代码就2行。

SOCKET插口层:
•     普通的I/O操作过程
–    打开文件->读/写操作->关闭文件

•     流式套接字(SOCK_STREAM)
–    提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。

•     数据报套接字(SOCK_DGRAM)
–    提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。

•     原始套接字(SOCK_RAW)
–    可以对较低层次协议,如IP、ICMP直接访问。

•     使用ServerSocket函数来给应用程序创建一个服务套接口。

ServerSocket(
     BU16 port, //以网络字节序表示的16位端口号;前1024个端口已作保留
                // 端口, port为0,则由系统自动指派一个1024-5000之间的端口号 
  BU16 protocol // TCP, UDP, RAW, ICMP, WSNP
);

•     使用clientSocket函数来给应用程序创建一个客户套接口。
         clientSocket( BU16 SocketP );
// SocketP指向有关远端服务器的IP地址和端口及协议表。有多少个协议连接就填插口连接表。浏览器的连接表中必定有一个DNS连接项。连接表通常希望自动填写,服务器是这样的,客户端要麻烦点,对人好记忆的一般是名称字符串,所以要通过DNS连接来实现自动转换。进程的头文件里定义了:最大连接数;为了线程公平调度的:非阻塞的最大处理接收包数量;非阻塞的最大处理发送包数量。为了流量控制的:最大发送缓存区缓存包的数量;最大接收缓存区缓存包的数量。

服务器连接表的内容:远端IP地址,远端端口,协议号,连接号。
客户端连接表内容:本地端口,自动分配;远端IP地址;远端端口;协议号;连接号。

Socket线程实现功能:与WinSocket比较
–             bind   绑定本机端口
–             connect 建立连接      // 三次握手
–             listen 监听端口
–             accept 接受连接     
–             CreateThread 建立新的连接线程自行与客户端通信
–             recv, recvfrom 数据接收。注:这不需要,另用钩挂提交技术。
–    发送数据包保留,以便应用层做超时重发;直到收到确认包才清除并回收缓存
–    根据最大接收缓存包的数量的80%,做流量控制。窗口大小应是可发送的数据包数量。如果发 送方收到接受方的窗口大小为0的TCP数据报,那么发送方将停止发送数据,等到接受方发送窗口大小不为0的数据报的到来。
    
应有层系统调用:
–             Socket
–             send, sendto数据发送。注:不需要,只要装配了数据包就行了。
–             close, shutdown 关闭套接字。4次握手

以下在应用层解决:
1) 重传定时器使用于当希望收到另一端的确认,拥塞避免。
2) 坚持(persist)定时器使窗口大小信息保持不断流动,即使另一端关闭了其接收窗。
3) 保活( keepalive )定时器可检测到一个空闲连接的另一端何时崩溃或重启。
4)交互数据流:捎带ACK,Nagle算法。

阅读(956) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~