网络编程书籍讲义第七讲.ppt

上传人:本田雅阁 文档编号:3226372 上传时间:2019-08-02 格式:PPT 页数:54 大小:946.01KB
返回 下载 相关 举报
网络编程书籍讲义第七讲.ppt_第1页
第1页 / 共54页
网络编程书籍讲义第七讲.ppt_第2页
第2页 / 共54页
网络编程书籍讲义第七讲.ppt_第3页
第3页 / 共54页
网络编程书籍讲义第七讲.ppt_第4页
第4页 / 共54页
网络编程书籍讲义第七讲.ppt_第5页
第5页 / 共54页
点击查看更多>>
资源描述

《网络编程书籍讲义第七讲.ppt》由会员分享,可在线阅读,更多相关《网络编程书籍讲义第七讲.ppt(54页珍藏版)》请在三一文库上搜索。

1、第1页,人民邮电出版社,第7章 网络程序设计入门,第2页,7.1 有关网络程序设计的一些概念,7.1.1 什么是网络应用软件? 人们常将应用软件分为单机版的和网络版,单机版应用软件就是一般程序设计语言教科书重点讲述的,在单机环境下使用的软件,这种软件的特点是结构简单,易于学习与设计。另一种应用软件是需要安装在网络环境中使用的,网络中不同主机上安装的软件需要进行相互通信才能完成其功能,这就是网络应用软件,这种软件的特点是结构较为复杂,软件设计者不但要掌握程序设计的一般知识,还要掌握网络系统的有关知识与理解网络通信协议。由于计算机网络的广泛应用,目前软件开发的主流是设计这类网络环境下的应用软件。,

2、第3页,7.1 有关网络程序设计的一些概念,7.1.2 网络中两个通信程序如何相互识别对方 在操作系统中我们知道,同一个系统中不同的两个进程间进行通信时,通过系统分配的进程号(process ID)就可以惟一标识一个进程,也就是说两个相互通信的进程,只要知道知识对方的进程号就可以进行通信。而网络情况下进程间的通信问题,就要复杂得多,不能只简单的用进程号来标识不同的进程。首先要解决如何识别网络中不同的主机问题,其次因为各个主机系统中都独立地进行进程号分配,并且不同系统中进程号的产生与分配策略也不同,所以在网络环境中不能再通过进程号来简单的识别两个相互通信的进程了。 那么,在网络环境中,两个相互通

3、信的进程,如何识别对方呢?,第4页,7.1 有关网络程序设计的一些概念,7.1.2 网络中两个通信程序如何相互识别对方 在网络中为了标识通信的进程,首先要标识网络中进程所在的主机,其次要标识主机上不同的进程。关于网络环境下不同主机的识别问题,在讲传输层协议时,我们知道为了完成端到端进程之间的通信,在互联网中使用IP地址来标识不同的主机。关于主机上不同的进程问题,在前面讲端口号时已经说明过,在网络协议中使用端口号来标识主机上的不同进程。还有一个问题,就是前面我们讨论的都是在TCP/IP协议下,端到端进程的标识问题,其实网络应用程序由于不同的主机可能使用不同的网络协议,其工作方式不同,地址的表示格

4、式也不同,因此,网络中进程的通信还要解决多种协议的识别问题。这样为了惟一的标识网络中通信的一个进程(即通信的某一方)就要使用一个如下的三元组: (本地协议,本地IP地址,本地端口号),第5页,7.1 有关网络程序设计的一些概念,7.1.2 网络中两个通信程序如何相互识别对方 这样一个三元组由于它只指定了通信时一条连接的半个部分,即通信的一方,所以叫一个半相关(half-association)。如果要完整的表示网络中进行通信的两个进程,那么就要使用一个如下结构的六元组: (本地协议,本地地址,本地端口号,远地协议,远地地址,远地端口号),第6页,7.1 有关网络程序设计的一些概念,7.1.2

5、网络中两个通信程序如何相互识别对方 下面我们仔细分析一下这样一个六元组,在互联网中通信的两台主机在网络层都只能使用IP协议,但在网络层之上可以选择使用TCP协议或UDP协议。这样就可能得到以下四种类型的相关六元组: (本地TCP协议,本地IP地址,本地端口号,远程TCP协议,远程IP地址,远程端口号) (本地UDP协议,本地IP地址,本地端口号,远程UDP协议,远程IP地址,远程端口号) (本地TCP协议,本地IP地址,本地端口号,远程UDP协议,远程IP地址,远程端口号) (本地UDP协议,本地IP地址,本地端口号,远程TCP协议,远程IP地址,远程端口号),第7页,7.1 有关网络程序设计

6、的一些概念,7.1.2 网络中两个通信程序如何相互识别对方 如果通信的两端使用不同的协议,即上面第3和第4种情况,根据前面我们所学的知识,由于TCP协议和UDP协议使用的协议格式大不相同,因此通信时双方在传输层不能相互识别对方送来的数据,也就不可能进行正常的通信,因此上面说的后两种情况是不存在的。换句话说,通信的两个进程在端到端的传输层只能使用相同的协议,因此一个完整的主机间的通信,可以简单的用一个五元组来标识通信的两个进程: (协议,本地IP地址,本地端口号,远程IP地址,远程端口号) 综上所述,这样一个五元组可以惟一标识网络中两个通信的进程或一条连接,因此叫做一个相关(associatio

7、n)。为了在进程间进行正确的通信,只有两个协议相同的半相关才能组合成一个可用的相关。,第8页,7.1 有关网络程序设计的一些概念,7.1.3 Windows Sockets介绍 1. Windows Sockets的概念 Windows Sockets顾名思义,它就是在Windows环境下使用的一套网络编程机制(或规范),常常简称为Winsock。该规范是在上个世纪90年代初制定的。这套规范是在Windows操作系统下得到广泛应用的、开放的、支持多种协议的网络编程接口。该规范从1991年的1.0版到1997年的2.2.1版,经过不断完善并在Intel、Microsoft、Sun、SGI、Inf

8、ormix、Novell等公司的大力支持下,现在已经成为Windows环境下网络编程事实上的标准。,第9页,7.1 有关网络程序设计的一些概念,7.1.3 Windows Sockets介绍 2. Windows Sockets的来源 Sockets本来是UNIX操作系统下流行的一种网络编程接口(API),它是1983年在Berkeley(加州大学伯克利分校)4.2 BSD操作系统中被首先引入的,因此被称为“Berkeley Socket API”。在刚开始时它只支持TCP/IP协议,后来在4.3 BSD操作系统中增加了对国际标准化组织ISO制定的开放系统互联参考模型OSI网络协议的支持。由于

9、BSD操作系统做为一种主流的UNIX操作系统被广泛使用,因此“Berkeley Socket API”这个模型就成了TCP/IP网络的编程接口标准。从4.2 BSD开始,Berkeley套接口API在不断地完善和发展,一直到1995年的4.4 BSD-Lite2为止。 Windows网络应用程序编程接口Windows Sockets API就是在1991年根据4.3 BSD操作系统的“Berkeley Socket API”制定的。,第10页,7.1 有关网络程序设计的一些概念,7.1.3 Windows Sockets介绍 3. Windows Sockets的版本 目前常用的Winsock

10、有两个版本:一个是16位的Winsock 1.1,由动态链接库WINSOCK.DLL提供支持;另一个是32位的Winsock 2.2,由动态链接库WSOCK32.DLL提供支持。前者主要用在Windows早期的版本中,如Windows 95等版本中,后者主要用在Windows2000和Windows XP等版本中。另外说明一下,本书所讲的Windows下TCP/IP编程接口为Winsock 2.2,与低版本的Winsock 1.1相比它主要是扩充了对其它协议(如IPX、NETBIOS等)的支持。由于Winsock 2.2与Winsock 1.1规格中所有的函数调用完全兼容,所以低版本的Wins

11、ock应用程序在Winsock 2库加载的情况下也能够正常运行。,第11页,7.1 有关网络程序设计的一些概念,7.1.3 Windows Sockets介绍 4. Winsock API函数的分类 在Winsock规范中把Winsock API函数集分为与BSD Socket(用在UNIX中)相兼容的基本函数、网络数据信息检索函数和Windows专用扩展函数三类。由此可以看出,Winsock来源于BSD Socket API,但它又根据Windows操作系统的特点进行了扩充。 因此,Winsock规范的核心内容是符合Berkeley Socket风格的库函数,但为了使程序员能充分地利用Win

12、dows消息驱动机制进行编程,也定义开发了一组针对Windows的扩展库函数。这份规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。,第12页,7.1 有关网络程序设计的一些概念,7.1.3 Windows Sockets介绍 5. Windows Sockets对多线程的支持 Windows Sockets支持多线程的Windows进程。一个进程可以包含一个(在Windows 3.1非多线程版本中,一个任务对应了一个仅具有单个线程的进程)或多个同时执行的线程。,第13页,7.1 有关网络程序设计的一些概念,7.1.4 套接口的概念 Windows Soc

13、kets API依靠套接口(Socket)进行通讯,那么什么是套接口(Socket)呢?对于初学网络程序设计的人们来说,套接口是一个既抽象且又很重要的概念。因此要注意对于套接口这个概念的理解与体会。 套接口可以看成是两个网络应用程序进行通信时,各自通信连接中的一个端点,这个端点是一个逻辑上的概念。通信时其中的一个网络应用程序将要传输的一段信息写入它所在主机的Socket中,该Socket通过与网络接口卡(Network Interface Cards, NIC)相连的传输介质将这段信息发送到另外一台主机的Socket中,使这段信息能传送到其他程序中,如图7-1所示。,第14页,7.1 有关网络

14、程序设计的一些概念,第15页,7.1 有关网络程序设计的一些概念,为了满足不同的通信程序对通信质量和性能的要求,一般的网络系统提供了三种不同类型的套接口,以供用户在设计网络应用程序时根据不同的要求来选择。这三种套接口分别是: 流式套接口(SOCK_STREAM):它提供了一种可靠的、面向连接的双向数据传输服务。实现了数据的无差错、无重复地发送。内设流量控制,被传输的数据看作是无记录边界的字节流。在TCP/IP协议族中,使用TCP协议来实现字节流的传输,当用户想要发送大批量的数据,或者对数据的传输有较高的要求时使用流式套接口。 ,第16页,7.1 有关网络程序设计的一些概念, 数据报套接口(SO

15、CK_DGRAM):它提供了一种无连接、不可靠的双向数据传输服务。数据包以独立的包形式被发送,并且保留了记录边界,不提供可靠性保证。数据在传输过程中可能会丢失或重复,并且不能保证在接收端数据按发送顺序接收。在TCP/IP协议族,使用UDP协议(Winsock 2也支持其它的协议)来实现数据报套接口。在同一台计算机上或负载较轻的LAN上,因为出现差错的可能性较小,可以使用数据报套接口进行数据传输,这样通信的质量可以得到保证,并且通信的效率较高。另外,在前面的章节中我们已经说过,UDP还可以实现广播通信。 原始套接口(SOCK_RAW):该套接口允许对较低层协议(如IP或ICMP)进行直接访问。常

16、用于检验新的网络协议实现,也可用于测试新配置或安装的网络设备。要说明的是Windows Sockets规范并没有规定Windows Sockets DLL必须支持原始套接口。 套接口在编程时对于用户来说是可见的,应用程序一般仅在同一类型的套接口间通讯。不过只要底层的通讯协议允许,不同类型的套接口间也照样可以通讯(一般很少这样做)。,第17页,7.1 有关网络程序设计的一些概念,7.1.5 套接口编程原理 为了便于读者理解套接口的工作机制,我们举一个日常生活中的例子。电话服务系统与面向连接的套接口机制非常相似。电信局提供的普通电话服务(比如114提供查号服务功能)类似于服务器(Server),普

17、通电话用户类似于客户(Client)。比如有客户要查某单位的电话号码,于是就开始了一次查号的通信过程,该过程可以分为以下几个阶段(对于下面提到的函数,只要知道其大概的含义就可以了,在后面将要专门介绍这些函数的用法): 首先电信局必须要有一个电话总机,相当于套接口通信机制中提供服务的服务器,在Socket中通过调用socket()函数来开启一个服务,也就是说要创建一个提供服务的套接口。,第18页,7.1 有关网络程序设计的一些概念,7.1.5 套接口编程原理 电信局必须给电话总机分配一个号码(如114,意思是查号服务的号码是114),以便用户通过拨该号码得到电话服务,同时接入该电信局的用户必须知

18、道该总机的号码。同样,在服务器端也要为启动的服务指定一端口号,并且要连接到该服务器的客户必须要知道该端口号(如客户要查询电话号码时,必须要知道查号台是114)。这在Socket中通过调用bind()函数把一个端口与一个特写服务联系起来(叫绑定)。 电信局的114查号台下会开设一些自动服务的分机,但是它们的数量是有限的。总机开通后就一直在监听(listen)用户的拨号,用户拨打114时,可能拨通,得到服务;也可能拨不通,就会听到忙音。同样地,我们在建立一个socket服务时,也会调用listen()函数来监听客户的请求。,第19页,7.1 有关网络程序设计的一些概念,7.1.5 套接口编程原理

19、对于用户来说,如果知道电信局的查号号码,在想得到查号服务时就可以拨打114,请求得到电信局的服务,这相当于在客户端要进行的操作。同样在Socket通信中,在客户端也要先用socket()函数建立一个请求服务的套接口,然后调connect()函数进行连接与服务器进行连接。当然和电话一样,用户可能连通得到服务,也可连不通,请求的服务失败。 电信局的总机接受了某用户拨打的电话后,负责把用户与一个分机连通,而总机本身则又回到等待的状态,以等待其它客户的请求。在Socket通信中这个相当于服务器调用accept()函数进入监听处理过程。服务器端建立一个新的套接口来对此连接提供服务,而原先的套接口则又回到

20、监听状态,等待新客户的请求。服务器与客户之间通信时,使用recv()函数接收套接口数据,使用send()函数向套接口发送数据。,第20页,7.1 有关网络程序设计的一些概念,7.1.5 套接口编程原理 服务完成后,挂上电话,线路断开,一次服务过程结束,不然的话则该线路一直被占用,浪费了通信资源。在服务器和客户之间,最后也要使用closesocket()函数关闭套接口,释放该套接口上的有关资源,这可以由通信的任何一方或双方同时提出。 上面提到的Winsock API函数是使用套接口编程时所使用的最基本的一些函数,几乎每一个使用套接口编程的网络应用程序都要使用这些函数,后面我们将详细介绍这些函数的

21、功能和用法。 用户编写网络应用程序时,错误的出现是不能可避免的,因此对错误的检查和控制是至关重要的,一个好的或者说成功的Winsock应用程序应该尽可能地检测和处理各种错误。由于下面我们接触到的所有函数都有调用成功与不成功两种情况,因此,我们打算先为大家介绍Winsock中错误检查和控制的方法。,第21页,7.1 有关网络程序设计的一些概念,7.1.5 套接口编程原理 对Winsock函数来说,返回错误是非常常见的。与大多数系统调用类似,Winsock函数发生的错误也有两种。多数情况下发生的错误都是无关紧要的,通信仍可在套接口上进行,但有些错误是致命性的,使应用程序无法继续执行。 不成功的Wi

22、nsock函数调用返回的最常见的值是宏定义SOCKET_ERROR,在Winsock的头文件中(如Winsock 2.h),它的数值是-1。实际上,如果调用一个Winsock函数时,发生了错误,我们应该进一步使用WSAGetLastError()函数,以获得对这一错误详细的说明,该函数的使用非常简单,它的格式如下: int WSAGetLastError ( void );,第22页,7.1 有关网络程序设计的一些概念,7.1.6 网络字节顺序 不同的主机对字节值的存储顺序不同。在存储由多个字节组成的一个字时,有的计算机在起始地址处存放低整数的低序号字节,这种存储格式叫“小序在前”(littl

23、e-endian)。而有的计算机在起始地址处存放整数的高序号字节,这种存储格式叫“大序在前”(big-endian)。 每种计算机究竟采用那种字节存储顺序由各自的设计决定,如Windows系列的操作系统使用的是小序在前的存储方式,而Sun OS和Solaris等采用的大序在前的存储方式。 在计算机中,TCP/IP协议使用的16位整数(如端口号)和32位整数(如IP地址)是按计算机各自的“主机字节”(host-byte)来表示的。 在网络中,为了保证数据的正确性,在网络通信协议中必须指定网络字节顺序。如果在网络中使用IP地址和端口号,按“互联网联网标准”的要求,指定的多字节值必须用“大序在前”的

24、形式来表示,一般称之为“网络字节”(network-byte)顺序。,第23页,7.1 有关网络程序设计的一些概念,7.1.6 网络字节顺序 在Winsock中有一系列的函数可用于多字节数的转换,把它们从主机字节顺序转换成网络字节顺序,反之亦然。下面四个API函数便将一个数从主机字节顺序转换成网络字节顺序: htonl():参数是主机字节顺序的一个4字节数,函数返回网络字节顺序的数。 WSAHtonl)():参数是主机字节顺序的一个4字节数,函数返回网络字节顺序的数。 htons():参数是主机字节顺序的一个2字节数,函数返回网络字节顺序的数。 WSAHtons():参数是主机字节顺序的一个2

25、字节数,函数返回网络字节顺序的数。,第24页,7.2 网络程序工作模型,7.2.1 网络程序要考虑的几个问题 网络程序与单机环境下运行的程序有较大的区别,程序员在设计网络程序时应该了解与注意这些问题,才能设计出高质量的网络应用程序。 1. 并发环境下的网络编程 我们常用的操作系统Windows和Linux等都支持多进程(或线程)的并发执行,多进程应用程序,由于涉及到资源共享、进程之间的同步等问题,其编程要比单进程环境复杂得多。,第25页,7.2 网络程序工作模型,7.2.1 网络程序要考虑的几个问题 在多进程编程环境中,使用的系统调用或函数必须是可重入的。哪些系统调用或系统函数是可重入的,这在

26、不同的系统中是不同的。一般的系统都会对其进行详细说明。在多线程应用中,对系统调用或函数的使用有很多限制,因此在编程时应该注意的是对于那些不可重入的调用或函数,系统如果不提供多线程安全的版本,则应用编程人员需要避免使用或自己编写相应的函数。例如在Solaris操作系统中,在一个线程中关闭另一个线程下在使用的网络端口会导致应用程序“死掉”,而这种情况在Digiatl Unix中则不会出现。,第26页,7.2 网络程序工作模型,7.2.1 网络程序要考虑的几个问题 2. 异构环境下的网络编程 在网络中,通常在异构环境下进行程序之间的通信。因此网络程序必须要考虑不同平台之间的异构性,这种异构性主要表现

27、在不同平台上数据表示格式的差异性上,这种差异性主要表现在以下几个方面: (1)字节顺序 即前一节介绍的网络字节顺序。作为程序员必须要清楚所用系统的字节顺序。在网络应用程序中,传输的通信数据主要包含两个部分,一部分是分组中协议的首部,另一个部分是程序中要使用的数据。这两个部分数据都要考虑字节顺序问题。 (2)字的长度 不同的系统中,对于相同的数据类型可能用不同的长度表示。例如在64位的操作系统中和32位的操作系统中对于long int类型的长度表示是不一样的。,第27页,7.2 网络程序工作模型,7.2.1 网络程序要考虑的几个问题 (3)字节定界问题 不同的平台上给结构(struct)或联合(

28、union)打包的方式也是不同的,这取决于所用数据类型的位数及机器的定界限制。一般情况下,操作系统在分配内存时,数据结构以4字节定界。例如,在很多系统中,默认情况下结构structchar a; int b的长度为8而不是5。只有在1字节定界的情况下其长度才为5。 对于字节的定界问题,对于具有相同字节顺序的平台,通信双方均经单字节定界,对于具不同字节顺序的平台间通信,可以显式的定义其格式(位数、字节顺序类型),也可以将需要发送信息的结构变换成一种统一的格式(如转换成一个字符数组),到达接收方后再执行相反的转换过程。对于数据结构中有比特变量的情况,处理起来比较复杂,因此建议在实际网络编程中,尽量

29、不要使用比特类型的变量。 另外要注意,在很多网络协议的设计中,常常需要填充一些无用的字节,以满足4字节定位的要求,从而简化协议的实现。,第28页,7.2 网络程序工作模型,7.2.1 网络程序要考虑的几个问题 3. 阻塞与非阻塞通信 在网络编程中,可以将通信分为阻塞与非阻塞两种模式。在进行网络编程时,选择通信模式也是一件很重要的事情。对于不同的协议,阻塞通信和非阻塞通信有不同的表现。以套接口编程为例,在阻塞模式下,利用TCP协议发送一个报文时,如果低层协议没有可用空间来存放用户数据,则应用进程将阻塞(即进行等待),直到有可用的空间。而在非阻塞模式下,调用将直接返回而不需要等待。 在应用进程调用

30、接收函数接收报文时,如果是在阻塞模式下,若没有到达的数据,则调用将一直阻塞直到有数据到达或出错为止,而在非阻塞模式下,将直接返回而不需要等待。 对于UDP协议而言,因为UDP没有发送缓冲,所有UDP协议即使在阻塞模式下也不会发生阻塞。,第29页,7.2 网络程序工作模型,7.2.1 网络程序要考虑的几个问题 对于面向连接的协议,在连接建立阶段,阻塞与非阻塞也表现不一样。在阻塞模式下,如果没有连接请求到达,则等待连接调用将阻塞直到有请求到达;但在非阻塞模式下,如果没有连接请求到达,等待连接调用将直接返回。 在连接建立阶段,不管是阻塞还是非阻塞模式,发起连接请求的一方总是会使调用它的进程阻塞,阻塞

31、间隔最少等于到达服务器的一次往返时间。 通信模式对应用程序的设计方法也有直接的影响。在非阻塞模式下,应用程序不断地轮询查看是否有数据到达或有连接请求到达。这种轮询方式比其他技术耗费更多的CPU时间,因此要尽量避免使用这种技术。而在阻塞模式下。不存在这一问题。但是阻塞模式的缺点是进程在执行I/O操作时将被阻塞而不能执行其他的工作,因此在单进程或单线程应用中不能使用这种模式。在多线程应用中比较适合采用阻塞模式,一个线程被阻塞不影响其他线程的工作。,第30页,7.2 网络程序工作模型,7.2.1 网络程序要考虑的几个问题 另外,通信模式的选择与操作系统的类型也有关系,例如在非抢占式的Windows操

32、作系统中,应用程序应尽量不要使用阻塞模式工作。 4. 服务类型的选择 从通信的角度来说,网络协议栈中的各层所提供的服务可以分为两大类:面向连接服务与无连接服务。 对于面向连接的通信,由于两个对等实体为进行数据通信要建立连接。面向连接服务的要求是:在数据交换之前,必须先建立连接;当数据交换结束后,则应该终止这个连接。例如,我们前面已经介绍过,在TCP/IP协议栈中,TCP协议提供的就是这种面向连接的服务。对于在一段时间间隔内,要向同一目的地发送许多提出报文的情况,就适合使用面向连接的服务。,第31页,7.2 网络程序工作模型,7.2.2 网络程序工作模型 使用TCP/IP协议的网络,其协议核心内

33、容在层次结构的低三层,即网络接口层、IP层和传输层,而这三层的功能一般是由操作系统内核来实现的。操作系统为了保证其安全性和可靠性,其内核部分一般是不能由用户直接进行操作和使用的,但这并不等于说用户就不能在其应用程序中使用TCP/IP了。那么用户如何在网络应用程序中使用中TCP/IP协议呢? 很显然,同操作系统提供的其它功能一样,用户虽然不能直接使用操作系统的内核,但操作系统给用户提供了使用内核功能的接口,用户就可以通过系统功能调用等方式间接的使用这些功能。同样,用户可以通过系统提供的网络应用程序编程接口使用TCP/IP。如图7-2所示的是两台主机通过网络编程接口,进程间进行通信的原理图。,第3

34、2页,7.2 网络程序工作模型,7.2.2 网络程序工作模型,网络应用程序编程接口,客户应用程序,服务器应用程序,图7-2 TCP/IP应用程序工作模型,TCP/IP核心协议,物理传输介质,物理传输介质,TCP/IP核心协议,网络应用程序编程接口,第33页,7.2 网络程序工作模型,7.2.2 网络程序工作模型 图7-2所示的是使用TCP/IP协议网络的典型应用方式,即客户-服务器模式(见7.3节内容)。除物理传输介质外,其它的各对等层之间都要进行虚通信,所以图7-2中用虚线表示。从图7-2中还可以看出,程序员不直接面对网络协议进行编程,但它通过网络应用程序编程接口间接的使用了网络协议各层提供

35、的服务。这样做的好处除了可以保证系统的安全可靠性外,还可以简化网络应用程序的开发。 通过图7-2用户还要明白一个问题,网络程序设计其实是使用系统提供的网络协议完成用户程序的功能,也就是说在我们的网络应用程序中如何去使用网络协议提供的服务,而不是让用户去实现网络协议各层的功能。 综上所述,用户在进行TCP/IP程序设计时,最关键的问题是要熟悉所用平台提供的网络编程界面(网络编程API)。在Windows环境下的网络应用程序编程接口叫Windows Sockets,其Socket在中文资料中的译名有好几种,如“接口”、“插口”、“套接口”等,本书中使用各种资料中较为常用的“套接口”。,第34页,7

36、.3 一个简单的客户机/服务器程序,编写一个服务器端程序,该程序的功能是只要有客户提出请求连接,服务器就接受连接,在连接成功后,服务器就向请求连接的客户发送“Hello! I am a server.”的信息。如果服务器向客户机正确发送了上述信息,则在服务器端显示客户机的IP地址和端口号,并输出所发送的字节数。 编写一个客户端程序,该程序的功能是向服务器发出连接请求,在连接成功后,接收并显示从服务器收到的信息。 该程序中通信协议使用的是面向连接的TCP协议(SOCK_STREAM,该内容在第8章中要详细介绍,在这里读者只要了解该知识就可以了)。,第35页,7.4 Winsock中建立连接的函数

37、及其应用,7.4.1 加载协议栈(WSAStartup) 在使用Winsock API编制网络应用程序时,要使用大量系统已经实现的网络功能函数,在调用任何一个Winsock API函数之前,都必须先检查协议栈的安装情况,也就是检查系统中是否有Windows Sockets的实现库。通过调用WSAStartup()函数便可检测系统中有没有一个或多个Windows Sockets的实现,本函数必须是应用程序或DLL调用的第一个Windows Sockets函数,它允许应用程序或DLL指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节。应用程序或DL

38、L只能在一次成功的WSAStartup()调用之后,才能进一步调用其它的Winsock API函数。,第36页,7.4 Winsock中建立连接的函数及其应用,7.4.1 加载协议栈(WSAStartup) 1. 函数格式 int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData ); 2. 函数参数说明 WSAStartup函数有两个参数,其含义是:,第37页,7.4 Winsock中建立连接的函数及其应用,7.4.1 加载协议栈(WSAStartup) wVersionRequested:第一个参数是一个WORD型(双字节型)数

39、值,它指定准备在应用程序中要使用的Winsock库的版本号。其中用高位字节指定副版本,而低位字节则指定主版本。就目前的常用的Win32平台而言,Winsock 2库的版本是2.2(在早期的Windows 95等版本中为Winsock 1.1)。如果需要加载Winsock 2.2版,则应指定这个值为(0x0202),也可使用宏MAKEWORD(X,Y),其中X为高位字节,Y为低位字节,如MAKEWORD(2,2)。 LpWSAData:第二个参数是一个指向WSADATA结构的指针。当该函数被调用时,它返回关于Windows Sockets实现的详细信息,该结构的定义如下:,第38页,7.4 Wi

40、nsock中建立连接的函数及其应用,7.4.1 加载协议栈(WSAStartup) typedef struct WSAData WORD wVersion; WORD wHighVersion; Char szDescriptionWSADESCRIPTION_LEN+1; Char szSystemStatusWSASYS_STATUS_LEN+1; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; WSADATA,FAR *LPWSADATA;,第39页,7.4 Winsock中建

41、立连接的函数及其应用,7.4.1 加载协议栈(WSAStartup) 该结构各字段的含义说明如下: wVersion:调用者希望使用的Winsock版本号。 wHighVersion:加载的Winsock库所支持的最高Winsock版本号,通常和第1个分量wVersion的值相同。 szDescription:系统加载的Winsock库的说明字符串,如“Winsock 2.0”。 szSystemStatus:系统状态或配置信息的说明字符串。 iMaxSockets:套接口的最大编号(该字段被Winsock 2或其后的版本所忽略)。 iMaxUdpDg:UDP数据报的最大容量(该字段被Wins

42、ock 2或其后的版本所忽略)。,第40页,7.4 Winsock中建立连接的函数及其应用,7.4.1 加载协议栈(WSAStartup) lpVendorInfo:厂商专有信息(该字段被Winsock 2或其后的版本所忽略)。 该结构中,WSAStartup()函数返回的比较有用的信息是wVersion和wHighVersion,这是两个关于Winsock版本号的字段,其它字段则很少使用。其中最后3个字段在Winsock 2中只是为了保持与低版本的Winsock兼容性而保留的字段,实际已经不使用这3个字段了。 3. 函数返回信息 WSAStartup()函数的返回值是一个整数,如果调用成功则

43、返回0。 WSAStartup()函数调用不成功时返回如下的错误信息: WSASYSNOTREADY:在Winsock的头文件Winsock 2.h中,该错误代码定义的数值为10091,它表明加载的Winsock DLL不存在或底层的网络子系统无法使用。,第41页,7.4 Winsock中建立连接的函数及其应用,7.4.1 加载协议栈(WSAStartup) WSAVERNOTSUPPORTED:该代码的数值为10092,所需的Windows Sockets API的版本未由特定的Windows Sockets实现提供。如果由wVersion返回的版本用户不能接受,则要调用WSACleanup

44、()函数清除对Winsock的加载。 WSAEINVAL:该代码的数值为10022,说明应用程序指出的Windows Sockets版本不能被该Winsock DLL的实现所支持。 WSAEINPROGRESS:该代码的数值为10036,说明一个阻塞的Winsock调用正在进行中。 WSAEPROCLIM:该代码的数值为10067,说明已经达到了Windows Sockets实现所支持的任务数量的极限。 WSAEFAULT:该代码数值为10014,说明lpWSAData参数是一个无效的指针。,第42页,7.4 Winsock中建立连接的函数及其应用,7.4.2 创建套接口(socket或WSA

45、Socket) 应用程序在使用套接口通信之前,必须要拥有一个套接口。在Winsock中,要使用Socket()或WSASocket()函数来给一个网络应用程序创建一个套接口。 函数格式 在Winsock 1中提供的格式是: SOCKET socket( int af, int type, int protocol );,第43页,7.4 Winsock中建立连接的函数及其应用,7.4.2 创建套接口(socket或WSASocket) 在Winsock 2中提供的扩展格式是: SOCKET WSASocket( int af, int type, int protocol, LPWSAPROT

46、OCOL_INFO lpProtocolInfo, Group g, int iFlags );,第44页,7.4 Winsock中建立连接的函数及其应用,7.4.3 地址绑定(bind) 当用socket()创建了一个套接口后,该套接口还是不能直接使用的,因为它只存在于一个名字空间(地址族)中,也就是说它只确定了通信所希望使用的服务类型,并没有与该主机上提供服务的某端口联系在一起,这样的套接口可以叫未命名的套接口。bind()函数通过给一个未命名的套接口分配一个本地名字,来为套接口建立本地绑定(即把一个套接口与一个主机地址和端口号联系起来)。本函数适用于数据报或流类套接口。 函数格式 int

47、 bind( SOCKET s, const struct sockaddr FAR* name, int namelen );,第45页,7.4 Winsock中建立连接的函数及其应用,7.4.4 服务器端监听连接(listen) 当在一个服务器端程序中,用socket()函数成功创建了一个套接口,并用bind()函数和一个指定的地址关联(即绑定)在一起后,就要指示该套接口进入监听连接请求的状态,可以接收由客户端发出的连接请求,这时就要用Winsock API函数listen()。 函数格式 int listen( SOCKET s, int backlog );,第46页,7.4 Wins

48、ock中建立连接的函数及其应用,7.4.5 客户端请求连接(connect或WSAConnect) 当服务器端建立好套接口并与一个本地地址绑定后,就进入监听状态,等待客户发出连接请求,从而为客户提供服务。在客户端当套接口建立好之后,就要调用connect()函数,提出与一个服务器建立连接的请求,如果服务器接受请求,就可以在服务器的远程套接口与客户端的本地套接口之间建立一条连接。 函数格式 在Winsock 1中提供的格式是: int connect( SOCKET s, const struct sockaddr FAR* name, int namelen );,第47页,7.4 Winso

49、ck中建立连接的函数及其应用,7.4.6 服务器端接受连接(accept或WSAAccept) 在服务器端通过listen()函数调用表示服务器进入监听客户的连接请求状态,而在服务器端调用accept()函数表示可以接收来自客户端由connect()发出的连接请求,就好象是在通话过程中被叫方听到电话铃声后,提起话筒的动作,然后双方进入通话状态。 函数格式 在Winsock 1中提供的格式是: SOCKET accept( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen );,第48页,7.5 Winsock中的数据传输函数及其应用,7.5.1 有连接的数据发送(send或WSASend) 在已经建立连接的套接口上发送数据,可以使用send()或WSASend()函数。 函数格式 在Winsock 1中提供的格式是: int send( SOCKET s, const char FAR* buf, int len, int flags );,第49页,7.5 Winsock中的数据传输

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1