第1章网络应用与开发概述OverviewWinsockAPI和OSI模型WindowsSockets规范基本概念WinsockAPI和OSI模型◆Winsock是访问众多的基层网络协议的首选接口。◆Winsock是网络编程接口,而不是协议。◆它从Unix平台的Berkeley(BSD)套接字方案借鉴了许多东西,后者能访问多种网络协议。◆在Win32环境中,Winsock接口最终成为一个真正的“与协议无关”接口,尤其是在Winsock2发布之后。OSI模型为用户提供相应的界面,以便使用提供的连网功能完成数据的格式化控制两个主机间的通信链路(开放、操作和关闭)提供数据传输服务(可靠或不可靠)在两个主机之间提供一套定址/寻址机制,同时负责数据包的路由选择控制两个主机间的物理通信链路:同时还要负责对数据进行整形,以便在物理媒体上传输物理媒体负责以一系列电子信号的形式,传出数据应用层表示层会话层传输层网络层数据链路层物理层WinsockAPI与OSI模型的关系•Winsock目录(通过WSAEnumProtocols列举出来的)中的传输提供者位于OSI模型的传送层(传输层)。•也就是说,每个传输协议都会提供一种传输数据的方法;但是,它们本身又是另一个网络协议的成员,而网络协议位于网络层,因为它是为网络上各节点提供定址方法的协议。比如,UDP和TCP就是传输协议,但两者又都属于因特网协议(IP)。WinsockAPI与OSI模型的关系•WinsockAPI安装在“会话层”和“传送层”之间。Winsock提供了一种可为指定传输协议打开、计算和关闭会话的能力。•在Windows下,上面三层(应用层、呈现层和会话层)在很大程度上与用户的Winsock应用有关。•换而言之,用户的Winsock应用控制了会话的方方面面,必要时,还会根据程序的需要格式化数据。应用程序与WindowsSockets的关系•应用程序调用WindowsSockets的API实现相互之间的通讯。WindowsSockets又利用下层的网络通讯协议功能和操作系统调用实现实际的通讯工作。它们之间的关系如下图所示。应用程序1应用程序2网络编程界面,例如WindowsSockets网络通讯协议服务界面,例如TCP/IP物理通讯介质操作系统,例如Windows图1-1应用程序与WindowsSockets关系图WindowsSockets规范•20世纪90年代初,microsoft公司联合intelsun、informix、novell等几家公司共同制定了一套Windows操作系统下的网络编程接口,它就是WindowsSockets规范。•WindowsSockets规范主要提供一个与协议无关的编程接口。•WindowsSockets规范以U.C.Berkeley大学BSDUNIX中流行的Socket接口为范例定义了一套MicosoftWindows下网络编程接口。它不仅包含了人们所熟悉的BerkeleySocket风格的库函数;也包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。基本概念•套接口(套接字):是应用层到传输层的接口,一个套接字就是双方通信进程的一个端点,就是一个指向传输提供者的句柄•一条TCP连接就是由2个套接字唯一确定。•为区别不同的TCP连接和应用程序的进程,套接字被作为应用程序与TCP/IP协议交互的接口。•一个正在被使用的套接口都有它的类型和与其相关的进程。套接口存在于通讯域中TCP字节流的分离•套接字识别应用进程与TCP字节流的分离有密切关系,如下图套接字的通讯域•通讯域是为了处理一般的线程通过套接口通讯而引进的一种抽象概念。套接口通常和同一个域中的套接口交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。•WindowsSockets规范支持单一的通讯域,即Internet域。各种进程使用这个域互相之间用Internet协议族来进行通讯(WindowsSockets1.1以上的版本支持其他的域,例如WindowsSockets2)。套接字的分类•套接字可以根据通讯性质分类;这种性质对于用户是可见的。应用程序一般仅在同一类的套接字间通讯。不过只要底层的通讯协议允许,不同类型的套接口间也照样可以通讯。•用户目前至少可以使用两种套接字,即流套接字和数据报套接字。•流套接字提供了双向的,有序的,无重复并且无记录边界的数据流服务。•数据报套接字支持双向的数据流,但并不保证是可靠,有序,无重复的。也就是说,一个从数据报套接字接收信息的进程有可能发现信息重复了,或者和发出时的顺序不同。数据报套接字的一个重要特点是它保留了记录边界。对于这一特点,数据报套接口采用了与现在许多包交换网络(例如以太网)非常类似的模型。套接字网络编程原理•套接字有三种类型:流式套接口,数据报套接口及原始套接口.•流式套接字定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输.•数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错.•原始套接口允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等.•无连接服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与服务程序之间的相互作用。若使用无连接的套接字编程,程序的流程下图表示。•下页图所示无连接的套接字编程流程面向连接的套接字编程流程客户机/服务器模型•一个在建立分布式应用时最常用的范例便是客户机/服务器模型。在这种方案中客户应用程序向服务器程序请求服务。这种方式隐含了在建立客户机/服务器间通讯时的非对称性。客户机/服务器模型工作时要求有一套为客户机和服务器所共识的惯例来保证服务能够被提供(或被接受)。•这一套惯例包含了一套协议。它必须在通讯的两头都被实现。根据不同的实际情况,协议可能是对称的或是非对称的。在对称的协议中,每一方都有可能扮演主从角色;在非对称协议中,一方被不可改变地认为是主机,而另一方则是从机。一个对称协议的例子是Internet中用于终端仿真的TELNET。而非对称协议的例子是Internet中的FTP。无论具体的协议是对称的或是非对称的,当服务被提供时必然存在“客户进程”和“服务进程”。客户机/服务器模型•一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“惊醒”并且为客户提供服务-对客户的请求作出适当的反应。•这一请求/相应的过程可以简单的用下图表示。虽然基于连接的服务是设计客户机/服务器应用程序时的标准,但有些服务也是可以通过数据报套接口提供的。(C/S模型即可用流套接字也可用数据报套接字,C/S模型依双方通信进程地位来定义,而非依靠是否基于连接)客户机/服务器模型客户机请求响应进程通讯设施服务器请求响应图1-2客户机/服务器模型字节排序•针对“大头”(big-endian)和“小头”(little-endian)形式的编号,不同的计算机处理器的表示方法有所不同,这由各自的设计决定。比如,Intel86处理器上,用“小头”形式来表示多字节编号:字节的排序是从最无意义的字节到最有意义的字节。在计算机中把IP地址和端口号指定成多字节数时,这个数就按“主机字节”(host-byte)顺序来表示。•但是,如果在网络上指定IP地址和端口号,“互联网联网标准”指定多字节值必须用“大头”形式来表示(从最有意义的字节到最无意义的字节),一般称之为“网络字节”(network-byte)顺序。有一系列的函数可用于多字节数的转换,把它们从主机字节顺序转换成网络字节顺序,反之亦然。(如:0X12345678以小头形式存放在内存中存放次序为0X78、0X56、0X34、0X12)字节转换函数(主---〉网)•下面四个API函数便将一个数从主机字节顺序转换成网络字节顺序:•u_longhtonl(u_longhostlong);•intWSAHtonl(SOCKETs,u_longhostlong,u_longFAR*lpnetlong);•u_shorthtons(u_shorthostshort);•intWSAHtons(SOCKETs,u_shorthostshort,u_shortFAR*lpnetshort);函数说明•htonl和WSAHtonl的hostlong参数是按主机字节顺序的一个4字节数。htonl函数返回的数顺序是网络字节顺序,而WSAHtonl函数通过lpnetlong参数返回的数顺序是网络字节顺序。•htons和WSAHtons的hostshort参数是按主机字节顺序的一个2字节数。htons函数把这个数当作按网络字节顺序的一个2字节值返回,而WSAHtons函数通过lpnetshort参数把这个数返回字节转换函数(网---〉主)•下面这四个是前面四个函数的反向函数:它们把网络字节顺序转换成主机字节顺序:•u_longntohl(u_longnetlong);•intWSANtohl(SOCKETs,u_longnetlong,u_longFAR*lphostlong);•u_shortntohs(u_shortnetshort);•intWSANtohs(SOCKETs,u_shortnetshort,u_shortFAR*lphostshort);面向消息•对每个离散写命令来说,如果传送协议把它们(而且只有它们)当做一条独立的消息在网上传送,我们就说该协议是面向消息的。同时,还意味着接收端在接收数据时,返回的数据是发送端写入的一条离散消息。接收端不能得到更多的消息,仅此而已。•比如,在下图中,左边的工作站向右边的工作站提交了三条分别是128、64和32字节的消息。作为接收端的工作站发出三条读取命令,缓冲区是256个字节。后来的各次调用返回的分别是128、64和32个字节。第一次读取调用不会将这所有的三个数据包都返回,即使这些数据包已经收到也如此。这称为“保护消息边界”(preservingmessageboundaries)保护消息边界的例子•保护消息边界,一般出现在交换结构化数据时。网络游戏是“保护消息边界”的较好范例。每个玩家均向别的玩家发出一个带有地图信息的数据包。•这种通信后面的代码很简单:一个玩家请求一个数据包,另一个玩家又准确地从别的玩家处获得一个地图信息数据包。保护消息边界的例子无保护消息边界•无保护消息边界的协议通常称作“基于流的协议”。大家要知道“基于流的协议”这一术语常用来指代附加特性。流服务的定义是连续的数据传输;不管消息边界是否存在,接收端都会尽量地读取有效数据。•对发送端来说,意味着允许系统将原始消息分解成小消息或把几条消息积累在一起,形成一个较大的数据包。对接收端来说,则是数据一到达网络堆栈,网络堆栈就开始读取它,并将它缓存下来等候进程处理。•在进程请求处理大量数据时,系统会在不溢出为客户请求提供的缓冲区这一前提下,尽量返回更多的数据。无保护消息边界•在下图中,发送端提交了三个数据包:分别是128、64和32个字节;但是,本地系统堆栈自由地把这些数据聚合在一起,形成一个大的数据包。这种情况下,重组后的2个数据包就会一起传输。•是否将各个独立的数据包累积在一起受许多因素的影响,比如最大传输单元或Nagle算法。在TCP/IP中,在将积累起来的数据发到线上之前,Nagle算法在等候数据积累的主机中进行。在需要发送的数据积累到一定数量或预定时间已超过之前,这个主机会一直等下去。实施Nagle算法时,主机的通信方在发送主机确认之前,会等一等外出数据,主机的通信方就不必发送一个只有确认的数据包。发送小数据包不仅没有多少意义,而且还会徒增错误检查和确认,相当烦人。无保护消息边界•在接收端,网络堆栈把所有进来的数据包聚集在一起,归入既定进程。如下图所示,如果接收端执行一次256字节缓冲区的读取,系统马上就会返回224个字节。如