Unix 网络编程(二)- 你需要知道的传输层协议实现细节。

写在开头:

上一篇中我们分析了并实际运行了第一个socket程序,从而对网络编程的整体设计以及工作流程有了一些了解。作为第二篇文章,在此文中我们将分析网络编程需要的一些理论知识,主要就是TCP/IP协议栈的内容。但是,我们不会枯燥的去讲解这些协议,而且我们的目的也不是去理解这些协议所有的细节,这里我们将会从网络编程的角度去分析传统的协议栈中的运输层协议(TCP, UDP, SCTP), 与纯粹介绍协议的不同,这里是从网络编程的角度去分析协议当初为何这样来设计而不是从协议的角度去分析如何编程,从而能够让我们在避免枯燥理论的同时又可以把握这些理论的实践价值,即当初的协议之所以这样来设计的工程价值。同样,本文的内容主要来自《Unix网络编程 卷一 套接字联网API》第二章的一些总结。

这里再说下我个人写博客的体会。看一本书,看一遍,和之后做习题再看一遍,和能把它写出来,和能把它讲给别人听,和能熟练的应用,这些过程需要花费的工夫区别很大。

下面开始第二篇的内容。

--------------------------------------------------------------------------------------------------------------------------------------------------

1. 什么是Socket 编程

我们首先看一下一个网络通信的过程到底发生了什么,为什么两台独立的机器就可以通信并且相互传递数据。

下面以我们每天都会做的一件事:用户访问一个网页为例:



用户在浏览器中输入ip地址访问一个网页,然后服务器监测到访问之后会回传网页中的内容。在这样一个应用中,我们为简单起见假设服务器和客户端在一个以太网中,网络中传输的数据流的示意图如下:


上图就是是个web请求的完整过程,这里面我们没有使用标准的OSI的七层模型而是以网际协议中实际使用的四层模型为例来进行删除。从用户应用进程的建立到数据的传输需要机器从用户态向内核态进行转化(或者是数据从应用层向运输层和网络层进行传输),而完成这个转化的桥梁就是我们socket提供的网络编程的API,通过网络编程可以完成上面的所有步骤,使得这个流程对用户来讲像是对等之间的传输而没有这种数据流向,并且API屏蔽了所有的这些细节,用户只需要几个函数就可以建立客户端到服务器之间的通信。

------------------------------------------------------------------------------------------------------------------------------------

2.  传输层协议

因为socket衔接的是应用层和传输层,所以为了编写健壮的客户服务器程序以及便于后面使用netstat等命令进行调试,熟悉传输层协议的细节是非常有必要的。Socket最长用的传输层协议是TCP,UDP,以及最新出现的SCTP,下面将分别进行介绍。

TCP协议

TCP协议(Transimmision Control Protocol),即传输控制协议是一种复杂的,面向连接的,可靠的字节流协议。应用层协议中的:SMTP(邮件传输协议),Telnet(远程登陆协议),SSH(安全的远程登录),FTP, HTTP, NNTP(网络新闻),LPR(远程打印)等协议都是依靠TCP协议来完成。

下面我们主要分析 TCP的连接建立,释放和TIME_WAIT状态。

TCP三次,四次握手:

 下面我们先看一下TCP连接的分组交换图:

Unix 网络编程(二)- 你需要知道的传输层协议实现细节。_第1张图片

在这幅图里面,包含了整个TCP传输的过程,包括:连接的建立,见三次握手部分;数据传输部分;连接的释放,见四次握手部分。

TCP连接的建立,三次握手:

1) 从上图中可以看到,服务器首先通过调用socket, bind 和 listen这三个函数进行监听,这里称为被动打开。这个时候我们可以查看(一)中server的代码,查看这三个函数的位置和用法。 之后,客户端如果想建立连接会调用socket以及connect函数(建议翻看上一节中客户端的代码),这时候TCP会发送一个SYN码用于标识这个请求,同时会发送MSS(maximum segment size, 最大分节大小)码,MSS用来告诉服务器每次它能接受的最大的TCP流数据量,从而防止数据到了还要进行分片从组。这里稍微介绍一下MSS,MSS用16位进行表示,所以MSS的最大值为65535. MSS 一般取1460 为以太网MTU减去TCP和IP的首部20(即: 1460)。而这里的MSS取值536,也是一个特殊值,他是IPV4最小重组缓冲区字节数576-20-20;

2) 服务器在收到建立连接请求之后,会发送消息给客户端,这些消息内容包括:自己的SYN, 对于客户端的SYN的验证ACK=J+1,以及自己的MSS.

3) 客户端在收到服务器的消息并验证无误之后认为连接建立,成为 Established状态,并将服务器需要验证的字节码ACK发送过去,服务器收到之后也便建立了连接。

至此经过三次握手建立了连接,下面便开始数据传输。

TCP数据传送:

连接建立之后,客户端会调用read()函数,开启阻塞读模式,这时候TCP会将请求发送给服务器,服务器一般会先发送要求客户端进行验证的ACK但是有的时候会和应答的数据一起发送。

TCP连接的中断:四次挥手

如果一端需要断开连接,这里以客户端断开连接为例,他调用close()函数执行关闭状态,然后TCP会向服务器端发送FIN码进行验证,服务器端收到之后会发送验证ACK同时一段时间之后服务器端也会发送一个FIN码请求客户端进行验证。客户端在收到验证码之后处于TIME_WAIT状态,这是一个很重要的状态,可保证TCP传输的可靠性,后面我们将进行研究。同时客户端向服务器端发送ACK,服务器端收到验证无误之后便关闭整个流。

TCP的TIME_WAIT状态

TIME_WAIT状态到底是什么呢? 在我看来他是TCP协议给关闭发起端设置的一个状态,这个状态持续的时间一般是2*MSL,(这个MSL称为最长分节生命期),在这个状态中,如果关闭请求的任何一个环节出现超时,有可能是由于链路状态造成的,那么为了顺利的关闭连接,TIME_WAIT的存在是很重要的。这就是它存在的一个理由:

“可靠的实现TCP全双工连级的终止”;

TIME_WAIT还有一个重要的功能就是:

“允许老的重复分节在网络中消逝”;

因为对处于TIME_WAIT阶段的连接,不会有新的相同ip和端口的连接在建立,那么在TIME_WAIT时间以后之前的分组会被丢弃,从而不会干扰后面的连接。

UDP协议

UDP(User Datagram Protocal),即用户数据报协议是一个相对简单的,不可靠的非连接的协议。因为没有建立连接的过程,所以客户端可以使用相同的socket发送数据包给不同的udp服务器,一个udp服务器也可以使用相同的socket接受来自不同客户端的请求。

因为相对简单,所以这里并不做过多的关于udp的介绍,等到后面有关于udp套接字的事例时再做介绍。

SCTP协议

SCTP(Stream Control Transmission Protocol ),即流控制传输协议,具有TCP协议相同的功能,提供可靠性,排序,流量控制以及全双工的数据传送。作为一种较新出现的传输层协议它具有许多特性,下面我们将SCTP和TCP进行比较来说明这些特性:

SCTP的特性

1) 相比于TCP的一对一的面向连接,SCTP提供的是多宿主的,单个SCTP端点支持多个IP地址 。这样的好处就是两个端点之间,如果一条链路出现了问题,那么可以切换到另外一条链路,所以可以有效的提高健壮性。

2) 相对于TCP的单字节流,SCTP两端之间存在多个流,每个流单可靠的传输,其中一个流消息的丢失不会影响其它的流,这样也提供很好的健壮性。

 同时与TCP建立连接的三次握手不同,SCTP建立连接需要四次握手,而释放连接需要三次握手:下面先看一下SCTP关联中的分组交换:

Unix 网络编程(二)- 你需要知道的传输层协议实现细节。_第2张图片

SCTP的四路握手

       建立一个SCTP关联时需要四路握手,如上图所示。首先

1) 和tcp建立关联一样,服务器通过调用socket, bind以及listen函数进行监听,之后如果客户端需要和服务器建立连接那么会调用connect函数,然后SCTP会发送INIT(Ta,j)消息(初始化),该消息会告知服务器自己的IP地址,验证标记 (Ta),以及初始序列号(J).  Ta的作用是,可以保证识别出服务器发往自己的字节流,因为这里要求每一次通信服务器都要向客户端发送的字节流中加入这个标识,同理服务器也要发送自己的验证标记,在第二个步骤中会提到。初始序列号(J)的作用是承载用户数据的DATA块的起始序列号

2) 服务器在接收到连接请求之后会发送一个INIT ACK消息来确认用户的INIT消息,消息中包含TZ(验证标记), K(初始序列号),以及cookie. 这里的cookie保存的是服务器用于设置此次关联所需的所有状态,目的是为了减少服务器SCTP栈需要维护的状态信息。

3) 客户端在收到消息之后通过发送COOKIE ECHO来回射服务器的状态cookie。在这个过程里面,客户端还有可能绑定用户的数据。

4) 服务器之后对用户回射的COOKIE进行验证,并将验证结果发送给客户端,如果验证准确那么连接便建立起来。同时服务器还会将数据块一并传给客户端,而且还会发送一个SACK,目前并不清楚SACK设置的目的,但是在数据发送阶段都会携带这个SACK,大概是用于标识现在正处于数据交换的过程吧。

连接建立之后便开始发送数据,注意每一个消息中都会包含SACK。

关联终止

这里面关联终止倒显得简单了,客户端执行close函数,SCTP发送SHUTDOWN消息给服务器,

服务器对此消息进行确认发送SHUTDOWN ACK,

之后客户端再将确认信息进行验证。

这里客户端没有TIME_WAIT等状态是因为,在协议的设计上,SCTP每次都会有验证标记(这里的Ta/Tz等,所以在消息的确定性上有了保证吗,就不需要额外的状态进行保证了。

--------------------------------------------------------------------------------------------------------------------------------

3.影响缓冲区大小的一些限制

下面介绍一些常用的限制条件:

(1) IPV4因为总长度只有16位,所以数据报的最大长度是65535字节,报头占20字节,IPV6最大大小是65575字节

(2) 一般硬件会有一个MTU,以太网协议的MTU是1500字节。另有一些链路是可以人为自己设置的。IPV4要求的最小链路MTU是68字节。IPV6要求的最小链路MTU是1208字节。

(3) 当IP数据报超过路径MTU时会执行分片操作,这里ip协议中的DF位应该是0.如果置1的话,不会分片。当路由器收到这样一个大于MTU的数据包时会产生一个ICMPV4的"destination unreachable, fragmentation needed but DF bit set"的消息。

(4) TCP有一个最大分校大小(MSS),上面在介绍我tcp连接建立的时候已经讲过。

至此我们讨论了在socket编程中需要了解的一些运输层协议的理论知识,对于这些知识的掌握有利于我们分析和写出健壮性的网络编程,下面我们将正式进入socket api的学习阶段。

2015/1/31 于南京 如需转载请标注出处谢谢: http://blog.csdn.net/michael_kong_nju/article/details/43344067

你可能感兴趣的:(unix,socket,网络编程)