>> 欢迎您, 傲气雄鹰: 重登陆 | 退出 | 注册 | 资料 | 设置 | 排行 | 新贴 | 精华 | 管理 | 帮助 首页

  小榕软件实验室
  刀光雪影
  有没有对socket比较熟悉的啊,帮我介绍点eb...
发表文章 发表涂鸦
  回复数:13  点击数:215 将此页发给您的朋友        
作者 主题: 有没有对socket比较熟悉的啊,帮我介绍点ebook下载啊 回复 | 收藏 | 打印 | 篇末
帅哥哦
级别:老 站 友
威望:0
经验:0
货币:875
体力:69
来源:北京
总发帖数:353
注册日期:2001-04-26
查看 邮件 主页 QQ 消息 引用 复制 下载 

谢了啊

编辑 删除 发表时间发表于 2002-04-08.15:24:05   MSIE 5.01 Windows 98IP: 已记录
妖怪美女哟
级别:圣骑士
威望:0
经验:0
货币:877
体力:57.9
来源:211.160.56.*
总发帖数:214
注册日期:2002-03-24
查看 邮件 主页 QQ 消息 引用 复制 下载 

搜索一下,到处都有啊
----------------------------------------------------------
恶从善生,魔由佛来

编辑 删除 发表时间发表于 2002-04-08.16:13:22   MSIE 6.0 Windows 98IP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 


第一章 简介
1.1 什么是Windows Sockets规范?
    Windows Sockets规范以U.C. Berkeley大学BSD UNIX中流行的Socket接口为范例定义了一套Micosoft Windows下网络编程接口。它不仅包含了人们所熟悉的Berkeley Socket风格的库函数;也包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。
    Windows Sockets规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。此外,在一个特定版本Windows的基础上,Windows Sockets也定义了一个二进制接口(ABI),以此来保证应用Windows Sockets API的应用程序能够在任何网络软件供应商的符合Windows Sockets协议的实现上工作。因此这份规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。
    遵守这套Windows Sockets规范的网络软件,我们称之为Windows Sockets兼容的,而Windows Sockets兼容实现的提供者,我们称之为Windows Sockets提供者。一个网络软件供应商必须百分之百地实现Windows Sockets规范才能做到现Windows Sockets兼容。
    任何能够与Windows Sockets兼容实现协同工作的应用程序就被认为是具有Windows Sockets接口。我们称这种应用程序为Windows Sockets应用程序。
    Windows Sockets规范定义并记录了如何使用API与Internet协议族(IPS,通常我们指的是TCP/IP)连接,尤其要指出的是所有的Windows Sockets实现都支持流套接口和数据报套接口.
应用程序调用Windows Sockets的API实现相互之间的通讯。Windows Sockets又利用下层的网络通讯协议功能和操作系统调用实现实际的通讯工作。它们之间的关系如图1-1。

    虽然我们并不反对使用这一套API来实现另一通讯协议栈(而且我们期望在将来规范的修改中能够讨论这个问题),但这种用法已经超出了我们这一份规范所规定的范围,我们在此将不作讨论。


1.2 Bekeley套接口
    Windows Sockets规范是建立在Bekeley套接口模型上的。这个模型现在已是TCP/IP网络的标准。它提供了习惯于UNIX套接口编程的程序员极为熟悉的环境,并且简化了移植现有的基于套接口的应用程序源代码的工作。Windows Sockets API也是和4.3BSD的要求一致的。

1.3 Microsoft Windows和针对Windows的扩展
    这一套Windows Sockets API能够在所有3.0以上版本的Windows和所有Windows Scokets实现上使用,所以它不仅为Windwos Sockets实现和Windows Sockets应用程序提供了16位操作环境,而且也提供了32位操作环境。
    Windows Sockets也支持多线程的Windows进程。一个进程包含了一个或多个同时执行的线程。在Windows 3.1非多线程版本中,一个任务对应了一个仅具有单个线程的进程。而我们在本书中所提到的线程均是指在多线程Windows环境中的真正意义的线程。在非多线程环境中(例如Windows 3.0)这个术语是指Windows Sockets进程.
    Windows Sockets规范中的针对Windows的扩展部分为应用程序开发者提供了开发具有Windows应用软件的功能。它有利于使程序员写出更加稳定并且更加高效的程序,也有助于在非占先Windows版本中使多个应用程序在多任务情况下更好地运作。除了WSAStartup()和WSACleanup()两个函数除外,其他的Windows扩展函数的使用不是强制性的。

1.4 这份规范的地位
    Windows Sockets是一份独立的规范。它的产生和存在是为了造益于应用程序开发者,网络软件供应商和广大计算机用户。这份规范的每一份正式出版的版本(非草稿)实际上代表了为网络软件供应商实现所需和应用程序开发者所用的一整套API。关于这套规范的讨论和改进还正在进行之中。这样的讨论主要是通过Internet上的一个电子邮件论坛-winsock@microdyne.com进行的。同时也有不定期的会议举行。会议的具体内容会在电子邮件论坛上发表。

1.5 曾经作过的修改

1.5.1 Windows Sockets 1.0
    Windows Sockets 1.0代表了网络软件供应商和用户协会细致周到的工作的结晶。Windows Sockets 1.0规范的发布是为了让网络软件供应商和应用程序开发者能够开始建立各自的符合Windows Sockets标准的实现和应用程序。

1.5.2 Windows Sockets 1.1
    Windows Sockets 1.1继承了Windows Sockets 1.0的准则和结构,并且仅在一些绝对必要的地方作了改动。这些改动都是基于不少公司在创作Windows Sockets 1.0实现时的经验和教训的。Windows Scokets 1.1包含了一些更加清晰的说明和对Windows Sockets 1.0的小改动。此外1.1还包含了如下重大的变更:
    * 加入了gethostname()这个常规调用,以便更加简单地得到主机名字和地址。
    * 定义DLL中小于1000的序数为Windows Sockets保留,而对大于1000的序数则没有限制。这使Windows Sockets供应商可以在DLL中加入自己的界面,而不用担心所选择的序数会和Windows Scokets将来的版本冲突。
    * 增加了WSAStartup()函数和WASClearup()函数之间的关联,要求两个函数互相对应。这使得应用程序开发者和第三方DLL在使用Windows Sockets实现时不需要考虑其他程序对这套API的调用。
    * 把函数intr_addr()的返回类型,从结构in_addr改为了无符号长整型。这个改变是为了适应Microsoft C编译器和Borland C编译器对返回类型为四字节结构的函数的不同处理方法。
    * 把WSAAsyncSelect()函数语义从“边缘触发”改为“电平触发”。这种方式大大地简化了一个应用程序对这个函数的调用。
    * 改变了ioctlsocket()函数中FIONBIO的语义。如果套接口还有未完成的WSAAsyncSelect()调用,该函数将失败返回。
    * 为了符合RFC 1122,在套接口选项中加入了TCP_NODELAY这一条。
    所有Windows Sockets 1.1对于Windows Sockets 1.0的改动在以下都作了记号。



----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.17:52:31   MSIE 6.0 Windows XPIP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 


第二章 使用Windows Sockets 1.1编程
在这一章,我们将介绍如何使用Windows Sockets 1.1编程,并讨论了使用Windows Sockets 1.1编程的一些细节问题。本章的讨论均是基于Windows Sockets 1.1规范的,在某些方面可能会和第六、七章对Windows Sockets 2的讨论不一致,请读者注意这一区别。

2.1 Windows Sockets协议栈安装检查
    任何一个与Windows Sockets Import Library联接的应用程序只需简单地调用WSAStartup()函数便可检测系统中有没有一个或多个Windows Sockets实现。而对于一个稍微聪明一些的应用程序来说,它会检查PATH环境变量来寻找有没有Windows Sockets实现的实例(Windows Sockets.DLL)。对于每一个实例,应用程序可以发出一个LoadLibrary()调用并且用WSAStartup()函数来得到这个实现的具体数据。
    这一版本的Windows Sockets规范并没有试图明确地讨论多个并发的Windows Sockets实现共同工作的情况。但这个规范中没有任何规定可以被解释成是限制多个Windows Sockets DLL同时存在并且被一个或者多个应用程序同时调用的。

2.2 套接口

2.2.1 基本概念
    通讯的基石是套接口,一个套接口是通讯的一端。在这一端上你可以找到与其对应的一个名字。一个正在被使用的套接口都有它的类型和与其相关的进程。套接口存在于通讯域中。通讯域是为了处理一般的线程通过套接口通讯而引进的一种抽象概念。套接口通常和同一个域中的套接口交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。Windows Sockets规范支持单一的通讯域,即Internet域。各种进程使用这个域互相之间用Internet协议族来进行通讯(Windows Sockets 1.1以上的版本支持其他的域,例如Windows Sockets 2)。
    套接口可以根据通讯性质分类;这种性质对于用户是可见的。应用程序一般仅在同一类的套接口间通讯。不过只要底层的通讯协议允许,不同类型的套接口间也照样可以通讯。
    用户目前可以使用两种套接口,即流套接口和数据报套接口。流套接口提供了双向的,有序的,无重复并且无记录边界的数据流服务。数据报套接口支持双向的数据流,但并不保证是可靠,有序,无重复的。也就是说,一个从数据报套接口接收信息的进程有可能发现信息重复了,或者和发出时的顺序不同。数据报套接口的一个重要特点是它保留了记录边界。对于这一特点,数据报套接口采用了与现在许多包交换网络(例如以太网)非常类似的模型。

2.2.2 客户机/服务器模型
    一个在建立分布式应用时最常用的范例便是客户机/服务器模型。在这种方案中客户应用程序向服务器程序请求服务。这种方式隐含了在建立客户机/服务器间通讯时的非对称性。客户机/服务器模型工作时要求有一套为客户机和服务器所共识的惯例来保证服务能够被提供(或被接受)。这一套惯例包含了一套协议。它必须在通讯的两头都被实现。根据不同的实际情况,协议可能是对称的或是非对称的。在对称的协议中,每一方都有可能扮演主从角色;在非对称协议中,一方被不可改变地认为是主机,而另一方则是从机。一个对称协议的例子是Internet中用于终端仿真的TELNET。而非对称协议的例子是Internet中的FTP。无论具体的协议是对称的或是非对称的,当服务被提供时必然存在“客户进程”和“服务进程”。
    一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“惊醒”并且为客户提供服务-对客户的请求作出适当的反应。这一请求/相应的过程可以简单的用图2-1表示。虽然基于连接的服务是设计客户机/服务器应用程序时的标准,但有些服务也是可以通过数据报套接口提供的。


2.2.3 带外数据
    注意:以下对于带外数据(也称为TCP紧急数据)的讨论,都是基于BSD模型而言的。用户和实现者必须注意,目前有两种互相矛盾的关于RFC 793的解释,也就是在这基础上,带外数据这一概念才被引入的。而且BSD对于带外数据的实现并没有符合RFC 1122定下的主机的要求,为了避免互操作时的问题,应用程序开发者最好不要使用带外数据,除非是与某一既成事实的服务互操作时所必须的。Windows Sockets提供者也必须提供他们的产品对于带外数据实现的语义的文挡(采用BSD方式或者是RFC 1122方式)。规定一个特殊的带外数据语义集已经超出了Windows Sockets规范的讨论范围。
    流套接口的抽象中包括了带外数据这一概念,带外数据是相连的每一对流套接口间一个逻辑上独立的传输通道。带外数据是独立于普通数据传送给用户的,这一抽象要求带外数据设备必须支持每一时刻至少一个带外数据消息被可靠地传送。这一消息可能包含至少一个字节;并且在任何时刻仅有一个带外数据信息等候发送。对于仅支持带内数据的通讯协议来说(例如紧急数据是与普通数据在同一序列中发送的),系统通常把紧急数据从普通数据中分离出来单独存放。这就允许用户可以在顺序接收紧急数据和非顺序接收紧急数据之间作出选择(非顺序接收时可以省去缓存重叠数据的麻烦)。在这种情况下,用户也可以“偷看一眼”紧急数据。
    某一个应用程序也可能喜欢线内处理紧急数据,即把其作为普通数据流的一部分。这可以靠设置套接口选项中的SO_OOBINLINE来实现(参见5.1.21节,setsockopt())。在这种情况下,应用程序可能希望确定未读数据中的哪一些是“紧急”的(“紧急”这一术语通常应用于线内带外数据)。为了达到这个目的,在Windows Sockets的实现中就要在数据流保留一个逻辑记号来指出带外数据从哪一点开始发送,一个应用程序可以使用SIOCATMARK ioctlsocket()命令(参见5.1.12节)来确定在记号之前是否还有未读入的数据。应用程序可以使用这一记号与其对方进行重新同步。
    WSAAsyncSelect()函数可以用于处理对带外数据到来的通知。

2.2.4 广播
    数据报套接口可以用来向许多系统支持的网络发送广播数据包。要实现这种功能,网络本身必须支持广播功能,因为系统软件并不提供对广播功能的任何模拟。广播信息将会给网络造成极重的负担,因为它们要求网络上的每台主机都为它们服务,所以发送广播数据包的能力被限制于那些用显式标记了允许广播的套接口中。广播通常是为了如下两个原因而使用的:1. 一个应用程序希望在本地网络中找到一个资源,而应用程序对该资源的地址又没有任何先验的知识。2. 一些重要的功能,例如路由要求把它们的信息发送给所有可以找到的邻机。
    被广播信息的目的地址取决于这一信息将在何种网络上广播。Internet域中支持一个速记地址用于广播-INADDR_BROADCAST。由于使用广播以前必须捆绑一个数据报套接口,所以所有收到的广播消息都带有发送者的地址和端口。
    某些类型的网络支持多种广播的概念。例如IEEE802.5令牌环结构便支持链接层广播指示,它用来控制广播数据是否通过桥接器发送。Windows Sockets规范没有提供任何机制用来判断某个应用程序是基于何种网络之上的,而且也没有任何办法来控制广播的语义。

2.3 字节顺序
    Intel处理器的字节顺序是和DEC VAX处理器的字节顺序一致的。因此它与68000型处理器以及Internet的顺序是不同的,所以用户在使用时要特别小心以保证正确的顺序。
    任何从Windows Sockets函数对IP地址和端口号的引用和传送给Windows Sockets函数的IP地址和端口号均是按照网络顺序组织的,这也包括了sockaddr_in结构这一数据类型中的IP地址域和端口域(但不包括sin_family域)。
    考虑到一个应用程序通常用与“时间”服务对应的端口来和服务器连接,而服务器提供某种机制来通知用户使用另一端口。因此getservbyname()函数返回的端口号已经是网络顺序了,可以直接用来组成一个地址,而不需要进行转换。然而如果用户输入一个数,而且指定使用这一端口号,应用程序则必须在使用它建立地址以前,把它从主机顺序转换成网络顺序(使用htons()函数)。相应地,如果应用程序希望显示包含于某一地址中的端口号(例如从getpeername()函数中返回的),这一端口号就必须在被显示前从网络顺序转换到主机顺序(使用ntohs()函数)。
    由于Intel处理器和Internet的字节顺序是不同的,上述的转换是无法避免的,应用程序的编写者应该使用作为Windows Sockets API一部分的标准的转换函数,而不要使用自己的转换函数代码。因为将来的Windows Sockets实现有可能在主机字节顺序与网络字节顺序相同的机器上运行。因此只有使用标准的转换函数的应用程序是可移植的。

2.4 套接口属性选项
    Windows Sockets规范支持的套接口属性选项都列在对setsockopt()函数和getsockopt()函数的叙述中。任何一个Windows Sockets实现必须能够识别所有这些属性选项,并且对每一个属性选项都返回合理的数值。每一个属性选项的缺省值列在下表中:

    选项 类型 含义 缺省值 注意事项

    SO_ACCEPTCON BOOL 套接口正在监听。 FALSE

    SO_BROADCAST BOOL 套接口被设置为可以 FALSE
                        发送广播数据。

    SO_DEBUG BOOL 允许Debug。 FALSE (*)

    S0_DONTLINGER BOOL 如果为真,SO_LINGER TRUE
                        选项被禁止。

    SO_DONTROUTE BOOL 路由被禁止。 FALSE (*)

    SO_ERROR int 得到并且清除错误状态。 0

    SO_KEEPALIVE BOOL 活跃信息正在被发送。 FALSE

    SO_LINGER struct 返回目前的linger信息。 l_onoff
                linger 为0
                FAR *

    SO_OOBINLINE BOOL 带外数据正在普通数据流 FALSE
                        中被接收。

    SO_RCVBUF int 接收缓冲区大小。 决定于实现 (*)

    SO_REUSEADDR BOOL 该套接口捆绑的地址 FALSE
                        是否可被其他人使用。

    SO_SNDBUF int 发送缓冲区大小。 决定于实现 (*)

    SO_TYPE int 套接口类型(如 和套接口被
                        SOCK_STREAM)。 创建时一致

    TCP_NODELAY BOOL 禁止采用Nagle 决定于实现
进行合并传送。

    (*) Windows Sockets实现有可能在用户调用setsockopt()函数时忽略这些属性,并且在用户调用getsockopt()函数时返回一个没有变化的值。或者它可能在setsockopt()时接受某个值,并且在getsockopt()时返回相应的数值,但事实上并没有在任何地方使用它。

2.5 数据库文件
    getXbyY()和WSAAyncGetXByY()这一类的例程是用来得到某种特殊的网络信息的。getXbyY()例程最初(在第一版的BERKELY UNIX中)是被设计成一种在文本数据库中查询信息的机制。虽然Windows Sockets实现可能用不同的方式来得到这些信息,但Windows Sockets应用程序要求通过getXbyY()或WSAAyncGetXByY()这一类例程得到的信息是一致。

2.6 与Berkeley套接口的不同
    有一些很有限的地方,Windows Sockets API必须与从严格地坚持Berkeley传统风格中解放出来。通常这么做是因为在Windows环境中实现的难度。

2.6.1 套接口数据类型和错误数值
    Windows Sockets规范中定义了一个新的数据类型SOCKET,这一类型的定义对于将来Windows Sockets规范的升级是必要的。例如在Windows NT中把套接口作为文件句柄来使用。这一类型的定义也保证了应用程序向Win/32环境的可移植性。因为这一类型会自动地从16位升级到32位。
    在UNIX中所有句柄包括套接口句柄,都是非负的短整数,而且一些应用程序把这一假设视为真理。Windows Sockets句柄则没有这一限制,除了INVALID_SOCKET不是一个有效的套接口外,套接口可以取从0到INVALID_SOCKET-1之间的任意值。
因为SOCKET类型是unsigned,所以编译已经存在于UNIX环境中的应用程序的源代码可能会导致signed/unsigned数据类型不匹配的警告。
    这还意味着,在socket()例程和accept()例程返回时,检查是否有错误发生就不应该再使用把返回值和-1比较的方法,或判断返回值是否为负(这两种方法在BSD中都是很普通,很合法的途径)。取而代之的是,一个应用程序应该使用常量INVALID_SOCKET,该常量已在WINSOCK.H中定义。
    例如:
        典型的BSD风格:
        s = socket(...);
        if (s == -1) /* of s<0 */
            {...}

        更优良的风格:
        s = socket(...);
        if (s == INVALID_SOCKET)
            {...}

2.6.2 select()函数和FD_*宏
    由于一个套接口不再表示了UNIX风格的小的非负的整数,select()函数在Windows Sockets API中的实现有一些变化:每一组套接口仍然用fd_set类型来代表,但是它并不是一个位掩码。整个组的套接口是用了一个套接口的数组来实现的。为了避免潜在的危险,应用程序应该坚持用FD_XXX宏来设置,初始化,清除和检查fd_set结构。

2.6.3 错误代码-errno,h_errno,WSAGetLastError()
    Windows Sockets实现所设置的错误代码是无法通过errno变量得到的。另外对于getXbyY()这一类的函数,错误代码无法从h_errno变量得到。错误代码可以使用WSAGetLastError()调用得到。这一函数在5.3.11中讨论。这个函数在Windows Sockets实现中是作为WIN/32函数GetLastError()的先导函数(最终是一个别名)。这样做是为了在多线程的进程中为每一线程得到自己的错误信息提供可靠的保障。
    为了保持与BSD的兼容性,应用程序可以加入以下一行代码:
        #define errno WSAGetLastError()
    这就保证了用全程的errno变量所写的网络程序代码在单线程环境中可以正确使用。当然,这样做有许多明显的缺点:如果一个原程序包含了一段代码对套接口和非套接口函数都用errno变量来检查错误,那么这种机制将无法工作。此外,一个应用程序不可能为errno赋一个新的值(在Windows Sockets中,WSASetLastError()函数可以做到这一点)。
    例如:
        典型的BSD风格:
        r = recv(...);
        if (r == -1 /* 但请见下文 */
            && errno == EWOULDBLOCK)
        {...}

        更优良的风格:
        r = recv(...);
        if (r == -1 /* 但请见下文 */
            && WSAGetLastError() == EWOULDBLOCK)
        {...}

    虽然为了兼容性原因,错误常量与4.3BSD所提供的一致;应用程序应该尽可能地使用“WSA”系列错误代码定义。例如,一个更准确的上面程序片断的版本应该是:
        r = recv(...);
        if (r == -1 /* 但请见下文 */
            && WSAGetLastError() == WSAEWOULDBLOCK)
        {...}

2.6.4 指针
    所有应用程序与Windows Sockets使用的指针都必须是FAR指针,为了方便应用程序开发者使用,Windows Sockets规范定义了数据类型LPHOSTENT。

2.6.5 重命名的函数
    有两种原因Berkeley套接口中的函数必须重命名以避免与其他的API冲突:

2.6.5.1 close()和closesocket()
    在Berkeley套接口中,套接口出现的形式与标准文件描述字相同,所以close()函数可以用来和关闭正规文件一样来关闭套接口。虽然在Windows Sockets API中,没有任何规定阻碍Windows Sockets实现用文件句柄来标识套接口,但是也没有任何规定要求这么做。套接口描述字并不认为是和正常文件句柄对应的,而且并不能认为文件操作,例如read(),write()和close()在应用于套接口后不能保证正确工作。套接口必须使用closesocket()例程来关闭,用close()例程来关闭套接口是不正确的,这样做的效果对于Windows Sockets规范说来也是未知的。

2.6.5.2 ioctl()和iooctlsocket()
    许多C语言的运行时系统出于与Windows Sockets无关的目的使用ioctl()例程,所以Windows Sockets定义ioctlsocket()例程。它被用于实现BSD中用ioctl()和fcntl()实现的功能。

2.6.6 阻塞例程和EINPROGRESS宏
    虽然Windows Sockets支持关于套接口的阻塞操作,但是这种应用是被强烈反对的.如果程序员被迫使用阻塞模式(例如一个准备移植的已有的程序),那么他应该清楚地知道Windows Sockets中阻塞操作的语义。有关细节请参见4.1.1

2.6.7 Windows Sockets支持的最大套接口数目
    一个特定的Windows Sockets提供者所支持的套接口的最大数目是由实现确定的。任何一个应用程序都不应假设某个待定数目的套接口可用。这一点在4.3.15 WSAStartup()中会被重申。而且一个应用程序可以真正使用的套接口的数目和某一特定的实现所支持的数目是完全无关的。
    一个Windows Sockets应用程序可以使用的套接口的最大数目是在编译时由常量FD_SETSIZE决定的。这个常量在select()函数(参见4.1.18)中被用来组建fd_set结构。在WINSOCK.H中缺省值是64。如果一个应用程序希望能够使用超过64个套接口,则编程人员必须在每一个源文件包含WINSOCK.H前定义确切的FD_SET值。有一种方法可以完成这项工作,就是在工程项目文件中的编译器选项上加入这一定义。例如在使用Microsoft C时加入-D FD_SETSIZE=128作为编译命令的一个命令行参数.要强调的是:FD_SET定义的值对Windows Sockets实现所支持的套接口的数目并无任何影响。

2.6.8 头文件
为了方便基于Berkeley套接口的已有的源代码的移植,Windows Sockets支持许多Berkeley头文件。这些Berkeley头文件被包含在WINSOCK.H中。所以一个Windows Sockets应用程序只需简单的包含WINSOCK.H就足够了(这也是一种被推荐使用的方法)。

2.6.9 API调用失败时的返回值
    常量SOCKET_ERROR是被用来检查API调用失败的。虽然对这一常量的使用并不是强制性的,但这是推荐的。如下的例子表明了如何使用SOCKET_ERROR常量

        典型的BSD风格:
        r = recv(...);
        if (r == -1 /* or r < 0 */
            && errno == EWOULDBLOCK)
        {...}

        更优良的风格:
        r = recv(...);
        if (r == SOCKET_ERROR
            && WSAGetLastError == WSAEWOULDBLOCK)
        {...}

2.6.10 原始套接口
    Windows Sockets规范并没有规定Windows Sockets DLL必须支持原始套接口-用SOCK_RAW打开的套接口。然而Windows Sockets规范鼓励Windows Sockets DLL提供原始套接口支持。一个Windows Sockets兼容的应用程序在希望使用原始套接口时应该试图用socket()调用(参见5.1.23节)来打开套接口。如果这么做失败了,应用程序则应该使用其他类型的套接口或向用户报告错误。

2.7 在多线程Windows版本中的Windows Sockets
    Windows Sockets接口被设计成既能够在单线程的Windows版本(例如Windows 3.1)又能够在占先的多线程Windows版本(例如Windows NT)中使用,在多线程环境中,套接口接口基本上是不变的。但多线程应用程序的作者必须知道,在线程之间同步对套接口的使用是应用程序的责任,而不是Windows Sockets实现的责任。这一点在其他形式的I/O中管理,例如文件I/O中是一样的。没有对套接口调用进行同步将导致不可预测的结果。例如,如果有两个线程同时调用同一套接口进行send(),那么数据发送的先后顺序就无法保证了。
    在一个线程中关闭一个未完成的阻塞的套接口将会导致另一个线程使用同一套接口的阻塞调用出错(WSAEINTER)返回,就象操作被取消一样。这也同样适用于某一个select()调用未完成时,应用程序关闭了其中的一个被选择的套接口。
    在占先的多线程Windows版本中,并没有缺省的阻塞钩子函数。这是因为如果一个单一的应用程序在等待某一操作结束时并不会调用PeekMessage()或GetMessage()这些会使应用程序产生一个非占先窗口的函数。因此机器在这种情况下不会被阻塞。然而,为了向后的兼容性,在多线程Windows版本中,WSASetBlockingHook()函数也被实现了。任何使用缺省阻塞钩子的应用程序可以安装它们自己的阻塞钩子函数来覆盖缺省的阻塞钩子函数。



----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.17:53:12   MSIE 6.0 Windows XPIP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 


第三章 Windows Sockets 1.1应用实例
在本章中,作者的实际工作为背景,给出了一个使用Windows Sockets 1.1编程的具体例子。并对这个例子作了详细的分析。这个例子在Windows 3.1、Windows Sockets 1.1和BSD OS for PC 2.0(BSD UNIX微机版)环境下调试通过。

3.1 套接口网络编程原理
    套接口有三种类型:流式套接口,数据报套接口及原始套接口.
流式套接口定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输.数据报套接口定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错.原始套接口允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等.
无连接服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与服务程序之间的相互作用。若使用无连接的套接口编程,程序的流程可以用图3-1表示。


面向连接服务器处理的请求往往比较复杂,不是一来一去的请求应答所能解决的,而且往往是并发服务器。使用面向连接的套接口编程,可以通过图3-1来表示:其时序。



    套接口工作过程如下:服务器首先启动,通过调用socket()建立一个套接口,然后调用bind()将该套接口和本地网络地址联系在一起,再调用listen()使套接口做好侦听的准备,并规定它的请求队列的长度,之后就调用accept()来接收连接.客户在建立套接口后就可调用connect()和服务器建立连接.连接一旦建立,客户机和服务器之间就可以通过调用read()和write()来发送和接收数据.最后,待数据传送结束后,双方调用close()关闭套接口.

3.2 Windows Sockets编程原理
    由于Windows的基于消息的特点,WINSOCK和BSD套接口相比,有如下一些新的扩充:
    1.异步选择机制
        异步选择函数WSAAsyncSelect()允许应用程序提名一个或多个感兴趣的网络事件,如FD_READ,FD_WRITE,FD_CONNECT,FD_ACCEPT等等代表的网络事件.当被提名的网络事件发生时,Windows应用程序的窗口函数将收到一个消息.这样就可以实现事件驱动了.
    2.异步请求函数
        异步请求函数允许应用程序用异步方式获得请求的信息,如WSAAsyncGetXByY()类函数. 这些函数是对BSD标准函数的扩充.函数WSACancelAsyncRequest()允许用户中止一个正在执行的异步请求.
    3.阻塞处理方法
        WINSOCK提供了"钩子函数"负责处理Windows消息,使Windows的消息循环能够继续.WINSOCK提供了两个函数(WSASetBlockingHook()和WSAUnhookBlockingHook())让应用程序设置或取消自己的"钩子函数".函数WSAIsBlocking()可以检测是否阻塞,函数WSACancelBlockingCall()可以取消一个阻塞的调用.
    4.错误处理
        WINSOCK提供了两个WSAGetLastError()和WSASetLastError()来获取和设置最近错误号.
    5.启动和终止
        由于Windows Sockets的服务是以动态连接库WINSOCK.DLL形式实现的,所以必须要先调用WSAStartup()函数对Windows Sockets DLL进行初始化,协商WINSOCK的版本支持,并分配必要的资源.在应用程序关闭套接口后,还应调用WSACleanup()终止对Windows Sockets DLL的使用,并释放资源,以备下一次使用.
    在这些函数中,实现Windows网络实时通信的关键是异步选择函数WSAAsyncSelect()的使用. 用法及详细说明参见第5.3.7.

3.3 Windows Sockets与UNIX套接口编程实例
    下面是一个简单的基于连接的点对点实时通信程序.它由两部分组成,服务器在主机UNIX下直接运行, 客户机在Windows下运行.

3.3.1 SERVER介绍
    由于SERVER是在UNIX下运行的,它对套接口的使用都是BSD的标准函数,程序也比较简单, 只有一段程序,下面简要解释一下.
    首先,建立自己的套接口.在互连网的进程通信中,全局标识一个进程需要一个被称为"半相关"的三元组(协议,本地主机地址,本地端口号)来描述,而一个完整的进程通信实例则需要一个被称为"相关"的五元组(协议, 本地主机地址,本地端口号,远端主机地址,远端端口号)来描述.
    s=socket(AF_INET, SOCK_STREAM, 0)
    该函数建立指定地址格式,数据类型和协议下的套接口,地址格式为AF_INET(唯一支持的格式),数据类型SOCK_STREAM表示建立流式套接口,参数三为0,即协议缺省.
    bind(s, (struct sockaddr *)&server, sizeof(server))
    该函数将建立服务器本地的半相关,其中,server是sockaddr_in结构,其成员描述了本地端口号和本地主机地址,经过bind()将服务器进程在网上标识出来.
    然后,建立连接.先是调用listen()函数表示开始侦听.再通过accept()调用等待接收连接.
    listen(s,1)表示连接请求队列长度为1,即只允许有一个请求,若有多个请求,则出现错误,给出错误代码WSAECONNREFUSED.
    ns = accept(s, (struct sockaddr *)&client, &namelen))
    accept()阻塞(缺省)等待请求队列中的请求,一旦有连接请求来,该函数就建立一个和s有相同属性的新的套接口.client也是一个sockaddr_in结构,连接建立时填入请求连接的套接口的半相关信息.
    接下来,就可以接收和发送数据了.
    recv(ns,buf,1024,0)
    send(ns,buf,pktlen,0)
    上面两个函数分别负责接收和发送数据,recv从ns(建立连接的套接口)接收数据放入buf中,send则将buf中数据发送给ns.至于第四个参数,表示该函数调用方式,可选择MSG_DONTROUTE和MSG_OOB, 0表示缺省.
    最后,关闭套接口.
    close(ns);
    close(s);

3.3.2 CLIENT介绍
    客户端是在Windows上运行的,使用了一些Windows Sockets的扩展函数,稍微复杂一些.包括了.RC和.C两个文件,其中的主窗口函数ClientProc()是程序的主要部分,下面简单解释一下.
    首先,是在WinMain()中建立好窗口后,即向主窗口函数发一条自定义的WM_USER消息, 做相关的准备工作.在主窗口函数中,一接收到WM_USER消息,首先调用WSAStartup()函数初始化Windows Sockets DLL,并检查版本号.如下:
    Status = WSAStartup(VersionReqd, lpmyWSAData);
    其中,VersionReqd描述了WINSOCK的版本(这里为1.1版),lpmyWSAData指向一个WSADATA结构,该结构描述了Windows Sockets的实现细节.
    WSAStartup()之后,进程通过主机名(运行时命令行参数传入)获取主机地址,如下:
    hostaddr = gethostbyname(server_address);
    hostaddr指向hostent结构,内容参见5.2.1.
    然后,进程就不断地消息循环,等待用户通过菜单选择"启动".这时,通过调用Client()来启动套接口.在Client()中,首先也是调用socket()来建立套接口.如下:
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        AlertUser(hWnd, "Socket Failed");
        return (FALSE);
    }
    紧接着,调用WSAAsyncSelect()函数提名FD_CONNECT网络事件,如下:       
    if (!SetSelect(hWnd, FD_CONNECT))
        return (FALSE);
    SetSelect()主要就是调用WSAASyncSelect(),让Windows Sockets DLL在侦测到连接建立时,就发送一条UM_SOCK的自定义消息,使消息循环继续下去.如下:
    BOOL SetSelect(HWND hWnd, long lEvent)
    {
        if (WSAAsyncSelect(s, hWnd, UM_SOCK, lEvent) == SOCKET_ERROR)
        {
            AlertUser(hWnd, "WSAAsyncSelect Failure.");
            return (FALSE);
        }
        return (TRUE);
    }
    为建立连接,必须马上调用connect()如下,由于先调用了WSAASyncSelect(),connect()便是非阻塞调用.进程发出连接请求后就不管了,当连接建立好后,WINSOCK DLL自动发一条消息给主窗口函数,以使程序运行下去.
    connect(s, (struct sockaddr FAR *)&dst_addr, sizeof(dst_addr));
    窗口函数在收到UM_SOCK消息后,判断是由哪个网络事件引起的,第一次,必然是由连接事件引起的,这样,就会执行相应的程序段,同样调用SetSelect()来提名FD_WRITE事件.希望在套接口可发送数据时接到消息.在收到FD_WRITE消息时,先调用send()发送数据,再调用SetSelect()来提名FD_READ事件, 希望在套接口可接收数据是接到消息.在收到FD_READ消息时,先调用recv()来接收数据再提名FD_WRITE事件,如此循环下去.直到发生连接关闭的事件FD_CLOSE,这时就调用WSAAsyncSelect(s,hWnd,0,0)来停止异步选择.在窗口函数接到WM_DESTROY消息时(即关闭窗口之前),先调用closesocket()(作用同UNIX 中的close())来关闭套接口,再调用WSACleanup()终止Windows Sockets DLL,并释放资源.
        ltemp = 8 * (bytes_sent/total_time);
        wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
        SetDlgItemText(hOurDlg, IDD_BITS, (LPSTR) prbuf);
       
        /* exit from the while loop */
        break;
    } /* end if (total_time) */
    write_count++;    /* incr. counter of times written */
    bytes_sent += tmp; /* # of bytes placed on connection */
    wsprintf((LPSTR)prbuf,"%ld\n",bytes_sent);
    SetDlgItemText(hOurDlg, IDD_SENT, (LPSTR) prbuf);
    } /* end if (tmp == -1) */
   
    write_count++;    /* incr. counter of times written */
    bytes_sent += tmp; /* # of bytes placed on connection */
    wsprintf((LPSTR)prbuf,"%ld\n",write_count);
    SetDlgItemText(hOurDlg, IDD_WRITE, (LPSTR) prbuf);

    wsprintf((LPSTR)prbuf,"%ld\n",bytes_sent);
    SetDlgItemText(hOurDlg, IDD_SENT, (LPSTR) prbuf);
} /* end while */

/* Look for a reply ... NOTE: most hosts won't give
* a 'reply', done to illustrate communication between
* sockets. Our ulisten example will give a reply though.
*/

SetDlgItemText(hOurDlg, IDD_COMMENT, "Waiting for reply from server..\n");
while (1) {
    tmp = sizeof(dest);
    i_temp = recvfrom(hSock,(char FAR *) &ReplyBuffer,sizeof(ReplyBuffer),
    0, (struct sockaddr *) &dest, (int FAR *) &tmp);
    if (i_temp == SOCKET_ERROR) {
    if (h_errno == WSAEWOULDBLOCK) /* if no data, read again */
        continue;
    else {
        /* any error besides these. just punt */
        wshout_err (hOurDlg, WSAGetLastError(), "recvfrom()");
    }
    break;
    } /* end if (i_temp == SOCKET_ERROR) */
    /* else got a reply ...*/
    wsprintf((LPSTR)prbuf, "Server: %s\n", (LPSTR) ReplyBuffer);
    SetDlgItemText(hOurDlg, IDD_COMMENT, prbuf);
    break;
} /* end while(1) */
   
/* All done */
return bytes_sent;
}

/* eof */

3.4.3.5 ulisten.c清单
/*
* 文件名: ULISTEN.C
*/

#include "wshout.h"


/* MSC Include files: */
#include <stdio.h>
#include <io.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

/* Returns the number of bytes written */
long UReadData(SOCKET hSock, HWND hOurDlg, int read_len)
{
static char    ReadBuf[BUF_SIZE];
static char    SendBuf[512];
struct sockaddr_in local;    /* Local machine address structure    */
int i;            /* General purpose return code        */
long total_time = 0L; /* variable to hold delta t        */
int    tmp, len = 0;
int    num_reads = 0;
long bytes_read = 0L;
long last_time, now, timeout = 15L;
long ltemp;
extern int run_cancelled;

BOOL bTemp = TRUE;

SetDlgItemText(hOurDlg, IDD_COMMENT, "Awaiting the UDP Data ...");
SetDlgItemText(hOurDlg, IDD_HNAME, " ");

time(&now); time(&last_time);
while (last_time + timeout > now) {
    time(&now);
    tmp = sizeof(local);
    do {
    ;
    } while (ShoutBlockingHook()); /* Dispatch messages while available */
   
    if (run_cancelled) {
    WSASetLastError(WSAEINTR);
    break;    /* Non-blocking mode was cancelled */
    }
   
    len = recvfrom(hSock, ReadBuf, read_len, 0,
    (struct sockaddr *) &local, &tmp);
    if (len == SOCKET_ERROR) {
    if (h_errno == WSAEWOULDBLOCK) {/* if no data, read again */
        continue;
    } /* end: if (errno == WSAEWOULDBLOCK) */
    else {
        if (bytes_read) {
        wshout_err (hOurDlg, WSAGetLastError(), "recvfrom()");
        }
    } /* end else */
    break;
    } /* end: if (len == SOCKET_ERROR) */
   
    if (bTemp) {    /* To update our main display once */
    /* Do not use wsprintf() or you will add an extra char */
    _fmemcpy(prbuf, inet_ntoa(local.sin_addr), 4*sizeof(u_long));
    SetDlgItemText(hOurDlg, IDD_HNAME, (LPSTR) prbuf);
    SetDlgItemText(hOurDlg, IDD_COMMENT, "Reading UDP Data ...");
    bTemp = FALSE;
    }

    num_reads++;
    if (len != SOCKET_ERROR)
    bytes_read += len;
    /* Print the statistics gathered */
    wsprintf((LPSTR)prbuf,"%d\n",num_reads);
    SetDlgItemText(hOurDlg, IDD_WRITE, (LPSTR) prbuf);

    wsprintf((LPSTR)prbuf,"%ld\n",bytes_read);
    SetDlgItemText(hOurDlg, IDD_SENT, (LPSTR) prbuf);
       
    time(&last_time);
} /* end: while */
total_time = timeout;

wsprintf((LPSTR)prbuf,"%ld\n",total_time);
SetDlgItemText(hOurDlg, IDD_TIME, (LPSTR) prbuf);

ltemp = num_reads/total_time;
wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
SetDlgItemText(hOurDlg, IDD_WRITES,(LPSTR) prbuf);

ltemp = bytes_read/total_time;
wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
SetDlgItemText(hOurDlg, IDD_BYTES, (LPSTR) prbuf);

ltemp = 8 * (bytes_read/total_time);
wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
SetDlgItemText(hOurDlg, IDD_BITS, (LPSTR) prbuf);

if (bytes_read) {
    SetDlgItemText(hOurDlg, IDD_COMMENT, "...UDP Listen Done\n");
} /* end: if (bytes_read) */
else {
    wsprintf((LPSTR)prbuf, "Timed out. No data received.\n");
    SetDlgItemText(hOurDlg, IDD_COMMENT, prbuf);
    goto come_here;
} /* end: else */

/* send reply to 'client' */
wsprintf((LPSTR)prbuf,
    "Replied to %s\n", inet_ntoa(local.sin_addr));
SetDlgItemText(hOurDlg, IDD_COMMENT, prbuf);

for (i=0; i<10; ++i) {
    sprintf(SendBuf, "Rec'd %ld bytes.\n", bytes_read);
    if(len = sendto(hSock, SendBuf, sizeof(SendBuf), 0,
    (struct sockaddr FAR *)&local,sizeof(local)))
    {
    if (len == SOCKET_ERROR) { /* if could not send bec. */
        if (h_errno == WSAEWOULDBLOCK)
        continue;
        wshout_err (hOurDlg, WSAGetLastError(), "sendto()");
        break;
    } /* end: if (len == -1) */
    } /* end: if (len = sendto()) */
} /* end for */
come_here:
return (bytes_read);
}

/* eof */

3.4.3.6 tshout.c清单
/*
* 文件名: TSHOUT.C
*/

#include "wshout.h"

/* MSC Include files: */
#include <stdio.h>
#include <io.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>


/* Returns the number of bytes written */
long TWriteData(SOCKET hSock, HWND hOurDlg, int send_len)
{
int counter;
static int DataBuffer[BUF_SIZE];    /* Buffer to hold generated data */
long    total_len = 1024L*1024L; /* Total # of bytes to generate */
long    bytes_sent = 0L;    /* Counter of bytes on connection */
int     tmp = 0;        /* holds count for bytes written */
long    write_count = 0L;    /* number of times        */
time_t    start, end;        /* variables to hold read timing */
long    total_time = 0L;    /* variable to hold delta t */
long ltemp = 0L;
extern int run_cancelled;

/* What makes shout unique is that it generates data*
* in memory (as opposed to accessing the disk).    *
* This tests the 'raw' speed of the TCP connection    *
* as the rate-limiting access time is eliminated.    *
* First, generate the data and place it into an    *
* array, data_buffer:                */

for (counter = 0; counter < BUF_SIZE; counter++)
    DataBuffer[counter] = counter;

/* Write data on the descriptor like a banshee,
* careful to time the writes and count data
* transmitted:
*/

SetDlgItemText(hOurDlg, IDD_COMMENT, "...Sending TCP Data");
time(&start);   
while ( bytes_sent < total_len) { /* while still bytes to send... */
    do {
    ;
    } while (ShoutBlockingHook()); /* Dispatch messages if any */
   
    if (run_cancelled) {
    WSASetLastError(WSAEINTR);
    break;    /* Non-blocking mode was cancelled */
    }
   
    tmp = send(hSock, (char FAR*) &DataBuffer, send_len, 0);
    if (tmp == SOCKET_ERROR) {
    if (h_errno == WSAEWOULDBLOCK)
        continue;
    else {
        wshout_err (hOurDlg, WSAGetLastError(), "send()");
    }
   
    /* Calc. time elapsed & stats about any data sent */
    time(&end);
    /* exit from the while loop */
    break;
   
    } /* end if (tmp == -1) */
    write_count++;    /* incr. counter of times written */
    bytes_sent += tmp; /* total # of bytes placed on connection*/
    wsprintf(prbuf,"%ld\n",write_count);
    SetDlgItemText(hOurDlg, IDD_WRITE, (LPSTR) prbuf);

    wsprintf(prbuf,"%ld\n",bytes_sent);
    SetDlgItemText(hOurDlg, IDD_SENT, (LPSTR) prbuf);
} /* end while */
time(&end);

if (total_time = (long) difftime(end, start)) {
    /* Print the statistics gathered */
    wsprintf((LPSTR)prbuf,"%ld\n",total_time);
    SetDlgItemText(hOurDlg, IDD_TIME, (LPSTR) prbuf);
   
    ltemp = write_count/total_time;
    wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
    SetDlgItemText(hOurDlg, IDD_WRITES,(LPSTR) prbuf);
   
    ltemp = bytes_sent/total_time;
    wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
    SetDlgItemText(hOurDlg, IDD_BYTES, (LPSTR) prbuf);
   
    ltemp = 8 * (bytes_sent/total_time);
    wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
    SetDlgItemText(hOurDlg, IDD_BITS, (LPSTR) prbuf);
} /* end if (total_time) */
   
/* All done */
SetDlgItemText(hOurDlg, IDD_COMMENT, "...TCP Shout Done");
return bytes_sent;
}

/* eof */

3.4.3.7 tlisten.c清单
/*
* 文件名:TLISTEN.C
*/

#include "wshout.h"

/* MSC Include files: */
#include <stdio.h>
#include <io.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>


/* Returns the number of bytes written */
long TReadData(SOCKET hSock, HWND hOurDlg, int read_len)
{
static char    ReadBuf[BUF_SIZE];
SOCKET hAcceptSock;
struct sockaddr_in local;    /* Local machine address structure    */
long total_time = 0L; /* variable to hold delta t        */
int    tmp, len = 0;
int    num_reads = 0;
long bytes_read = 0L;
long last_time = 0L;
long now = 0L;
long ltemp;
extern long blocking_option;
extern int run_cancelled;
struct linger AcceptLinger;

BOOL running = FALSE;
BOOL bTemp = TRUE;

    SetDlgItemText(hOurDlg, IDD_COMMENT, "Awaiting the TCP Data ...");
    SetDlgItemText(hOurDlg, IDD_HNAME, " ");

    tmp = sizeof(local);
    if (!blocking_option) {
    hAcceptSock = accept(hSock,(struct sockaddr FAR *)&local,
        (int FAR *)&tmp);
    }
    else {
    for (; {
        do {
        ;
        } while (ShoutBlockingHook()); /* Dispatch messages if any */

        if (run_cancelled) {
        WSASetLastError(WSAEINTR);
        break;    /* Non-blocking mode was cancelled */
        }

        hAcceptSock = accept(hSock,(struct sockaddr FAR *)&local,
                        (int FAR *)&tmp);
        if (hAcceptSock == INVALID_SOCKET) {
        if (h_errno == WSAEWOULDBLOCK)
            /* Try again */
            ;
        else {
            /* Fatal error -- pop out. */
            break;
        }
        } /* end if ((hAcceptSock = .. */
        else {
        /* Success -- pop out. */
        break;
        }
    } /* end for */
    } /* end else */

    if (hAcceptSock == INVALID_SOCKET) {
    wshout_err (hOurDlg, WSAGetLastError(), "accept()");
    return 0;
    }

/* Now, read the data as fast as we can until no more to read */
time(&last_time);
do {
    do {
    ;
    } while (ShoutBlockingHook()); /* Dispatch messages while available */
   
    if (run_cancelled) {
    WSASetLastError(WSAEINTR);
    break;    /* Non-blocking mode was cancelled */
    }
   
    len = recv(hAcceptSock, ReadBuf, read_len, 0);
    if (len == SOCKET_ERROR) {
    if (h_errno == WSAEWOULDBLOCK)
        continue;
    else
        break;
    }
    else if (len == 0)
    break;
    num_reads++;
    bytes_read += len;
   
    wsprintf((LPSTR)prbuf,"%d\n",num_reads);
    SetDlgItemText(hOurDlg, IDD_WRITE, (LPSTR) prbuf);

    wsprintf((LPSTR)prbuf,"%ld\n",bytes_read);
    SetDlgItemText(hOurDlg, IDD_SENT, (LPSTR) prbuf);
   
    if (bTemp) {    /* To update our main display once */
    /* Do not use wsprintf() or you will add an extra char */
    _fmemcpy(prbuf, inet_ntoa(local.sin_addr), 4*sizeof(u_long));
    SetDlgItemText(hOurDlg, IDD_HNAME, (LPSTR) prbuf);
    SetDlgItemText(hOurDlg, IDD_COMMENT, "Reading TCP Data ...");
    bTemp = FALSE;
    }
   
} while ((len != 0) || (len != SOCKET_ERROR));
time (&now);

if (len == SOCKET_ERROR) {
    if ((h_errno == WSAESHUTDOWN) || (h_errno == WSAENOTCONN)) {
    /* nothing available for read. */
    wsprintf((LPSTR)prbuf,
        "Connection from %s closed.\n",inet_ntoa(local.sin_addr));
    SetDlgItemText(hOurDlg, IDD_COMMENT, prbuf);
    }
    else { /* Other error */
    wshout_err (hOurDlg, WSAGetLastError(), "recv()");
    }
}
else if (len == 0) {
    /* Other side shut down the connection */
    wsprintf((LPSTR)prbuf,
    "Connection from %s closed.\n",inet_ntoa(local.sin_addr));
    SetDlgItemText(hOurDlg, IDD_COMMENT, prbuf);
}

AcceptLinger.l_onoff = 1;
AcceptLinger.l_linger = 0;
ret = setsockopt(hAcceptSock, SOL_SOCKET, SO_LINGER,
    (char FAR *) &AcceptLinger, sizeof(AcceptLinger));
if (ret == SOCKET_ERROR) {
    wshout_err (hOurDlg, WSAGetLastError(), "setsockopt()");
}

ret = closesocket(hAcceptSock);
if (ret == SOCKET_ERROR) {
    wshout_err (hOurDlg, WSAGetLastError(), "closesocket()");
}

total_time = (long) difftime(now, last_time);
if (total_time == 0)
    total_time = 1L; /* Avoid dividing by zero */

wsprintf((LPSTR)prbuf,"%ld\n",total_time);
SetDlgItemText(hOurDlg, IDD_TIME, (LPSTR) prbuf);

ltemp = num_reads/total_time;
wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
SetDlgItemText(hOurDlg, IDD_WRITES,(LPSTR) prbuf);

ltemp = bytes_read/total_time;
wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
SetDlgItemText(hOurDlg, IDD_BYTES, (LPSTR) prbuf);

ltemp = 8 * (bytes_read/total_time);
wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
SetDlgItemText(hOurDlg, IDD_BITS, (LPSTR) prbuf);

if (bytes_read) {
    SetDlgItemText(hOurDlg, IDD_COMMENT, "...TCP Listen Done\n");
} /* end: if (bytes_read) */

return (bytes_read);
}

/* eof */

3.4.3.8 errno.c清单
#include <windows.h>
#include <winsock.h>

/*
* 文件名: ERRNO.C
*/

/*
* Function: WSAsperror()
*
* Description:
*
* Copies string corresponding to the error code provided
* into buf, maximum length len. Returns length actually
* copied to buffer, or zero if error code is unknown.
* String resources should be present for each error code
* using the value of the code as the string ID (except for
* error = 0, which is mapped to WSABASEERR to keep it with
* the others). The DLL is free to use any string IDs that
* are less than WSABASEERR for its own use.
*
*/
int PASCAL FAR WSAsperror (HANDLE hInst, /* Instance Handle */
            int errorcode, /* WSA Error Number */
            char far * buf, /* Buffer for error string */
            int len) /* Length of buffer */
{
    int err_len; /* length of error text */

if (errorcode == 0) /* If error passed is 0, use the */
errorcode = WSABASEERR; /* base resource file number */
if (errorcode < WSABASEERR) /* If invalid Error code */
return 0; /* return string length of zero */
   
    /* error string from the table in the Resource file into buffer */
err_len = LoadString(hInst,errorcode,buf,len);
   
    return (err_len); /* return length of error string retrieved */
   
} /* end WSAsperror() */

/* eof */

3.4.3.9 resolve.c清单
/*
* 文件名: RESOLVE.C
*/

#include "wshout.h"

/* MSC Include files: */
#include <stdio.h>
#include <io.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>


SOCKET
ResolveAndConnectHost(LPSTR lpHostName,HWND hOurDlg,int iproto, int iSockPort)
{
struct    hostent FAR *host_ptr;        /* Ptr to the host name    */
struct    sockaddr_in dest;        /* Addr of target host    */
SOCKET    hSock;                /* The socket to create */
int iSockType;
extern int iTCP;
extern int iUDP;

/* Internet family addressing */
dest.sin_family = PF_INET;
if (iproto == iTCP) {
    iSockType = SOCK_STREAM;
}
else if (iproto == iUDP) {
    iSockType = SOCK_DGRAM;
}
else {
    return (SOCKET) -1;        /* Unknown protocol */
}

/* default port to connect to. Must be in network byte order    */
dest.sin_port = htons((u_int) iSockPort);

SetDlgItemText(hOurDlg, IDD_COMMENT,"Resolving hostname...");

/* Resolve the host name */
host_ptr = gethostbyname(lpHostName);
if (host_ptr == NULL) {
    wshout_err (hOurDlg, WSAGetLastError(), "gethostbyname()");
    return (SOCKET) -1;
}
/* Patch host address into struct describing conn: */
bcopy(host_ptr->h_addr,&dest.sin_addr,host_ptr->h_length);

/* Allocate a network (socket) descriptor:    */
hSock = socket(PF_INET, iSockType, 0);
if (hSock == INVALID_SOCKET) {
    wshout_err (hOurDlg, WSAGetLastError(), "socket()");
    return (SOCKET) -1;
}

/* Start connection process to host described in 'dest'    *
* struct.   
*/
SetDlgItemText(hOurDlg, IDD_COMMENT, "Connecting ...");
ret=connect(hSock,(struct sockaddr FAR *)&dest,sizeof(dest));

if (ret == SOCKET_ERROR) {
    wshout_err (hOurDlg, WSAGetLastError(), "connect()");
    closesocket(hSock);
    return (SOCKET) -1;
}

SetDlgItemText(hOurDlg, IDD_COMMENT, "...Connected");
return hSock;
}


SOCKET GetSocketAndBind(HWND hOurDlg, int iProto, int iSockPort)
{
SOCKET hSock;            /* Connection socket descriptor    */
struct sockaddr_in local;        /* Local machine address structure*/
int iSockType;
extern int iTCP;
extern int iUDP;

/* Internet family addressing */
if (iProto == iTCP) {
    iSockType = SOCK_STREAM;
}
else {
    iSockType = SOCK_DGRAM;
}

memset(&local, '\0', sizeof (local));
local.sin_family = PF_INET;
local.sin_port = htons((u_short)iSockPort);

/* allocate a socket descriptor */
hSock = socket(PF_INET, iSockType, 0);
if (hSock == INVALID_SOCKET) { /* socket() failed    */
    wshout_err (hOurDlg, WSAGetLastError(), "socket()");
    return (SOCKET) -1;
}

/* bind socket to a local addr */
ret = bind(hSock, (struct sockaddr FAR *) &local, sizeof(local));
if (ret == SOCKET_ERROR){    /* bind() failed    */
    wshout_err (hOurDlg, WSAGetLastError(), "bind()");
    return (SOCKET) -1;
}

if (iProto == iUDP)
    return (hSock);

/* If iProto == iTCP, then must listen() and accept() also */

ret = listen(hSock, 0);    /* listen on the socket */
if (ret == SOCKET_ERROR){ /* listen() failed    */
    wshout_err (hOurDlg, WSAGetLastError(), "listen()");
    return (SOCKET) -1;
}

return(hSock);
}

/* eof */
----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.17:55:29   MSIE 6.0 Windows XPIP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 

3.3.3 源程序清单
程序1:CLIENT.RC
ClientMenu MENU
BEGIN
    POPUP "&Server"
    BEGIN
        MENUITEM "&Start...", 101
        MENUITEM "&Exit", 102
    END
END

程序2:CLIENT.C
#define USERPORT 10001
#define IDM_START 101
#define IDM_EXIT 102
#define UM_SOCK WM_USER + 0X100

#include <alloc.h>
#include <mem.h>
#include <windows.h>
#include <winsock.h>
#define MAJOR_VERSION 1
#define MINOR_VERSION 2
#define WSA_MAKEWORD(x,y) ((y)*256+(x))

HANDLE hInst;
char server_address[256] = {0};
char buffer[1024];
char FAR * lpBuffer = &buffer[0];
SOCKET s = 0;
struct sockaddr_in dst_addr;
struct hostent far *hostaddr;
struct hostent hostnm;
struct servent far *sp;
int count = 0;

BOOL InitApplication(HINSTANCE hInstance);
long FAR PASCAL ClientProc(HWND hWnd, unsigned message, UINT wParam, LONG lParam);
void AlertUser(HWND hWnd, char *message);

BOOL Client(HWND hWnd);
BOOL ReceivePacket(HWND hWnd);
BOOL SetSelect(HWND hWnd, long lEvent);
BOOL SendPacket(HWND hWnd, int len);

int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hWnd;
    MSG msg;

    lstrcpy((LPSTR)server_address, lpCmdLine);
    if (!hPrevInstance)
        if (!InitApplication(hInstance))
            return (FALSE);
    hInst = hInstance;
    hWnd = CreateWindow("ClientClass", "Windows ECHO Client", WS_OVERLAPPEDWINDOW,\
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,\
        hInstance, NULL);
    if (!hWnd)
        return (FALSE);
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    PostMessage(hWnd, WM_USER, (WPARAM)0, (LPARAM)0);
    while (GetMessage(&msg, NULL, NULL, NULL))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (msg.wParam);
}

BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS WndClass;

    char *szAppName = "ClientClass";
// fill in window class information

    WndClass.lpszClassName = (LPSTR)szAppName;
    WndClass.hInstance = hInstance;
    WndClass.lpfnWndProc = ClientProc;
    WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    WndClass.hIcon = LoadIcon(hInstance, NULL);
    WndClass.lpszMenuName = "ClientMenu";
    WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
    WndClass.style = CS_HREDRAW | CS_VREDRAW;
    WndClass.cbClsExtra = 0;
    WndClass.cbWndExtra = 0;

// register the class

if (!RegisterClass(&WndClass))
        return(FALSE);

return(TRUE);
}

long FAR PASCAL ClientProc(HWND hWnd, unsigned message, UINT wParam, LONG lParam)
{
    int length, i;
    WSADATA wsaData;

    int Status;

    switch (message)
    {
        case WM_USER:
        {
            WORD    wMajorVersion, wMinorVersion;
            LPWSADATA    lpmyWSAData;
            WORD         VersionReqd;
            int            ret;

            wMajorVersion = MAJOR_VERSION;
            wMinorVersion = MINOR_VERSION;
            VersionReqd = WSA_MAKEWORD(wMajorVersion,wMinorVersion);
       
            lpmyWSAData = (LPWSADATA)malloc(sizeof(WSADATA));
            Status = WSAStartup(VersionReqd, lpmyWSAData);
            if (Status != 0)
            {
                AlertUser(hWnd, "WSAStartup() failed\n");
                PostQuitMessage(0);
            }
            hostaddr = gethostbyname(server_address);
            if (hostaddr == NULL)
            {
                AlertUser(hWnd, "gethostbyname ERROR!\n");
                WSACleanup();
                PostQuitMessage(0);
            }
            _fmemcpy(&hostnm, hostaddr, sizeof(struct hostent));
        }
            break;
        case WM_COMMAND:
            switch (wParam)
            {
                case IDM_START:
                    if (!Client(hWnd))
                    {
                        closesocket(s);
                        AlertUser(hWnd, "Start Failed");
                    }
                    break;
                case IDM_EXIT:
//                    WSACleanup();
                    PostQuitMessage(0);
                    break;
            }
            break;
        case UM_SOCK:
            switch (lParam)
            {
                case FD_CONNECT:
                    if (!SetSelect(hWnd, FD_WRITE))
                        closesocket(s);
                    break;
                case FD_READ:
                    if (!ReceivePacket(hWnd))
                    {
                        AlertUser(hWnd, "Receive Packet Failed.\n");
                        closesocket(s);
                        break;
                    }
                    if (!SetSelect(hWnd, FD_WRITE))
                        closesocket(s);
                    break;
                case FD_WRITE:
                    for (i = 0; i < 1024; i ++)
                        buffer[i] = (char)'A' + i % 26;
                    length = 1024;
                    if (!SendPacket(hWnd, length))
                    {
                        AlertUser(hWnd, "Packet Send Failed!\n");
                        closesocket(s);
                        break;
                    }
                    if (!SetSelect(hWnd, FD_READ))
                        closesocket(s);
                    break;
                case FD_CLOSE:
                    if (WSAAsyncSelect(s, hWnd, 0, 0) == SOCKET_ERROR)
                        AlertUser(hWnd, "WSAAsyncSelect Failed.\n");
                    break;
                default:
                    if (WSAGETSELECTERROR(lParam) != 0)
                    {
                        AlertUser(hWnd, "Socket Report Failure.");
                        closesocket(s);
                        break;
                    }
                    break;
}
            break;
        case WM_DESTROY:
            closesocket(s);
            WSACleanup();
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return(NULL);
}

void AlertUser(HWND hWnd, char *message)
{
    MessageBox(hWnd, (LPSTR)message, "Warning", MB_ICONEXCLAMATION);
    return;
}

BOOL Client(HWND hWnd)
{
    memset(&dst_addr,'\0', sizeof (struct sockaddr_in));
    _fmemcpy((char FAR *)&dst_addr.sin_addr,(char FAR *)hostnm.h_addr,hostnm.h_length);
    dst_addr.sin_family = hostnm.h_addrtype;
    dst_addr.sin_port = htons(USERPORT);
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
    {
        AlertUser(hWnd, "Socket Failed");
        return (FALSE);
    }
    if (!SetSelect(hWnd, FD_CONNECT))
        return (FALSE);
    connect(s, (struct sockaddr FAR *)&dst_addr, sizeof(dst_addr));
    return (TRUE);
}


BOOL ReceivePacket(HWND hWnd)
{
    HDC hDc;
    int length;
    int i1,i2,i3;
    char line1[255], line2[255], line3[255];

    count ++;
    if ((length = recv(s, lpBuffer, 1024, 0)) == SOCKET_ERROR)
        return (FALSE);
    if (length == 0)
        return (FALSE);
    if (hDc = GetDC(hWnd))
    {
        i1 = wsprintf((LPSTR)line1, "TCP Echo Client No.%d", count);
        i2 = wsprintf((LPSTR)line2, "Receive %d bytes",length);
        i3 = wsprintf((LPSTR)line3, "Those are:%c, %c, %c, %c, %c, %c",buffer[0],buffer[1],buffer[2],buffer[100],buffer[1000],buffer[1023]);
        TextOut(hDc, 10, 2, (LPSTR)line1, i1);
        TextOut(hDc, 10, 22, (LPSTR)line2, i2);
        TextOut(hDc, 10, 42, (LPSTR)line3, i3);
        ReleaseDC(hWnd, hDc);
    }
    return (TRUE);
}

BOOL SetSelect(HWND hWnd, long lEvent)
{
    if (WSAAsyncSelect(s, hWnd, UM_SOCK, lEvent) == SOCKET_ERROR)
    {
        AlertUser(hWnd, "WSAAsyncSelect Failure.");
        return (FALSE);
    }
    return (TRUE);
}

BOOL SendPacket(HWND hWnd, int len)
{
    int length;

    if ((length = send(s, lpBuffer, len, 0)) == SOCKET_ERROR)
        return (FALSE);
    else
    if (length != len)
    {
        AlertUser(hWnd, "Send Length NOT Match!");
        return (FALSE);
    }
    return (TRUE);
}

程序3ERVER.C
#include <sys/types.h>
#include <sys/mntent.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define USERPORT 10001
#define HOST_IP_ADDR "192.1.1.2"

main(int argc, char **argv)
{
    char buf[1024];
    struct sockaddr_in client;
    struct sockaddr_in server;

    int s;
    int ns;
    int namelen;
    int pktlen;
   
    if ((s=socket(AF_INET, SOCK_STREAM, 0))<0)
    {
        perror("Socket()");
        return;
    }
    bzero((char *)&server,sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(USERPORT);
    server.sin_addr.s_addr = INADDR_ANY;
    if (bind(s, (struct sockaddr *)&server, sizeof(server))<0)
    {
        perror("Bind()");
        return;
    }
    if (listen(s,1)!=0)
    {
        perror("Listen()");
        return;
    }
    namelen = sizeof(client);
    if ((ns = accept(s, (struct sockaddr *)&client, &namelen)) ==-1)
    {
        perror("Accept()");
        return;
    }
    for (;
    {
        if ((pktlen = recv(ns,buf,1024,0))<0)
        {
            perror("Recv()");
            break;
        }
        else
        if (pktlen == 0)
        {
            printf("Recv():return FAILED,connection is shut down!\n");
            break;
        }
        else
            printf("Recv():return SUCCESS,packet length = %d\n",pktlen);
        sleep(1);
        if (send(ns,buf,pktlen,0)<0)
        {
            perror("Send()");
            break;
        }
        else
            printf("Send():return SUCCESS,packet length = %d\n",pktlen);
    }
    close(ns);
    close(s);
    printf("Server ended successfully\n");
}

3.4 另一个精巧的应用程序实例-wshout
在本节中,我们通过一个经过精心选择的例子,进一步讨论一下Windows Sockets编程技术。例如如何编制客户机或服务器程序,如何应用TCP有连接服务(流式套接口)或UDP无连接服务(数据报套接口),如何进行阻塞或非阻塞方式的套接口操作等等,这些都是经常碰到的问题。接下来要介绍的wshout程序,可以通过灵活地设置不同选项来达到上述应用情况的任意组合,从而基本覆盖了应用Windows Sockets编程所可能碰到的问题,具有很好的研究参考价值。
由于该程序思路清晰,结构精良,所以我们不打算很详细地剖析每一个语句,而只是简要介绍一下整个程序的逻辑结构,并在源程序中加入适当的注释。我们相信,任何具有基本C语言和Windows编程经验的读者,都能很轻松地读懂绝大部分内容。经过仔细咀嚼和推敲后,更能得到一些编写优质程序的灵感。
该程序在FTP公司的PCTCP支撑环境下调试通过,不过只要读者拥有任何符合Windows Sockets 1.1规范的实现,也能顺利执行该程序。


3.4.1 源程序目录
1. wshout.c wshout主程序
2. wshout.h wshout头文件
3. wshout.rc wshout资源文件
4. ushout.c UDP客户机程序
5. ulisten.c UDP服务器程序
6. tshout.c TCP客户机程序
7. tlisten.c TCP服务器程序
8. errno.c 获取WSAE*错误描述字符串程序
9. resolve.c 客户机/服务器启动程序

在编译本程序时,笔者用的是BC3.1,只需做一个PRJ工程文件,将上述.c文件及winsock.lib包括进来就行了。请注意winsock.h应在include目录或当前目录中,winsock.lib可利用winsock.dll通过implib工具来建立。如果读者使用其他的编译器,可自行作相应的调整,在此不再赘述。

3.4.2 程序逻辑结构



----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.17:56:07   MSIE 6.0 Windows XPIP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 

3.4.3 源程序清单及注释
3.4.3.1 wshout.c清单
/*
* 文件名: WSHOUT.C
*/

/* MSC Include files: */
#include <stdio.h>
#include <io.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#include "wshout.h"

#define MAJOR_VERSION    1
#define MINOR_VERSION    2
#define WSA_MAKEWORD(x,y)    ((y) * 256 + (x)) /* HI:Minor, LO:Major */

HANDLE    hInst;            /* 进程实例句柄 */
HWND    hOurWnd;        /* 主窗口句柄 */
HWND    hMainDlg;        /* 主对话框句柄 */

int ret;            /* 工作变量 */
char    prbuf[PRBUF_LEN];    /* 用于显示文本的工作缓冲区 */

SOCKET sd;            /* 用于监听连接的套接口描述字 */
long temporary_option = 0L;    /* 缺省为阻塞模式 */
long blocking_option = 0L;    /* 阻塞模式的全局标识 */
int run_cancelled    = 0;    /* 指示何时按下了取消按钮 */
int len = 1024;            /* 一次写的字节数 */
BOOL    running = FALSE;    /* 程序的运行状态 */
const int iTCP = 1;        /* 指定为TCP Shout */
const int iUDP = 2;        /* 指定为UDP Shout */
int iProto    = 1;        /* 缺省为TCP Shout */
int iPortNo    = SOCK_SHOUT;   
int temporary_protocol = 1;    /* 在Settings()中使用 */
int iShout = 1;
int iListen = 2;
int iClientOrServer = 1;    /* 缺省为Shout(客户机)    */
int tClientOrServer = 1;    /* 在Settings()中使用 */
char HostModeBuf[20];/* 保存模式字符串 */
WORD VersionReqd;
LPWSADATA lpmyWSAData;

int PASCAL
WinMain (HANDLE hInstance,HANDLE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{

HWND hWnd;
MSG msg;
BOOL InitApp(HANDLE);

if (!hPrevInstance)
if (!InitApp(hInstance))
    return (NULL);

hInst = hInstance;

hWnd = CreateWindow("MainMenu",
    "Windows Shout",
    WS_OVERLAPPEDWINDOW | WS_SYSMENU | WS_MINIMIZEBOX,
    CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);

if (!hWnd)
    return (NULL);

hOurWnd = hWnd;

while (GetMessage(&msg, NULL, NULL, NULL)) {
    TranslateMessage(&msg); /* 翻译虚拟键码 */
    DispatchMessage(&msg);
}

return (msg.wParam);
}

BOOL InitApp(HANDLE hInstance)
{
HANDLE hMemory;
PWNDCLASS pWndClass;
BOOL bSuccess;

hMemory = LocalAlloc(LPTR, sizeof(WNDCLASS));
pWndClass = (PWNDCLASS) LocalLock(hMemory);
pWndClass->hCursor = LoadCursor(NULL, IDC_ARROW);
pWndClass->hIcon = LoadIcon(hInstance, (LPSTR) "SHOUT");
pWndClass->lpszMenuName = (LPSTR) "MainMenu";
pWndClass->lpszClassName = (LPSTR) "MainMenu";
pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
pWndClass->hInstance = hInstance;
pWndClass->style = NULL;
pWndClass->lpfnWndProc = ShoutWndProc;

bSuccess = RegisterClass(pWndClass);

LocalUnlock(hMemory);
LocalFree(hMemory);
return (bSuccess);
}

long FAR PASCAL ShoutWndProc(HWND hWnd, WORD message,WORD wParam, LONG lParam)
{
FARPROC lpDialogBoxProc;

switch (message){
    case WM_CREATE:
   
    /* Put up the dialog box */
    lpDialogBoxProc = MakeProcInstance(DialogProc, hInst);
    DialogBox (hInst, (LPSTR) "MainDialog", hWnd, lpDialogBoxProc) ;
    FreeProcInstance(lpDialogBoxProc);
    PostMessage(hWnd, WM_DESTROY, 0, 0L);
    break;
   
    case WM_DESTROY:
    PostQuitMessage(0);
    break;
   
    default:
    return(DefWindowProc(hWnd, message, wParam, lParam));
    }
return NULL;

}


BOOL FAR PASCAL DialogProc(HWND hOurDlg, WORD message, WORD wParam, LONG lParam)
{
FARPROC lpProcAbout;
FARPROC lpProcSettings;
long lret;
WORD wMajorVersion, wMinorVersion;
char hostnm[64];        /* 包含主机名的工作缓冲区 */

switch (message) {
    case WM_INITDIALOG:
    /* 选择缺省主机 */
    SetDlgItemText(hOurDlg, IDD_HNAME, "");
    SendDlgItemMessage(hOurDlg,        /* 对话框句柄 */
        IDD_HNAME,            /* 向何处发送msg */
        EM_SETSEL,            /* 选择字符 */
        NULL,                /* 附加信息 */
        MAKELONG(0, 0x7fff));        /* 全部内容 */
    SetFocus(GetDlgItem(hOurDlg, IDD_HNAME));
   
    /* 初始化 */
    hMainDlg = hOurDlg; /* 保存自己的窗口句柄 */
    SetDlgItemText(hOurDlg, IDD_COHOST,"Shout to:");
    wMajorVersion = MAJOR_VERSION;
    wMinorVersion = MINOR_VERSION;
    VersionReqd=WSA_MAKEWORD(wMajorVersion, wMinorVersion);

    lpmyWSAData = (LPWSADATA)_calloc(1, sizeof(WSADATA));
   
    ret = WSAStartup(VersionReqd, lpmyWSAData);
   
    if (ret != 0){
        wshout_err (hOurDlg, WSAGetLastError(), "WSAStartup()");
    }
   
    return (TRUE);

    case WM_CLOSE:
    PostMessage(hOurDlg, WM_COMMAND, IDM_EXIT, 0L);
    break;
   
    case WM_SYSCOMMAND:
    SendMessage(hOurWnd, message, wParam, lParam);
    break;
   
    case WM_COMMAND:
    switch (wParam) {
    case IDD_CONNECT:        /* 按下连接按钮 */
    case IDM_START:        /* 选择了Start菜单项 */
        run_cancelled = FALSE;
        /* 不能重入 */
        if (running){
        MessageBox(hOurWnd,"Shout is already running !",
            "Shout", MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
        return FALSE;
        }
        ClearBoxes(hOurDlg);
        running = TRUE;

        if (iClientOrServer == iShout) {
        /* 确保有主机名 */
        if (GetDlgItemText (hOurDlg, IDD_HNAME, hostnm, 80) < 2) {
        MessageBeep(0);
        SetDlgItemText(hOurDlg,
            IDD_COMMENT,"No hostname specified");
        running = FALSE;
        break;
        }
        sd = ResolveAndConnectHost((char FAR *)hostnm,hOurDlg,iProto,
        iPortNo);
        if (sd == SOCKET_ERROR) {/* 无法创建套接口 */
        running = FALSE;
        break;
        }
    }
        else {
        sd = GetSocketAndBind(hOurDlg, iProto, iPortNo);
        if (sd == SOCKET_ERROR) {
        running = FALSE;
        break;
        }
    }
       
        /* Set the I/O mode of the socket */
        if (blocking_option) {
        lret = 1L; /* 非阻塞模式 */
        ioctlsocket(sd, FIONBIO, (u_long FAR *) &lret);
        }
        else {
        lret = 0L; /* 阻塞模式 */
        ioctlsocket(sd, FIONBIO, (u_long FAR *) &lret);
        }
       
        if (iClientOrServer == iShout) { /* SHOUT */
        /* 产生数据并写入套接口 */
        if (iProto == iTCP)
            lret = TWriteData(sd, hOurDlg, len);
        else /* UDP */
            lret = UWriteData(sd, hOurDlg, len);
        }
        else {    /* LISTEN */
        if (iProto == iTCP)
            lret = TReadData(sd,hOurDlg, len);
        else /* UDP */
            lret = UReadData(sd,hOurDlg, len);
        }
       
        closesocket(sd);
        running = FALSE;
        break;
       
    case IDD_CANCEL:
        if (running) {
        /* 停止 */
        ret = WSACancelBlockingCall();
        run_cancelled = TRUE;
        if (ret == SOCKET_ERROR) {
            /* WSANOTINITIALISED or WSAENETDOWN or WSAEINVAL */
            if (h_errno == WSAENETDOWN) {
            /* Watch out for hAcceptSock! */
            /* close what is left of the connection */
            closesocket(sd);
            }
        }
        }
        break;
       
    case IDM_EXIT:
        ret = WSACleanup();
        if (ret == SOCKET_ERROR && h_errno == WSAEINPROGRESS){
        MessageBox(hOurWnd,
            "Data transfer in progress.\nStop transfer first.",
            "WndProc()", MB_OK | MB_APPLMODAL|MB_ICONINFORMATION);
        break; /* 一个套接口正处于阻塞状态 */
        }
       
        _free((char NEAR *) lpmyWSAData);
        EndDialog(hOurDlg, TRUE) ; /* 退出 */
        break;
       
    case IDM_ABOUT:
        lpProcAbout = MakeProcInstance(About, hInst);
        DialogBox(hInst, "AboutBox", hOurDlg, lpProcAbout);
        FreeProcInstance(lpProcAbout);
        break;
       
    case IDM_SETTINGS:
        lpProcSettings = MakeProcInstance(Settings, hInst);
        DialogBox(hInst, "SettingsDialog", hOurDlg, lpProcSettings);
        FreeProcInstance(lpProcSettings);
        break;
       
    default:
        break;
       
    } /* switch (wParam) */
    break;
    } /* switch (message) */
return FALSE;
}


/* 此函数处理About对话框 */
BOOL FAR PASCAL About(HWND hDlg, WORD message, WORD wParam, LONG lParam)
{
char tempBuf[15];

switch (message) {
case WM_INITDIALOG:
    SetDlgItemText(hDlg, IDA_COPYRIGHT,(LPSTR)lpmyWSAData->szDescription);

    wsprintf(tempBuf, "%d.%2d\n",MAJOR_VERSION, MINOR_VERSION);
    SetDlgItemText(hDlg, IDA_APP_VERSION, (LPSTR) tempBuf);

    wsprintf(tempBuf, "%d.%2d\n",
    lpmyWSAData->wVersion%256,lpmyWSAData->wVersion/256);
    SetDlgItemText (hDlg, IDA_DLL_VERSION, (LPSTR) tempBuf);
    return (FALSE);

case WM_COMMAND:
    if (wParam == IDOK
    || wParam == IDCANCEL) {
    EndDialog(hDlg, TRUE);
    return (TRUE);
    }
    break;
}
return (FALSE);
}

/* 此函数处理Settings 对话框 */
BOOL FAR PASCAL Settings(HWND hDlg, WORD message, WORD wParam, LONG lParam)
{
int buffer_len = len;
int port_no = iPortNo;

switch (message) {
case WM_INITDIALOG:
    /* Select a default send() buffer length */
    SetDlgItemInt(hDlg, IDS_BUFFLEN, len, 0);

    /* Select a default port number */
    SetDlgItemInt(hDlg, IDS_PORTNO, iPortNo, 0);

    if (iClientOrServer == iShout)        /* 程序类型 */
    CheckThisProgBoxOn(hDlg, IDS_CLIENT);
    else
    CheckThisProgBoxOn(hDlg, IDS_SERVER);
   
    if (iProto == iTCP)            /* 协议类型 */
    CheckThisProtoBoxOn(hDlg, IDS_TCP);
    else
    CheckThisProtoBoxOn(hDlg, IDS_UDP);
   
    if (!blocking_option)            /* 阻塞模式 */
    CheckThisBoxOn(hDlg, IDS_BLOCK);
    else
    CheckThisBoxOn(hDlg, IDS_NOBLOCK);
   
    SendDlgItemMessage(hDlg,        /* dialog handle    */
        IDS_PORTNO,            /* where to send msg    */
        EM_SETSEL,            /* select characters    */
        NULL,                /* additional info    */
        MAKELONG(0, 0x7fff));        /* entire contents    */
   
    SendDlgItemMessage(hDlg,        /* dialog handle    */
        IDS_BUFFLEN,            /* where to send msg    */
        EM_SETSEL,            /* select characters    */
        NULL,                /* additional info    */
        MAKELONG(0, 0x7fff));        /* entire contents    */
    SetFocus(GetDlgItem(hDlg, IDS_BUFFLEN));
    return (TRUE);

case WM_COMMAND:
    switch (wParam){
    case IDS_CLIENT:
    /* USer has set to Shout */
    CheckThisProgBoxOn(hDlg, IDS_CLIENT);
    tClientOrServer = iShout;
    SetDlgItemText(hMainDlg, IDD_COHOST,"Foreign host:");
    SetDlgItemText(hMainDlg, IDD_HNAME,"");
    break;
    case IDS_SERVER:
    /* USer has set to Listen */
    CheckThisProgBoxOn(hDlg, IDS_SERVER);
    tClientOrServer = iListen;
    SetDlgItemText(hMainDlg, IDD_COHOST,"Listening to:");
    SetDlgItemText(hMainDlg, IDD_HNAME,"[Hit 'Start']");
    break;
    case IDS_TCP:
    /* USer has set to TCP */
    CheckThisProtoBoxOn(hDlg, IDS_TCP);
    temporary_protocol = iTCP;
    break;
    case IDS_UDP:
    /* USer has set to UDP */
    CheckThisProtoBoxOn(hDlg, IDS_UDP);
    temporary_protocol = iUDP;
    break;
    case IDS_BLOCK:
    /* User has set to blocking mode */
    CheckThisBoxOn(hDlg, IDS_BLOCK);
    temporary_option = 0L;
    break;
    case IDS_NOBLOCK:
    /* User has set to nonblocking mode */
    CheckThisBoxOn(hDlg, IDS_NOBLOCK);
    temporary_option = 1L;
    break;
    case IDOK:
    /* 用户已完成对设置的修改 */
    buffer_len = GetDlgItemInt(hDlg, IDS_BUFFLEN, NULL, 0);
    if (buffer_len == 0 || buffer_len > 8192) {
        MessageBox(hOurWnd, "Buffer length must be between 1 and 8K",
            "Settings()",
            MB_OK | MB_APPLMODAL | MB_ICONSTOP);
        return (FALSE);
    }
   
    port_no = GetDlgItemInt(hDlg, IDS_PORTNO, NULL, 0);
    if (port_no == 0) {
        MessageBox(hDlg, "Port number must be between 0 and 65,535",
            "Settings()",
            MB_OK | MB_APPLMODAL | MB_ICONSTOP);
        return (FALSE);
    }
    len = buffer_len;
    iPortNo = port_no;
    blocking_option = temporary_option;
    iProto    = temporary_protocol;
    iClientOrServer = tClientOrServer;
   
    case IDCANCEL:
    /* 用户不想改变设置 */
    EndDialog(hDlg, TRUE);
    return (TRUE);
   
    default:
    break;
    }

default:
    break;
}
return (FALSE);
}

void
CheckThisBoxOn(HWND hDlg, int ButtonID)
{
switch (ButtonID) {
case IDS_BLOCK:
    CheckDlgButton(hDlg, IDS_BLOCK, 1);
    CheckDlgButton(hDlg, IDS_NOBLOCK, 0);
    break;
case IDS_NOBLOCK:
    CheckDlgButton(hDlg, IDS_BLOCK, 0);
    CheckDlgButton(hDlg, IDS_NOBLOCK, 1);
    break;
default:
    break;
}
return;
}

void
CheckThisProtoBoxOn(HWND hDlg, int ButtonID)
{
switch (ButtonID) {
case IDS_TCP:
    CheckDlgButton(hDlg, IDS_TCP, 1);
    CheckDlgButton(hDlg, IDS_UDP, 0);
    break;
case IDS_UDP:
    CheckDlgButton(hDlg, IDS_TCP, 0);
    CheckDlgButton(hDlg, IDS_UDP, 1);
    break;
default:
    break;
}
return;
}

void
CheckThisProgBoxOn(HWND hDlg, int ButtonID)
{
switch (ButtonID) {
case IDS_CLIENT: /* Shout */
    CheckDlgButton(hDlg, IDS_CLIENT, 1);
    CheckDlgButton(hDlg, IDS_SERVER, 0);
    break;
case IDS_SERVER: /* Listen */
    CheckDlgButton(hDlg, IDS_CLIENT, 0);
    CheckDlgButton(hDlg, IDS_SERVER, 1);
    break;
default:
    break;
}
return;
}

/* 以下就是我们如何处理“模拟阻塞”-本函数检查消息队列,如果发现需要处理的消息,就返回一个正的值。*/
int
ShoutBlockingHook (void)
{
MSG msg;        /* lets us pull messages via PeekMessage */
int ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

if (ret) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return ret;
}

char *
_calloc (nelem, elsize)
unsigned nelem, elsize;
{
    HANDLE hMem;
    PSTR ptr;
    unsigned size = nelem * elsize;

    if ((hMem = LocalAlloc(LPTR, size)) == NULL)
        return (char *) 0;
    if ((ptr = LocalLock(hMem)) == NULL) {
        LocalFree(hMem);
        return (char *) 0;
    }
    return (char *) ptr;
}

void
_free (void *cP)
{
    (void) LocalFree(LocalHandle((WORD) cP));
}

void
ClearBoxes(HWND hOurDlg)
{
wsprintf(prbuf," \n");
SetDlgItemText(hOurDlg, IDD_WRITE, (LPSTR) prbuf);
SetDlgItemText(hOurDlg, IDD_SENT, (LPSTR) prbuf);
SetDlgItemText(hOurDlg, IDD_TIME, (LPSTR) prbuf);
SetDlgItemText(hOurDlg, IDD_WRITES,(LPSTR) prbuf);
SetDlgItemText(hOurDlg, IDD_BYTES, (LPSTR) prbuf);
SetDlgItemText(hOurDlg, IDD_BITS, (LPSTR) prbuf);

return;
}

/*
* wshout_err()函数
* 描述:
*
* 通过错误代码获取相应的错误描述文本,与用户提供的错误前缀合
* 并,并显示在对话框中。
*/
void wshout_err (HWND hOurDlg, /* 对话框的窗口句柄 */
        int wsa_err, /* WinSock错误代码 */
        char far *err_prefix) /* 错误前缀字符串 */
{
    char errbuf[PRBUF_LEN];    /* 错误描述字符串缓冲区 */

/* 获取错误描述字符串 */
    WSAsperror(hInst, wsa_err, (LPSTR)errbuf, PRBUF_LEN);
   
    /* 合并错误描述字符串与用户错误前缀字符串 */
    wsprintf((LPSTR)prbuf, "%s:%s", (LPSTR) err_prefix, (LPSTR)errbuf);

    /* 在对话框中显示错误文本 */
    SetDlgItemText(hOurDlg, IDD_COMMENT, (LPSTR) prbuf);
   
} /* end wshout_err() */


/* eof */

3.4.3.2 wshout.h清单
/*
* 文件名: WSHOUT.H
*/

#ifndef _WSHOUT_INC_
#define _WSHOUT_INC_

/* Windows 3.0 头文件 */
#include <windows.h>
#define _INC_WINDOWS
#include <winsock.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/* WSHOUT.C 中定义的全局变量 */
extern    HANDLE    hInst;            /* Instance handle */
extern    HWND    hOurWnd;        /* Main Window Handle */
extern    int    ret;            /* work variable */

#define PRBUF_LEN    50
extern    char    prbuf[PRBUF_LEN];    /* work buffer */

/* 菜单IDs    */
#define IDM_START    101
#define IDM_ABOUT    102
#define IDM_STOP    103
#define IDM_EXIT    104
#define IDM_SETTINGS    105

/* 对话框控制IDs    */
#define IDD_HNAME    200
#define IDD_CONNECT    IDOK
#define IDD_CANCEL    IDCANCEL
#define IDD_WRITES    208
#define IDD_BYTES    210
#define IDD_BITS    212
#define IDD_HELP    214
#define IDD_SENT    230
#define IDD_WRITE    232
#define IDD_TIME    234
#define IDD_COMMENT    236
#define IDD_COHOST    240

/* “Settings”对话框控制IDs    */
#define IDS_BUFFLEN    300
#define IDS_PORTNO    301
#define IDS_BLOCK    302
#define IDS_NOBLOCK    304
#define IDS_TCP        306
#define IDS_UDP        308
#define IDS_CLIENT    310
#define IDS_SERVER    312
#define IDS_DEFAULT    314

/* “About”对话框控制IDs    */
#define IDA_COPYRIGHT    400
#define IDA_APP_VERSION    402
#define IDA_DLL_VERSION    404

/* 程序控制IDs    */
#define    WM_SELECT        WM_USER+16

/* 全局变量*/
#define    SOCK_DISCARD    9    /* use the UDP ttytst source port for test */
#define SOCK_SHOUT    32766    /* TCP port used for SHOUT & LISTEN    */
#define BUF_SIZE    8192
#define WRITE_TIMER    1

/* 函数原型 */
int PASCAL WinMain(HANDLE, HANDLE, LPSTR, int);
long FAR PASCAL ShoutWndProc(HWND, WORD, WORD, LONG);
BOOL FAR PASCAL About(HWND, WORD, WORD, LONG);
BOOL FAR PASCAL DialogProc(HWND, WORD, WORD, LONG);
BOOL FAR PASCAL Settings(HWND, WORD, WORD, LONG);
BOOL InitApp(HANDLE);
void CheckThisBoxOn(HWND, int);
void CheckThisProtoBoxOn(HWND, int);
void CheckThisProgBoxOn(HWND, int);
void ClearBoxes(HWND);
SOCKET ResolveAndConnectHost(LPSTR, HWND, int, int);
SOCKET GetSocketAndBind(HWND, int, int);
long UWriteData(SOCKET, HWND, int);
long UReadData(SOCKET, HWND, int);
long TWriteData(SOCKET, HWND, int);
long TReadData(SOCKET, HWND, int);
int ShoutBlockingHook (void);
int PASCAL FAR WSAsperror (HANDLE, int, char far *, int);
void wshout_err (HWND, int, char far *);

#define bcopy(a,b,c)    _fmemcpy(b,a,c)
char * _calloc (unsigned, unsigned);
void _free (void *);

#ifdef _cplusplus
}
#endif /* __cplusplus */

#endif /* ifndef _WSHOUT_INC_ */

/* eof */

3.4.3.3 wshout.rc清单
/*
* 文件名: WSHOUT.RC
*/
#include <windows.h>
#include <winsock.h>
#include "wshout.h"

MainMenu MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Start", IDM_START
        MENUITEM "Sto&p", IDM_STOP
        MENUITEM SEPARATOR
        MENUITEM "E&xit", IDM_EXIT
    END

    POPUP "&Options"
    BEGIN
        MENUITEM "&Settings ...", IDM_SETTINGS
        MENUITEM SEPARATOR
        MENUITEM "&About Shout...", IDM_ABOUT
    END

END

ABOUTBOX DIALOG 22, 17, 144, 102
CAPTION "About Shout for Windows"
STYLE DS_MODALFRAME | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU
BEGIN
    CTEXT "Windows Shout", -1, 29, 5, 85, 8
    CTEXT "Version", -1, 46, 13, 33, 8, SS_CENTER | WS_GROUP
    CTEXT "WINSOCK.DLL \n FTP Software, Inc. \nCopyright 1993", IDA_COPYRIGHT, 38, 40, 68, 25, SS_CENTER | WS_GROUP
    CTEXT "Version", -1, 46, 67, 33, 8, SS_CENTER | WS_GROUP
    CTEXT "num", IDA_DLL_VERSION, 79, 67, 18, 8, SS_CENTER | WS_GROUP
    CONTROL "OK", 1, "BUTTON", BS_DEFPUSHBUTTON | WS_GROUP | WS_TABSTOP, 56, 82, 32, 14
    ICON "SHOUT", -1, 11, 8, 16, 16
    CONTROL "num", IDA_APP_VERSION, "STATIC", SS_LEFT | WS_GROUP, 79, 13, 18, 8
    CONTROL "using", -1, "STATIC", SS_CENTER | WS_GROUP, 55, 26, 30, 8
END

SettingsDialog DIALOG 9, 16, 172, 117
CAPTION "Settings"
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
BEGIN
    CONTROL " send/recv \nBuffer &length", -1, "STATIC", SS_LEFT | WS_GROUP, 84, 8, 48, 20
    CONTROL "&Port number", -1, "STATIC", SS_LEFT | WS_GROUP, 84, 31, 48, 10
    CONTROL "&Blocking", IDS_BLOCK, "BUTTON", BS_AUTOCHECKBOX | WS_TABSTOP, 100, 61, 56, 12
    CONTROL "&TCP", IDS_TCP, "BUTTON", BS_AUTOCHECKBOX | WS_TABSTOP, 20, 60, 41, 12
    CONTROL "&Client", IDS_CLIENT, "BUTTON", BS_AUTOCHECKBOX | WS_TABSTOP, 19, 15, 35, 12
    CONTROL "&Server", IDS_SERVER, "BUTTON", BS_AUTOCHECKBOX | WS_TABSTOP, 19, 26, 35, 12
    CONTROL "&UDP", IDS_UDP, "BUTTON", BS_AUTOCHECKBOX | WS_TABSTOP, 20, 72, 41, 12
    CONTROL "&Nonblocking", IDS_NOBLOCK, "BUTTON", BS_AUTOCHECKBOX | WS_TABSTOP, 100, 73, 56, 12
    CONTROL "O.K.", IDOK, "BUTTON", BS_PUSHBUTTON | WS_TABSTOP, 40, 95, 37, 14
    CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | WS_TABSTOP, 90, 95, 37, 14
    CONTROL "", IDS_BUFFLEN, "EDIT", ES_CENTER | WS_BORDER | WS_TABSTOP, 130, 11, 36, 12
    CONTROL "", IDS_PORTNO, "EDIT", ES_CENTER | WS_BORDER | WS_TABSTOP, 130, 29, 36, 12
    CONTROL "Protocol", 237, "button", BS_GROUPBOX, 6, 49, 70, 38
    CONTROL "I/O Mode", 239, "button", BS_GROUPBOX, 90, 49, 70, 38
    CONTROL "Program Mode", 241, "button", BS_GROUPBOX, 6, 7, 70, 34
END

MainDialog DIALOG 17, 32, 163, 135
CAPTION "Windows Shout"
MENU MainMenu
STYLE DS_ABSALIGN | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
BEGIN
    CONTROL "", IDD_HNAME, "EDIT", ES_CENTER | WS_BORDER | WS_GROUP | WS_TABSTOP, 62, 9, 93, 12
    CONTROL "", IDD_WRITE, "STATIC", SS_CENTER | SS_NOPREFIX | WS_BORDER, 7, 95, 45, 11
    CONTROL "", IDD_SENT, "STATIC", SS_CENTER | WS_BORDER, 59, 95, 45, 11
    CONTROL "", IDD_TIME, "STATIC", SS_CENTER | WS_BORDER, 111, 95, 45, 11
    CONTROL "", IDD_WRITES, "STATIC", SS_CENTER | WS_BORDER, 7, 120, 45, 11
    CONTROL "", IDD_BYTES, "STATIC", SS_CENTER | WS_BORDER, 59, 120, 45, 11
    CONTROL "", IDD_BITS, "STATIC", SS_CENTER | WS_BORDER, 111, 120, 45, 11
    CONTROL "writes[reads]", 105, "STATIC", SS_CENTER | WS_GROUP, 3, 85, 52, 9
    CONTROL "writes[reads]/s", 105, "STATIC", SS_CENTER | WS_GROUP, 3, 111, 55, 9
    CONTROL "bytes", 105, "STATIC", SS_CENTER | WS_GROUP, 61, 85, 42, 9
    CONTROL "bytes/sec", 105, "STATIC", SS_CENTER | WS_GROUP, 61, 111, 42, 9
    CONTROL "time (sec)", 105, "STATIC", SS_CENTER | WS_GROUP, 111, 85, 45, 9
    CONTROL "bits/sec", 105, "STATIC", SS_CENTER | WS_GROUP, 113, 111, 42, 9
    CONTROL "Host", IDD_COHOST, "STATIC", SS_LEFT, 7, 10, 52, 10
    CONTROL "", IDD_COMMENT, "STATIC", SS_CENTER | WS_BORDER | WS_GROUP, 9, 68, 146, 11
    CONTROL "&Start", IDOK, "BUTTON", BS_PUSHBUTTON | WS_TABSTOP, 6, 32, 32, 20
    CONTROL "Sto&p", IDCANCEL, "BUTTON", BS_PUSHBUTTON | WS_TABSTOP, 65, 32, 32, 20
    CONTROL "E&xit", IDM_EXIT, "BUTTON", BS_PUSHBUTTON | WS_TABSTOP, 125, 32, 32, 20
    CONTROL "", -1, "static", SS_BLACKFRAME, 0, 60, 163, 1
END

SHOUT ICON wshout.ico


/*
* 错误描述字符串表
* 用于WSAsperror()函数
*/
STRINGTABLE
BEGIN
WSABASEERR, "[0] No Error"
WSAEINTR, "[10004] Interrupted system call"
WSAEBADF, "[10009] Bad file number"
WSAEACCES, "[10013] Permission denied"
WSAEFAULT, "[10014] Bad address"
WSAEINVAL, "[10022] Invalid argument"
WSAEMFILE, "[10024] Too many open files"
WSAEWOULDBLOCK, "[10035] Operation would block"
WSAEINPROGRESS, "[10036] Operation now in progress"
WSAEALREADY, "[10037] Operation already in progress"
WSAENOTSOCK, "[10038] Socket operation on non-socket"
WSAEDESTADDRREQ, "[10039] Destination address required"
WSAEMSGSIZE, "[10040] Message too long"
WSAEPROTOTYPE, "[10041] Protocol wrong type for socket"
WSAENOPROTOOPT, "[10042] Bad protocol option"
WSAEPROTONOSUPPORT, "[10043] Protocol not supported"
WSAESOCKTNOSUPPORT, "[10044] Socket type not supported"
WSAEOPNOTSUPP, "[10045] Operation not supported on socket"
WSAEPFNOSUPPORT, "[10046] Protocol family not supported"
WSAEAFNOSUPPORT, "[10047] Address family not supported by protocol family"
WSAEADDRINUSE, "[10048] Address already in use"
WSAEADDRNOTAVAIL, "[10049] Can't assign requested address"
WSAENETDOWN, "[10050] Network is down"
WSAENETUNREACH, "[10051] Network is unreachable"
WSAENETRESET, "[10052] Net dropped connection or reset"
WSAECONNABORTED, "[10053] Software caused connection abort"
WSAECONNRESET, "[10054] Connection reset by peer"
WSAENOBUFS, "[10055] No buffer space available"
WSAEISCONN, "[10056] Socket is already connected"
WSAENOTCONN, "[10057] Socket is not connected"
WSAESHUTDOWN, "[10058] Can't send after socket shutdown"
WSAETOOMANYREFS, "[10059] Too many references, can't splice"
WSAETIMEDOUT, "[10060] Connection timed out"
WSAECONNREFUSED, "[10061] Connection refused"
WSAELOOP, "[10062] Too many levels of symbolic links"
WSAENAMETOOLONG, "[10063] File name too long"
WSAEHOSTDOWN, "[10064] Host is down"
WSAEHOSTUNREACH, "[10065] No Route to Host"
WSAENOTEMPTY, "[10066] Directory not empty"
WSAEPROCLIM, "[10067] Too many processes"
WSAEUSERS, "[10068] Too many users"
WSAEDQUOT, "[10069] Disc Quota Exceeded"
WSAESTALE, "[10070] Stale NFS file handle"
WSAEREMOTE, "[10071] Too many levels of remote in path"
WSASYSNOTREADY, "[10091] Network SubSystem is unavailable"
WSAVERNOTSUPPORTED, "[10092] WINSOCK DLL Version out of range"
WSANOTINITIALISED, "[10093] Successful WSASTARTUP not yet performed"
WSAHOST_NOT_FOUND, "[11001] Host not found"
WSATRY_AGAIN, "[11002] Non-Authoritative Host not found"
WSANO_RECOVERY, "[11003] Non-Recoverable errors: FORMERR, REFUSED, NOTIMP"
WSANO_DATA, "[11004] Valid name, no data record of requested
type"
END

/* eof */

3.4.3.4 ushout.c清单
/*
* 文件名: USHOUT.C
*/

#include "wshout.h"

/* MSC Include files: */
#include <stdio.h>
#include <io.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

/* Returns the number of bytes written */
long UWriteData(SOCKET hSock, HWND hOurDlg, int send_len)
{
int counter;
static int DataBuffer[BUF_SIZE]; /* Buffer to hold generated data    */
static char ReplyBuffer[512]; /* Buffer to hold any reply rcvd */
long bytes_sent = 0L;    /* Counter of bytes on connection    */
long total_len = 1024L*1024L; /* Total # of bytes to generate    */
time_t start, end;        /* variables to hold read timing    */
long total_time = 0L;    /* variable to hold delta t    */
long write_count = 0L;    /* number of times        */
long tmp = 0L;        /* holds count for bytes written    */
long ltemp = 0L;
int i_temp;
extern int run_cancelled;

struct    sockaddr_in dest; /* Destination machine address structure */

/* What makes shout unique is that it generates data*
* in memory (as opposed to accessing the disk).    *
* This tests the 'raw' speed of the TCP connection    *
* as the rate-limiting access time is eliminated.    *
* First, generate the data and place it into an    *
* array, data_buffer:                */

for (counter = 0; counter < BUF_SIZE; counter++)
    DataBuffer[counter] = counter;

/* Write data on the descriptor like a banshee,
* careful to time the writes and count data
* transmitted:
*/

SetDlgItemText(hOurDlg, IDD_COMMENT, "Sending UDP Data ...");
time( &start );   
while (bytes_sent < total_len){/* while still bytes to send */
    do {
    ;
    } while (ShoutBlockingHook()); /* Dispatch messages if any */
   
    if (run_cancelled) {
    WSASetLastError(WSAEINTR);
    break;    /* Non-blocking mode was cancelled */
    }
   
    tmp = send(hSock, (char FAR *) &DataBuffer, send_len, 0);
    if (tmp == SOCKET_ERROR) {
    if (h_errno == WSAEWOULDBLOCK) /* if no data, read again */
        continue;
    else {
        wshout_err (hOurDlg, WSAGetLastError(), "send()");
    }
   
    /* Calc. time elapsed & stats about any data sent */
    time(&end);
    if (total_time = (long) difftime(end, start)) {
        /* Print the statistics gathered */
        wsprintf((LPSTR)prbuf,"%ld\n",write_count);
        SetDlgItemText(hOurDlg, IDD_WRITE, (LPSTR) prbuf);

        wsprintf((LPSTR)prbuf,"%ld\n",bytes_sent);
        SetDlgItemText(hOurDlg, IDD_SENT, (LPSTR) prbuf);

        wsprintf((LPSTR)prbuf,"%ld\n",total_time);
        SetDlgItemText(hOurDlg, IDD_TIME, (LPSTR) prbuf);
       
        ltemp = write_count/total_time;
        wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
        SetDlgItemText(hOurDlg, IDD_WRITES,(LPSTR) prbuf);
       
        ltemp = bytes_sent/total_time;
        wsprintf((LPSTR)prbuf,"%ld\n", ltemp);
        SetDlgItemText(hOurDlg, IDD_BYTES, (LPSTR) prbuf);
       
----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.17:57:24   MSIE 6.0 Windows XPIP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 


第四章 Windows Socket 1.1库函数概览

4.1 套接口函数
    Windows Sockets规范包含了以下Berkeley风格的套接口例程:

    accept()* 响应联结请求,并且新建一个套接口。原来的套接口则返回监听状态。

    bind() 把一个本地的名字和一个无名的套接口捆绑起来。

    closesocket()* 把套接口从拥有对象参考表中取消。该函数只有在SO_LINGER被设置时才会阻塞。

    connect()* 初始化到一个指定套接口上的连接。

    getpeername() 得到连接在指定套接口上的对等通讯方的名字。

    getsockname() 得到指定套接口上当前的名字。

    getsockopt() 得到与指定套接口相关的属性选项。

    htonl() 把32位的数字从主机字节顺序转换到网络字节顺序。

    htons() 把16位的数字从主机字节顺序转换到网络字节顺序。

    inet_addr() 把一个Internet标准的"."记号地址转换成Internet地址数值。

    inet_ntoa() 把Internet地址数值转换成带"."的ASCII字符串。

    ioctlsocket() 为套接口提供控制。

    listen() 监听某一指定套接口上连接请求的到来。

    ntohl() 把32位数字从网络字节顺序转换为主机字节顺序。

    ntons() 把16位数字从网络字节顺序转换为主机字节顺序。

    recv()* 从一个已连接的套接口接收数据。

    recvfrom()* 从一个已连接的或未连接的套接口接收数据。

    select()* 执行同步I/O多路复用。

    send()* 从一已连接的套接口发送数据。

    sendto()* 从已连接或未连接的套接口发送数据。

    setsockopt() 设置与指定套接口相关的属性选项。

    shutdown() 关闭一部分全双工的连接。

    socket() 创建一个通讯端点并返回一个套接口。

    *表示例程在应用于阻塞套接口时会阻塞。

4.1.1 阻塞/非阻塞和数据易失性
    阻塞是在把应用程序从Berkeley套接口环境中移植到Windows环境中的一个主要焦点。阻塞是指唤起一个函数,该函数直到相关操作完成时才返回。由于操作可能需要任意长的时间才能完成,于是问题就出现了。最明显的一个例子就是recv(),这个函数会一直处于阻塞状态直到收到对方系统发送的数据。在Berkeley套接口模型中,一个套接口的操作的缺省行为是阻塞方式的,除非程序员显式地请求该操作为非阻塞方式。我们强烈推荐程序员在尽可能的情况下使用非阻塞方式(异步方式)的操作。因为非阻塞方式的操作能够更好地在非占先的Windows环境下工作。程序员应该在绝对必要的时候才采用阻塞方式。而且在你必须使用阻塞方式的操作前仔细阅读并理解这一部分。
    即使在阻塞方式下,有些操作(例如bind(),getsockopt(),getpeername())也会立刻完成。对于这些操作,阻塞方式和非阻塞方式并没有什么两样。其他一些操作(例如recv())可能立刻完成,也可能需要阻塞一段随机的时间才能完成。这都取决于不同的传输情况。当用于阻塞套接口时,这些操作被认为是工作于阻塞方式的,所有会阻塞的例程在以前或以后的列表中都打上了星号作标记。
    在Windows Sockets实现中,一个无法立刻完成的阻塞操作是按如下方式处理的。DLL先初始化操作,然后进入一个循环,在循环中发送收到的任何信息-为了使在必要时把处理器交给其他线程,然后检查Windows Sockets功能是否完成。如果功能完成了,或者WSACancelBlockingCall()被唤起,阻塞操作以一个适当的返回值结束。完整的关于这种机制的描述,请参见5.3.13节,WSASetBlockingHook(),这一部分还包括了对于各种函数伪代码的讨论。
    如果一个正在运行某一阻塞操作的进程收到了一个Windows消息,那么应用程序有可能试图发出另一个Windows Sockets调用,由于很难安全地处理这种情形,Windows Sockets规范不支持这种应用程序的工作方式。在这种情况下,有两个函数可以帮助程序员。WSAIsBlocking()可以用来确定在该进程上是否有阻塞的Windows Sockets调用。WSACancelBlookingCall()可以用来取消在线的阻塞调用,如果有的话。任何其他的Windows Sockets函数如果在这种情况下被调用,则会失败并返回错误代码WSAEINPROGRESS。要强调的是,这一限制适用于所有阻塞和非阻塞的操作。
    虽然这种机制对于简单的应用程序已经足够了,但这不能支持高级应用程序的复杂的消息发送要求。(例如,那些MDI模型的用户)对于这样的应用程序,Windows Sockets API设计了WSASetBlockingHook()函数,这个函数可以允许程序员定义特殊的阻塞钩子来代替上面讨论的缺省消息发送例程。
    只有在以下都为真时,Windows Sockets DLL才调用阻塞钩子函数:例程是被定义为可以阻塞的,指定的套接口也是阻塞套接口,而且请求不能被立刻完成。(套接口是被缺省地设为阻塞方式的,但IOCTL FIONBIO和WSAAsyncSelect()都可以把套接口设置成为非阻塞模式)。如果应用程序只使用非阻塞方式的套接口,而且使用WSAAsyncSelect()和/或WSAAsyncGetXByY()例程,而不是使用select()和/或getXbyY()例程,那么阻塞钩子函数就永远也不会被调用,应用程序也不用再操心由于阻塞钩子函数而带来的重入问题。
    如果一个应用程序在唤起异步或非阻塞方式调用时使用了一个内存对象的指针(例如一个缓冲区,或者一个全程变量)作为参数,那么应用程序要保证那个对象在Windows Sockets实现的整个操作中都可得到并使用。应用程序不能再唤起可能影响到内存唤射或寻址能力的其他的Windows函数。在多线程系统中,应用程序也有责任使用某种同步机制来协调对内存对象的存取。Windows Sockets实现不能,也不会提出这种事情。没有遵守这条规则,所可能产生的后果已不在规范讨论的范围之内。

4.2 数据库函数
    Windows Sockets规范定义了如下数据库例程。正如我们先前提出的,Windows Sockets提供者有可能不采用依赖于本地数据库的方式来实现这些函数。某些数据库例程返回的指针(例如gethostbyname())指向的区域是由Windows Sockets函数库分配的。这些指针指向的数据是易失的。它们只在该线程的下一个Windows Sockets API调用前有效。此外,应用程序不应试图修改这个结构,或者释放其中的任何一部分。在一个线程中,这个结构只有一份拷贝。因此,应用程序应该在发出下一个Windows Sockets API调用以前把所需的信息拷贝下来。

    gethostbyaddr()* 从网络地址得到对应的名字(有可能多个)和地址。

    gethostbyname()* 从主机名得到对应的名字(有可能多个)和地址。

    gethostname() 得到本地主机名。

    getprotbyname()* 从协议名得到对应的协议名和数值。

    getservbyname()* 从一个服务的名字得到对应的服务名以及端口号。

    getservbyport()* 从一个端口号得到对应的服务名以及端口号。

    *表示例程在某些情况下可能会阻塞。

4.3 针对Microsoft Windows的扩展函数
    Windows Sockets规范提供了许多在标准的Berkelet套接口例程之外的扩展函数。本质上,这些扩展的API是为了应用程序能更好地处理基于消息的异步发送的网络事件。虽然基于Windows Sockets的编程并不强制要使用这个扩展的API集(WSAStartup()和WSACleanup()除外)但我们推荐应用程序开发者遵循Microsoft Windows的编程范例。

    WSAAsyncGetHostByAddr() 一个标准的Berkeley的getXbyY()函数集合的异步版本。例如WSAAsyncGetHostByName()函数提供了一个标准Berkeley的gethostbyname()函数的异步基于消息的实现。

    WSAAsyncGetHostByName()

    WSAAsyncGetProtoByName()

    WSAAsyncGetProtByNumber()

    WSAAsyncGetServByName()

    WSAAsyncGetServByPort()

    WSAAsyncSelect() select()函数的异步版本。

    WSACancelAsyncRequest() 取消一个未完成的WSAAsyncGetXByY()函数的实例。

    WSACancelBlockingCall() 取消未完成的阻塞的API调用。

    WSACleanup() 从底层的Windows Sockets DLL中撤销注册。

    WSAGetLastError() 得到最近的一个Windows Sockets API调用错误的详细情况。

    WSAIsBlocking() 确定底层的Windows Sockets DLL是否在该线程已经被一个调用阻塞。

    WSASetBlockingHook() 为底层的Windows Sockets实现设置阻塞钩子。

    WSASetLastError() 设置下一次WSAGetLastError()返回的错误信息。

    WSAStartup() 初始化底层的Windows Sockets DLL。

    WSAUnhookBlockingHook() 恢复原始的阻塞钩子。

4.3.1 异步选择机制
    WSAAsyncSelect()调用允许应用程序程序注册一个或多个感兴趣的网络事件。这一API调用用来取代探寻网络I/O调用。在select()或非阻塞I/O例程(例如send()和recv())已经被调用或将要被调用的情况下都可以使用WSAAsyncSelect()调用。在这种情况下,在声明感兴趣的网络事件时,你必须提供一个通知时使用的窗口句柄。那么在你声明的感兴趣的网络事件发生时,对应的窗口将收到一个基于消息的通知。
    Windows Sockets允许对于一特定的套接口声明如下感兴趣的网络事件:
    *套接口已准备读数据。
    *套接口已准备写数据。
    *带外数据准备好。
    *套接口准备接收连接请求。
    *非阻塞连接操作结束。
    *连接关闭。

4.3.2 异步支持例程
    异步数据库函数允许应用程序用异步方式请求信息。某些网络实现和/或配置,需要通过执行网络操作来应答这些请求。WSAAsyncGetXByY()函数允许应用程序开发者不必象在使用Berkeley标准函数时阻塞整个Windows环境。WSACancelAsyncRequest()函数可以允许一个应用程序取消任何未完成的异步的WSAAsyncGetXByY()请求。

4.3.3 阻塞钩子函数方法
    正如4.1.1节所讲述的,Windows Sockets实现以这样一种方式阻塞一个操作,Windows消息处理可以继续,发出调用的应用程序仍然可以收到Windows消息。但在某些情况下,应用程序可能希望影响或改变这种伪阻塞的实现方式。WSASetBlockingHook()函数就提供了这样一种功能。它使得应用程序可以替换Windows Sockets实现在“阻塞”操作中放弃处理器时调用的例程。

4.3.4 错误处理
    为了与基于线程的环境兼容,API调用的错误细节可以通过WSAGetLastError()调用得到。虽然已经为大家接收的Berkeley风格的机制是通过"errno"得到关于套接口的网络错误的,这种机制不能够保证在多线程环境中错误代码的完整性和正确性。WSAGetLastError()允许程序员能够得到对应于每一线程的最近的错误代码。
    WSAGetLastError()所返回的错误代码尽量避免与标准的Microsoft C错误代码冲突。但是某些Windows Sockets例程返回的错误代码是在Microsoft C定义的标准错误代码之内的。如果你使用的应用程序开发环境中的错误代码定义与Microsoft C不同,那么我们建议你使用Windows Sockets错误代码前缀"WSA"来保证准确的检测错误。
    这份规范定义了一个推荐的错误代码的集合,而且列举了每一个函数有可能返回的错误。但是某些Windows Sockets实现也有可能返回一些在我们列举之外的错误代码。应用程序应该具备能够处理在每个API描述下列举的错误代码之外的错误的能力。然而Windows Sockets实现不能返回在附录4.1中列举的合法Windows Sockets错误之外的任何数值。

4.3.5 通过中介DLL调用Windows Sockets DLL
    Windows Sockets DLL既可以直接从应用程序中调用也可以通过中介DLL调用。通过中介DLL的例子是:使用Windows Sockets为应用程序实现一个提供通用网络功能的虚拟网络API层,这样的DLL可以同时被多个应用程序使用。担这样的DLL必须对WSAStartup()和WSACleanup()这两个函数非常警惕,它们必须保证在任何一个使用Windows Sockets调用的任务前后均调用了WSAStartup()和WSACleanup()。这是因为Windows Sockets DLL需要一个对WSAStartup()的调用来为每个任务初始化其数据结构,也需要一个对WSACleanup()的调用来释放为任务分配的所有资源。
    有至少两种方法去完成这一任务。最简单的方法是中介DLL具有与WSAStarup()和WSACleanup()类似的调用提供给应用程序使用,DLL将在这些例程中调用WSAStartup()和WSACleanup()。另一种机制就是中介DLL建立一个任务句柄列表。任务句柄是从GetCurrentTask()这一个Windows API中获得的。在中介DLL的每一个入口处检查对于当前任务WSAStartup()函数是否已被调用,并且在必要的时候调用WSACleanup()函数。
    在Windows NT环境中,这一点是没有必要的。因为Windows NT中的DLL结构与流程是与Windows不同的。在Windows NT中,中介DLL只需简单的在它的DLL初始化例程中调用WSAStartup()即可。这个例程将在任何一个新的进程试图使用DLL的开始时刻被执行。
    如果中介DLL调用了阻塞操作而又没有安装任何它自己的阻塞钩子,那么DLL作者必须清楚地认识到控制有可能会通过应用程序安装的阻塞钩子或缺省的阻塞钩子回到应用程序手中。这样应用程序有可能通过WSACancelBlockingCall()来取消中介DLL的阻塞操作,如果这种情况发生了,中介DLL的阻塞操作会失败并返回错误代码WSAEINTR。这时候,中介DLL必须尽快地把控制交还给调用它的任务。因为用户有可能按了Cancel或者Close按钮。应用程序正在急切地盼望获得CPU的控制权。我们推荐中介DLL在进行阻塞调用时安装自己的阻塞钩子来防止不可预见的中介DLL和应用程序之间的互相影响。

4.3.6 Windows Sockets实现内部对消息的使用
    为了把Windows Sockets实现成一个纯粹的DLL,有时在DLL内部互相发送消息来通讯和定时是必要的。这是非常合法的。但是Windows DLL不应该无缘无故地发送消息给一个由客户打开的窗口句柄,除非应用程序要求这些消息。所以为了自身的目的而需要使用消息的Windows Sockets DLL打开了一个隐藏的窗口,并且发送必要的消息给这个窗口的句柄。

4.3.7 私有的API接口
    附录B.3中的WINSOCK.DEF文件列出了Windows Sockets API功能调用的序数。除了已经列出的序数值外,所有小于999的序数都是保留给将来的Windows Sockets使用的。对于一个Windows Sockets实现来说,提供附加的私有的接口也是很方便的。这是完全可以接受的,只要这些调用的序数大于1000,要注意的是,任何使用了某个特定Windows Sockets DLL私有的API的应用程序极有可能在任何其他供应商的Windows Sockets DLL上无法工作。应该注意到,只有使用在这份规范中定义的API才能可以保证每一个Windows Sockets实现都支持。
    如果一个应用程序使用了某个供应商的Windows Sockets DLL的特定接口,最好不要把应用程序与DLL静态连接,而通过Windows Sockets例程LoadLibrary()和GetProcAddress()动态载入,这就使得应用程序在其他不支持同样的扩展功能集的Windows Sockets DLL系统上运行时,可以得到适当的错误信息。



----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.17:58:18   MSIE 6.0 Windows XPIP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 


第六章 Windows Socket 2的扩展特性
    这一章将讨论从Windows Sockets 1.1到Windows Socket 2的主要变动。

6.1 同时使用多个传输协议
    为了用户能够同时使用多个传输协议,在Windows Socket 2中,结构有所改变。在Windows Sockets 1.1中,软件开发商所提供的DLL实现了Windows Sockets的API和TCP/IP协议栈。Windows Sockets DLL和底层协议栈的接口是唯一而且独占的。Windows Socket 2改变了这种模型:它定义了一个Windows Sockets DLL和底层协议栈间的标准服务提供接口(SPI)。这使得一个Windows Sockets DLL能够同时访问不同软件开发商的多个底层协议栈。此外,Windows Sockets 2并不象Windows Sockets 1.1仅支持TCP/IP协议栈。与Windows开放系统结构(WOSA)兼容的Windows Sockets 2的结构如下图:



图6-1:Windows Socket 2开放系统结构图

    注意:16位的Windows Sockets 2应用程序应使用WS2-16.DLL,而32位的Windows Sockets 2应用程序应使用WS2-32.DLL。但今后,为了简单起见,它们将都使用WINSOCK.DLL。这并不会造成任何问题,因为在它们之间并没有任何语法上的区别。
    由于以上的结构,现在已没有必要每个协议栈开发商都提供它们自己的WINSOCK.DLL(甚至这样做也不是期望的)。因为任何一个WINSOCK.DLL能够在所有协议栈上工作。因此,WINSOCK.DLL可以被看作是一个操作系统组件。Microsoft将在Windows 95和Windows NT上提供一个32位的WINSOCK.DLL。Intel公司目前正在打算提供Windows 3.1和Windows 3.11上的Windows Sockets 2兼容的16位WINSOCK.DLL。

6.2 与Windows Socket 1.1应用程序的向后兼容性
    Windows Socket 2与Windows Sockets 1.1在两个基础上向后兼容:源码和二进制代码。这就实现了Windows Sockets应用程序和任何版本的Windows Sockets实现之间的最大的互操作性,而且也减少了Windows Sockets应用程序使用者,网络协议栈提供者和服务提供者的许多痛苦。现有的Windows Sockets 1.1兼容的应用程序可以在Windows Sockets 2实现上不加修改的运行,只要有一个TCP/IP协议栈被安装。

6.2.1 源码的兼容性
    Windows Sockets 2中的源码兼容性意味着所有的Windows Sockets 1.1版的API在Windows Sockets 2中都被保留了下来。这意味着现有的Windows Sockets 1.1应用程序的原程序可以被简单的移植到Windows Sockets 2系统上运行。程序员需要做的只是包含新的头文件-WINSOCK2.H和简单的与合适的Windows Sockets 2函数库的连接。应用程序开发者应该把这种工作看作是完全转向Windows Sockets 2的第一步,因为有许多方式可以使用Windows Sockets 2中的新函数来提高原来的Windows Sockets 1.1应用程序的运行性能。

6.2.2 二进制兼容性
    在设计Windows Sockets 2时的一个主要目标就是使得现有的Windows Sockets 1.1应用程序在二进制级别上能够不加修改的应用于Windows Sockets 2之上。由于Windows Sockets 1.1是基于TCP/IP上的,二进制兼容性就要求Windows Sockets 2系统提供基于TCP/IP上的Windows Sockets 2的传输和名字解析服务。为了Windows Sockets 1.1应用程序能在这种意义上运行,Windows Sockets 2系统提供了一个附加的组件-Windows Sockets 1.1的DLL。Windows Sockets 2安装时的提示保证了在终端机用户引进Windows Sockets 2系统时不会对已有的Windows Sockets软件环境有任何影响。



图6-2:与Windows Sockets 1.1二进制兼容性结构图

    一个完全的Windows Sockets 1.1二进制兼容的必要的前提是在系统上已经安装了至少一个TCP/IP协议栈并且在Windows Sockets 2中做了注册。Windows Sockets 1.1目前通过WSAData结构中的某些元素来得到关于底层TCP/IP协议栈的信息(例如通过WSAStartup()函数调用),这些信息包括iMaxSockets,iMaxUdpDg和IPVendorInfo。但是在Windows Sockets 2中,应用程序应该知道忽略这些信息,因为这些值不能统一地适用于所有协议栈。不过DLL必须仍然提供这些值以免破坏Windows Sockets 1.1的应用程序。这些信息只能从(因此也只能应用于)缺省的TCP/IP服务提供者得到。缺省的TCP/IP服务提供者是由WSAEnumProtocols()调用返回的PROTOCOL_INFO结构缓冲区的第一条TCP/IP协议栈。

6.3 在Windows Sockets中注册传输协议
    要使Windows Sockets能够利用一个传输协议,该传输协议必须在系统上安装并且在Windows Sockets中注册。Windows Sockets 2的DLL包含了一组API来完成这个注册过程。这个注册过程包括建立一个新的注册和取消一个已有的注册。在建立新的注册时,调用者(假设是协议栈开发商的安装程序)必须提供一组或多组完整的关于协议的信息,这些信息将被用来填充PROTOCOL_INFO结构。

6.3.1 使用多个协议
    一个应用程序可以通过WSAEnumProtocols()功能调用来得到目前有多少个传输协议可以使用,并且得到与每个传输协议相关的信息,这些信息包含在PROTOCOL_INFO结构中。然而,某些传输协议可能表现出多种行为。例如SPX是基于消息的(发送者发送的消息的边界在网络上被保留了),但是接收的一方可以选择忽略这些边界并把套接口作为一个字节流来对待。这样就很合理地导致了SPX有两个不同的PROTOCOL_INFO结构条目,每一个条目对应了一种行为。
    在Windows Sockets 1中仅有一个地址族(AF_INET),它包含了数量不多的一些众所周知的套接口类型和协议标识符。这在Windows Sockets 2中已经有所改变。除了现有的地址族,套接口类型和协议标识符为了兼容性原因被保留以外,Windows Sockets 2加入了许多唯一的但是可能并不为大家所知的地址族,套接口类型和协议标识符。不为大家所知并不意味着会对应用程序开发造成问题,因为一个企图做成协议无关的应用程序应该在对自身合适的基础上选择协议而不应该依赖于某个分配给它的特定的套接口类型或协议类型值。PROTOCOL_INFO结构中包含的通讯性质指明了协议的合适性(例如:基于消息的对应于基于字节流的,可靠的对应于不可靠的,等等)。基于合适性原则选取协议而不使用某个特定的协议名和套接口类型。
    对于客户机/服务器模型,服务器一端的应用程序最好能够在所有合适的传输协议上建立监听套接口。这样,客户机一端的应用程序就可以通过任何合适的传输协议来与服务器一端的应用程序建立连接。这样做可以使得一个客户机应用程序易于移植。例如一台运行于LAN上的台式机的客户机应用程序在转到运行于无线网上的笔记本计算机时就不用作任何改变。

6.3.2 select()函数应用中关于多个服务提供者的限制
    在Windows Sockets 2中,函数select()使用FD_SET仅能应用于和单个服务提供者相连的套接口。但是这并不限制一个应用程序使用多个服务提供者打开多个套接口。如果应用程序开发者喜欢使用非阻塞方式编程,那么可以使用WSAAsyncSelect()函数。由于该函数需要一个套接口描述字作为输入参数,那么与该套接口相连的服务提供者是很重要的。如果一个应用程序需要在一组跨越多个服务提供者的套接口上使用带有阻塞语法的函数,那么应该使用WSAWaitForMultipleEvents()函数。应用程序也可以使用WSAEventSelect()函数。该函数允许应用程序把FD_XXX网络事件和一个事件对象相连接,并且在该事件对象中处理网络事件(这一模式将在下文讨论)。

6.4 协议无关的名字解析
    Windows Sockets 2包含了应用程序可以使用的多种标准化的网络名字服务。Windows Sockets 2应用程序并不需要理解与名字服务相关的许多迥异的接口(例如DNS,NIS,X.5000,SAP等等)。本书的4.2介绍了这一主题,并对API做了详细介绍。

6.5 重叠I/O和事件对象
    Windows Sockets 2引入了重叠I/O的概念并且要求所有的传输协议提供者都支持这一功能。重叠I/O仅能在由WSASocket()函数打开的套接口上使用(使用WSA_FLAG_OVERLAPPED标记)。这种方式的使用将采用Win32建立的模型。
    对于接收,应用程序使用WSARecv()函数或WSARecvFrom()函数来提供存放接收数据的缓冲区。如果数据在网络接收以前,应用程序已经提供了一个或多个数据缓冲区,那么接收的数据就可以立即被存放进用户缓冲区。这样可以省去使用recv()函数和recvfrom()函数时需要进行的拷贝工作。如果在应用程序提供数据缓冲区时已经有数据到来,那么接收的数据将被立即拷贝进用户缓冲区。如果数据到来时,应用程序没有提供接收缓冲区,那么网络将回到我们熟悉的同步操作方式-传送来的数据将被存放进内部缓冲区,直到应用程序发出了接收调用并且提供了接收缓冲区,这时接收的数据就被拷贝进接收缓冲区。这种做法会有一个例外:就是当应用程序使用setsockopt()函数把接收缓冲区长度置为了0。在这种情况下,对于可靠传输协议,只有在应用程序提供了接收数据缓冲区后,数据才会被接收;而对于不可靠传输协议,数据将会丢失。
    对于发送的一方,应用程序使用WSASend()函数或WSASendTo()函数提供一个指向已填充的数据缓冲区的指针。应用程序不应在网络使用完该缓冲区的数据以前以任何方式破坏该缓冲区的数据。
    重叠发送和接收调用会立即返回。如果返回值是0,那么表明了I/O操作已经完成,对应的完成指示也已经可以得到。如果返回值是SOCKET_ERROR,并且错误代码是WSA_IO_PENDING,那么表明重叠操作已经被成功地初始化,今后发送缓冲区被用完或者接收缓冲区被填满时,将会有完成指示。任何其他的错误代码表明了初始化没有成功,今后也不会有什么完成指示。
    发送操作和接收操作都可以被重叠使用。接收函数可以被多次调用,发出接收缓冲区,准备接收到来的数据。发送函数也可以被多次调用,组成一个发送缓冲区队列。要注意的是,应用程序可以通过按顺序提供发送缓冲区来确保一系列重叠发送操作的顺序,但是对应的完成指示有可能是按照另外的顺序排列的。同样的,在接收数据的一方,缓冲区是按照被提供的顺序填充的,但是完成指示也可能按照另外的顺序排列。
    WSAIoctl()函数(ioctolsocket()函数的增强版本)还可以使用重叠I/O操作的延迟完成特性。

6.5.1 事件对象
    重叠I/O概念的引入需要建立一个机制使得应用程序能够正确的把发送和接收事件与今后它们完成时的指示相连接。在Windows Sockets 2中,这一点是通过事件对象实现的,它采用了Win32事件的模型。Windows Sockets事件对象是一个相当简单的结构,它可以被创建,关闭,设置,清除,等待和检查。它们的主要用处是使得应用程序能够阻塞并等待直到一个或多个事件对象被设置。
    应用程序可以使用WSACreateEvent()函数来得到一个事件对象句柄,这个句柄可以作为以后的重叠发送和接收函数的输入参数(WSASend(),WSASendTo(),WSARecv(),WSARecvFrom())。事件对象在创建时被清除,在相关的重叠I/O操作完成时由传输协议提供者设置(或者成功,或者出错)。每个被WSACreateEvent()函数创建的事件对象都必须有对应的WSACloseEvent()函数释放它。
    WSAEventSelect()函数把一个或多个FD_XXX网络事件与一个事件对象连接。这将在2.6中讨论。
    在32位环境中,与事件对象相关的函数,包括WSACreateEvent(),WSACloseEvent(),WSASetEvent(),WSAResetEvent(),WSAWaitForMultipleEvent()和WSAGetOverlappedResult(),都被直接映射到对应的Win32函数,例如没有WSA前缀的同名函数。

6.5.2 接收操作完成指示
    为了提供给应用程序适当的灵活性,Windows Sockets 2为接收操作完成指示提供了多个选项。它们包括:等待(阻塞)事件对象,检查事件对象和套接口I/O完成例程。

6.5.2.1 阻塞并且等待完成指示。
    应用程序可以使用WSAWaitForMultipleEvents()函数来选择阻塞程序直到一个或多个事件对象被设置。在Win16实现中,这种方式将使用一个阻塞钩子,就象在标准的阻塞套接口操作时一样。在Win32实现中,进程或线程会被真正地阻塞。因为Windows Sockets 2事件对象被实现成Win32事件,所以Win32函数WaitForMultipleObjects()也可以使用。这在线程需要阻塞套接口和非套接口事件时将会非常有用处。

6.5.2.2 检查完成指示
    应用程序如果不希望使用阻塞方式,它可以使用WSAGetOverlappedResults()函数来检查与某个特定的事件对象相连的完成状态。该函数检查重叠操作是否完成,如果完成的话,处理重叠操作的出错信息,使得该信息在WSAGetLastError()函数调用时可以得到。

6.5.2.3 使用套接口I/O操作完成例程
    所有的用来初始化重叠I/O操作的函数(WSASend(),WSASentTo(),WSARecv(),WSARecvFrom())都把lpCompletionRoutine作为输入参数。这是一个应用程序定义的函数指针,在重叠I/O操作完成时可以被调用。
    在Win16环境中,回调函数有可能在VMM环境(有时也被称作中断环境)下被激活。传送对时间要求较高的数据(例如视频或音频数据)在这种低延时,占先方式下接受这种指示会很方便。但是应用程序必须知道,在这种特殊情况下,只要很少一部分运行时和Windows库函数可以被调用。作为一条规则,应用程序必须把自己限制在一套Windows文挡说明的在一个多媒体定时回调函数中可以被安全调用的运行时函数库。
    在Windows 95和Windows NT中,完成例程与Win32文件I/O完成例程遵循着同样的规则。在所有的环境中,传输协议允许应用程序以完成例程的方式唤起发送和接收操作,而且保证对于给定的一个套接口,I/O完成例程不会嵌套。这就允许了在一个占先的环境中进行对时间敏感的数据的传送。

6.5.3 WSAOVERLAPPED的细节
WSAOVERLAPPED结构提供了一个重叠I/O操作的初始化和它将来的如何被完成之间的通讯媒体。WSAOVERLAPPED结构被设计成与Win32中的OVERLAPPED结构兼容:
    typedef struct WSAOVERLAPPED {
        DWORD Internal; // reserved
        DWORD InternalHigh; // reserved
        DWORD Offset; // ignored
        DWORD OffsetHigh; // ignored
        WSAEVENT hEvent;
    } WSAOVERLAPPED, LPWSAOVERLAPPED;
   
    Internal 这一个保留的域是由重叠I/O实现的实体内部使用的。对于使用类文件方式创建套接口的传输服务提供者,这一域是被底层的操作系统使用的;对于其他的传输服务提供者(那些创建伪句柄的),可以视需要使用这个域。
   
    InternalHigh 这一个保留的域是由重叠I/O实现的实体内部使用的。对于使用类文件方式创建套接口的传输服务提供者,这一域是被底层的操作系统使用的;对于其他的传输服务提供者(那些创建伪句柄的),可以视需要使用这个域。

    Offset 由于套接口没有文件偏移量的概念,应用程序可以视需要使用这个域。

    OffsetHigh 由于套接口没有文件偏移量的概念,应用程序可以视需要使用这个域。

    hEvent 如果一个重叠的I/O操作在被调用时没有使用I/O操作完成例程(lpCompletionRoutine为空指针),那么这个域必须包含一个有效的WSAEVENT对象的句柄,否则(lpCompletionRoutine不为空指针),应用程序可以视需要使用这个域。

6.6 使用事件对象异步通知
    为了适应一些应用程序例如精灵程序或者某些没有用户界面的服务程序(因此不使用窗口句柄),Windows Socket 2提供了WSAEventSelect()函数和WSAEnumNetworkEvents()函数。WSAEventSelect()函数和WSAAyncSelect()函数很类似,区别仅在于当一个FD_XXX网络事件发生时,WSAEventSelect()函数将导致一个应用程序指定的事件对象被设置,而WSAAyncSelect()将导致一条Windows消息被发送(例如FD_READ,FD_WRITE等等)。
    此外,传输服务提供者会记住每个特定的FD_XXX网络事件的发生。应用程序可以调用WSAEnumNetworkEvents()函数把目前的网络事件记忆拷贝到应用程序提供的缓冲区中,并且自动清除网络事件记忆。如果需要,应用程序还可以把某个特定的事件对象和网络事件记忆一起清除。

6.7 服务的质量(QOS)
    Windows Sockets 2中的QOS机制是从Craig Partridge在RFC 1363中描述的流规格引入的。这一概念可以大致描述如下:
    流规格描述了一个网络上单向数据流的性质的集合。应用程序可以在调用WSAConnect()函数发出连接请求或者使用WSAIoctl()函数等其他QOS命令时,把一对流规格和一个套接口连接(一个规范对应了一个方向)。流规格以参数方式声明了应用程序所要求的服务的级别,并且为应用程序适应不同的网络条件提供了一套反馈机制-如果应用程序要求的服务级别不能达到,应用程序是否愿意松动它的要求。
    Windows Sockets 2中QOS的使用模型如下:
    对于基于连接的传输服务,应用程序可以很方便的在使用WSAConnect()函数提出连接请求时规定它所要求的服务质量(QOS)。要注意的是:如果应用程序在调用WSAConnect()时QOS参数不为空,那么对于基于连接的套接口,任何预先设置的QOS都会被覆盖。如果WSAConnect()函数成功返回,应用程序就会知道它所要求的QOS已经被网络接受,那么应用程序就可以随意的使用这个套接口进行数据交换。如果连接操作由于资源有限而失败,应用程序应该适当地降低它所要求的服务质量或者干脆就放弃操作。
    在每次连接企图之后(不论成功与否),传输服务提供者都会更新flow_spec结构,以便尽可能地指明目前的网络条件。如果应用程序所要求的服务质量仅仅包含了一些传输服务提供者必须满足的缺省值,那么这种更新会是很有用处的。应用程序可以利用这些关于当前网络条件的信息来指导自己使用网络,例如今后的QOS要求。然而应用程序应该注意的是,传输服务提供者在不断更新的flow_spec结构中提供的信息仅仅是一个参考,它们只不过是粗略的估计。应用程序应该很小心的解释这些数据。
    无连接的套接口也可以使用WSAConnect()函数为一个指定的通讯规定特定的QOS级别。WSAIoctl()函数也可以用来规定初始的QOS要求,或者用来今后的QOS协商。
    即使是一个流规格已经建立,网络的情况也有可能改变,或者通讯的一方可能提出了QOS重协商的要求,这将导致可以得到的服务级别的降低或者提高。Windows Sockets 2引入了一个通知机制。它使用了一般的WS通知方式(FD_QOS和FD_GROUP_QOS事件)来告诉应用程序QOS级别已经改变了。一般服务提供者只在当前的服务级别和上一次报告有很大区别(通常是逆向的),并且有可能会影响到应用程序时才发出FD_QOS/FD_GROUP_QOS通知。应用程序应该使用WSAIoctl()函数来得到当前的状态并且检查服务等级的那些方面有了变化。如果当前的QOS级别是不可接受的,应用程序应该调整自己以去适应当前的状态,试图重新协商或者关闭套接口。
    Windows Sockets 2推荐的流规格把QOS特性划分为如下几个方面:
        1. 源通讯描述:应用程序的通讯事件以什么方式被送入网络。
        2. 延时性:最大延时和可接受的延时变化。
        3. 需要保证的服务级别:应用程序是否要求对服务质量的绝对保证。
        4. 费用:这一项是为将来可以决定有意义的费用时保留的。
        5. 服务提供者特定的参数:流规格可以根据具体的提供者扩展。

6.8 套接口组
    Windows Sockets 2引入了一个所谓套接口组的概念。它允许应用程序(或者一组共同工作的应用程序)通知底层的服务提供者一组特定的套接口是相关的,它们享有一些特定的性质。组的特性包括了组内单个套接口之间的相关特性和整个组的服务规范的特性。
    需要在网络上传输多媒体数据的应用程序会因为在所使用的一组套接口上建立联系而得到好处。至少这可以告诉服务提供者正在传输的数据流的一些相关性质。例如,一个会议应用程序希望传送音频数据的套接口比传送视频数据的套接口有更高的优先级。此外,一些传输服务提供者(例如数字电话和ATM)可以利用服务规范的组特性来决定底层调用或者线路连接的性质。通过应用程序指明套接口组及其特性,服务提供者可以以最大效率应用这些套接口。
    WSASocket()函数和WSAAccept()函数可以用来在创建一个新的套接口的同时显式的创建或者加入套接口组。getsockopt()函数可以用来得到套接口所属套接口组的标志。

6.9 共享套接口
    为了在进程间共享套接口,Windows Sockets 2引入了WSADuplicateSocket()函数。共享套接口是通过对底层的套接口创建附加的套接口描述字实现的。该函数的输入是本地的套接口描述字和目标进程的句柄。它返回一个仅在目标进程中有效的新的套接口描述字(目标进程有可能就是原始进程)。这一机制既可以在单线程Windows版本(例如Windows 3.1)中使用,也可以在占先的多线程Windows版本(例如Windows 95和Windows NT)中使用。要注意的是,套接口可以在一个进程的不同线程中共享而不需要使用WSADuplicateSocket()函数,因为一个套接口描述字在进程的所有线程中都有效。
    基于一个共享套接口的两个或者单个套接口描述字应该独立地使用套接口I/O。然而Windows Sockets没有实现任何共享控制。因此,在一个共享套接口上协调它们的操作是应用程序的责任。一个典型的使用共享套接口的例子是,有一个进程专门负责创建套接口和建立连接,并把套接口交给其他负责信息交换的进程。由于重新创建的是套接口描述字而不是底层的套接口,所以一切与套接口相关的状态对于所有套接口描述字都是相同的。例如对一个套接口描述字应用setsockopt()操作后,对所有的套接口描述字应用getsockopt()操作都可以看到这一变化。一个进程有可能调用closesocket()函数关闭一个复制的套接口描述字,于是该描述字就被清除了,然而,底层的套接口并不会被关闭,底层的套接口将一直保持打开,直到最后的一个套接口描述字被关闭。
    选择对共享套接口的通知可以使用WSAAsyncSelect()函数和WSAEventSelect()函数。对任何共享的套接口描述字发出这些调用将会取消在这一套接口上的所有注册事件,无论先前的注册使用了那个套接口描述字。因此,如果应用程序想使进程A接收FD_READ事件,进程B接收FD_WRITE事件,这是做不到的。如果应用程序确实需要使用这种紧密的协调方式,我们建议应用程序开发者使用线程而不要使用进程。

6.10 连接建立和拆除的高级函数
    WSAAccept()函数允许应用程序在接受连接请求以前得到请求者的信息,例如请求者的ID,QOS等等。这一点是通过对一个应用程序提供的条件函数的回调来实现的。如果服务提供者支持的话,在WSAConnect()函数的参数或者WSAAccept()函数的条件函数说明的用户对用户的数据可以在连接建立的时候传送到对方。
    在连接拆除时,如果协议支持的话,也可以在通讯的端点间交换用户数据。需要提出拆除连接的通讯端点可以调用WSASendDisconnect()函数声明没有要传送的数据并启动连接拆除过程。对于某些协议,这一拆除过程包括了从发起拆除连接的一方发送拆除数据。在接收到远端已经启动了连接拆除过程的通知后(通常是FD_CLOSE),应用程序可以调用WSARecvDisconnect()函数接收某些拆除数据。
    为了解释如何使用拆除数据,我们考虑如下的场景:在客户机/服务器模型中,通常是由客户机决定何时终止套接口的连接。在终止连接的同时,它通过拆除数据提供和服务器连接的次数。服务器也提供它和所有客户机的总的连接次数。这一过程如下所示:
           
            客户机端 服务器端
(1) 调用WSASendDisconnect()函数
终止对话并提供总的交互次数。
                                        (2) 得到FD_CLOSE,recv()函数
                                        返回0,或者WSARecv()函数返回
                                        WSAEDISCON错误表示优雅的关闭。
                                        (3) 调用WSARecvDisconnect()
                                        函数来得到客户机的总的交互次
                                        数。
                                        (4) 计算累积授权次数。
                                        (5) 调用WSASendDisconnect()
                                        函数来传送累积授权次数。
(6) 接收FD_CLOSE指示 (5’) 调用closesocket()函数。
(7) 调用WSARecvDisconnect()
函数来接收并存放累积授权次数。
(8) 调用closesocket()函数。

    注意:步骤(5’)必须在步骤(5)之后执行,但是与步骤(6),(7)或(8)没有时间联系.

6.11 扩展的字节顺序转换例程
    Windows Sockets 2并不假设对于所有协议,网络字节顺序都是正确的。所以Windows Sockets 2提供了一套把16位或者32位数字转换到网络字节顺序或者从网络字节顺序转换的例程。这些例程通常有一个整型的输入参数,它通常是一个常量,指明了需要的网络字节顺序是什么(目前或者是big_endian,或者是little_endian)。同时,每个协议的PROTOCOL_INFO结构包含一个域指明了协议对应的网络字节顺序,它可以用来作为字节顺序转换例程的输入参数。

6.12 分散/聚集方式I/O
    WSASend(),WSASendTo(),WSARecv()和WSARecvFrom()函数都以应用程序缓冲区数组作为输入参数,因此它们可以进行分散/聚集方式(向量方式)的I/O操作。如果应用程序需要传送的信息除了信息体外还包含了一个或多个固定长度的头时,这种操作是很有用的。这些头在发送之前不需要由应用程序连接到一个连续的缓冲区中。同样的,在接收时,这些头会自动的分离到各自的缓冲区中。
    如果接收时应用程序提供了多个缓冲区,当有数据到来时,操作就结束了,不论提供的缓冲区是否都被使用了。

6.13 协议无关的多点通讯
    Windows Sockets 2支持基本的数据传输以一般的方式使用不同的传输协议。Windows Sockets 2也支持应用程序以一般的方式使用传输协议的多点通讯能力。
    目前的多点通讯实现在节点加入一个多点对话的方式上有很大的不同。例如,是否有一个特殊的节点被指定为中心节点或者就是根节点;数据是在所有节点之间交换还是只在根节点和它的叶节点之间交换。Windows Sockets 2中的PROTOCOL_INFO结构允许一个协议声明它的多点通讯的各种特性。通过检查这些特性,应用程序可以知道在使用Windows Sockets 2函数设置,使用和拆除多点对话时应该遵循那一种协定。
    Windows Sockets 2中为支持多点通讯而作的增加如下:
        * PROTOCOL_INFO结构中的两个特性位。
        * 为WSASocket()的参数iflags定义的四个标志。
        * 一个新函数-WSAJoinLeaf(),它用来在多点对话中加入一个叶节点。
        * 两个WSAIoctl()的命令代码。

6.14 新增套接口选项一览
    Windows Sockets 2新增的套接口选项归纳如下:

选项值 类型 含义 缺省值

SO_GROUP_ID GROUP 套接口所属的套接口组 NULL

SO_GROUP_PRIORITY int 套接口在套接口组中的 0
                            相对优先级

SO_MAX_MSG_SIZE int 对于基于消息的套接口, 决定于
                            这一选项指明了一个消 实现
                            息的最大长度。对于基
                            于流的套接口,这一选
                            项没有任何意义。

SO_PROTOCOL_INFO struct 描述捆绑到套接口的协 决定于
                PROTOCOL 议的信息。 协议
                _INFO

PVD_CONFIG char 一个包含了服务提供者 决定于
                    FAR * 配置信息的不透明的数 实现
                            据结构对象。

6.15 新增套接口ioctl操作代码
    Windows Sockets 2新增的ioctl操作代码归纳如下。WSAIoctol()函数支持所有为ioctlsocket()函数定义的操作代码。

    操作代码 输入类型 输出类型 含义

SIO_ASSOCIATE_HANDLE 决定于伴随 没有使用。 把套接口与一个指定
                    的API。 的伴随接口连接。

SIO_ENABLE_CIRCULAR_ 没有使用 没有使用 允许循环队列。
QUEUEING

SIO_FIND_ROUTE struct 没有使用 请求找到对应于指定
                    sockaddr 地址的例程。

SIO_FLUSH 没有使用 没有使用 废除当前发送队列的内容。

SIO_GET_BROADCAST_ 没有使用 没有使用 得到特定协议的广播地址,
                                            该地址可以使用在send()
                                            函数和WSASend()函数中。

SIO_GET_QOS 没有使用 QOS 得到套接口当前的流协议。

SIO_GET_GROUP_QOS 没有使用 QOS 得到套接口所属套接口组的
                                            流协议。

SIO_MULTIPOINT_LOOK BOOL 没有使用 决定多点对话的数据是否由
                                            本地主机的同一套接口接收。

SIO_MULTICAST_SCOPE int 没有使用 定义允许多方传送的空间。

SIO_SET_QOS QOS 没有使用 为套接口建立新的流协议。

SIO_SET_GROUP_QOS QOS 没有使用 为套接口所属套接口组建立
                                            新的流协议。

SIO_TRANSLATE_HANDLE int 决定于伴 得到一个上下文有效的套接口
                                随API 对应的句柄。

6.16 新增函数一览
    Windows Sockets 2新增的函数列在下表中:

WSAAccept() accept()函数的扩展版本,它支持条件接收和套接口分组。
WSACloseEvent() 释放一个事件对象。
WSAConnect() connect()函数的扩展版本,它支持连接数据交换和QOS规范。
WSACreateEvent() 创建一个事件对象。
WSADuplicateSocket() 为一个共享套接口创建一个新的套接口描述字。
WSAEnumNetworkEvents() 检查是否有网络事件发生。
WSAEnumProtocols() 得到每个可以使用的协议的信息。
WSAEventSelect() 把网络事件和一个事件对象连接。
WSAGetOverlappedResu() 得到重叠操作的完成状态。
WSAGetQOSByName() 对于一个传输协议服务名字提供相应的QOS参数。
WSAHtonl() htonl()函数的扩展版本。
WSAHtons() htons()函数的扩展版本。
WSAIoctl() ioctlsocket()函数的允许重叠操作的版本。
WSAJoinLeaf() 在多点对话中加入一个叶节点。
WSANtohl() ntohl()函数的扩展版本。
WSANtohs() ntohs()函数的扩展版本。
WSARecv() recv()函数的扩展版本。它支持分散/聚集I/O和重叠套接口操作。
WSARecvDisconnect() 终止套接口的接收操作。如果套接口是基于连接的,得到拆除数据。
WSARecvFrom() recvfrom()函数的扩展版本。它支持分散/聚集I/O和重叠套接口操作。
WSAResetEvent() 重新初始化一个数据对象。
WSASend() send()函数的或者版本。它支持分散/聚集I/O和重叠套接口操作。
WSASendDisconnect() 启动一系列拆除套接口连接的操作,并且可以选择发送拆除数据。
WSASendTo() sendto()函数的扩展版本。它支持分散/聚集I/O和重叠套接口操作。
WSASetEvent() 设置一个数据对象。
WSASocket() socket()函数的扩展版本。它以一个PROTOCOL_INFO结构作为输入参数,并且允许创建重叠套接口。它还允许创建套接口组。
WSAWaitForMultipleEvents() 阻塞多个事件对象。



----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.17:59:09   MSIE 6.0 Windows XPIP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 


第七章 Windows Sockets 2扩展库函数简要参考

7.1 WSAAccept()
简述:根据条件函数的返回值有条件地接受连接,同时(可选地)创建和/或加入一个套接口组。

SOCKET WSAAPI WSAAccept ( SOCKET s, struct
sockaddr FAR * addr, int FAR * addrlen,
LPCONDITIONPROC lpfnCondition, DWORD
dwCallbackData );

s:标识一个套接口的描述字,该套接口在listen()后监听连接。
addr:(可选)指针,指向存放通讯层所知的连接实体地址的缓冲区。addr参数的具体格式由套接口创建时产生的地址族决定。
addrlen:(可选)指针,指向存放addr地址长度的整形数。
lpfnCondition:(可选的)用户提供的条件函数的进程实例地址。该函数根据参数传入的调用者信息作出接受或拒绝的决定,并通过给结果参数赋予特定的值来(可选地)创建和/或加入一个套接口组。
dwCallbackData:作为条件函数参数返回给应用程序的回调数据。WinSock不分析该参数。

返回值:
若无错误发生,WSAAccept()函数返回所接受套接口的描述字。否则的话,将返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。
addrlen参数引用的整形数初始时包含了addr参数所指向的空间数,在调用返回时包含了返回地址的实际长度。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAECONNREFUSED        根据条件函数的返回值(CF_REJECT)强制拒绝连接请求。
WSAENETDOWN            网络子系统失效。
WSAEFAULT             addrlen参数太小(小于sockaddr结构的大小),或者lpfnCondition并不是用户空间的一部分。
WSAEINTR                通过WSACancelBlockingCall()函数取消(阻塞)调用。
WSAEINPROGRESS        一个阻塞WinSock调用正在进行。
WSAEINVAL                WSAAccept()调用前未执行listen()调用;条件函数中的g参数非法;条件函数的返回值非法;套接口处于非法状态。
WSAEMFILE                WSAAccept()调用时排队队列非空,且无可用套接口描述字。
WSAENOBUFS            无可用缓冲区空间。
WSAENOTSOCK            描述字不是一个套接口。
WSAEOPNOTSUPP        所引用的套接口不是支持面向连接服务类型的。
WSATRY_AGAIN            根据条件函数的返回值(CF_DEFER) ,连接请求被推迟。
WSAEWOULDBLOCK        套接口标志为非阻塞,无连接请求供接受。
WSAEACCES                被推迟的连接请求超时或撤销。

另请参阅:accept(), bind(), connect(), getsockopt(),listen(), select(), socket(), SAAsyncSelect(), WSAConnect().


7.2 WSACloseEvent()
简述:关闭一个开放的事件对象句柄。

#include <winsock2.h>

BOOL WSAAPI WSACloseEvent( WSAEVENT hEvent );

hEvent:标识一个开放的事件对象句柄。

返回值:
如果函数顺利完成,返回值为真TRUE。如果失败,返回值为假FALSE。可用 WSAGetLastError()调用获取更多的错误信息。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSA_INVALID_HANDLE    hEvent不是一个合法的事件对象句柄。

另请参阅: WSACreateEvent(), WSAEnumNetworkEvents(),WSAEventSelect(), WSAGetOverlappedResult(),WSARecv(), WSARecvFrom(), WSAResetEvent(),WSASend(), WSASendTo(), WSASetEvent(),WSAWaitForMultipleEvents().


7.3 WSAConnect()
简述:创建一个与远端的连接,交换连接数据,并根据所提供的流描述确定所需的服务质量。

#include <winsock2.h>

int WSAAPI WSAConnect ( SOCKET s, const struct
sockaddr FAR * name,
int namelen, LPWSABUF lpCallerData, LPWSABUF
lpCalleeData,
LPQOS lpSQOS, LPQOS lpGQOS );


s:用于描述一个未连接套接口的描述字。
name:欲与套接口连接的远端名字。
namelen:名字长度。
lpCallerData:指向用户数据的指针,该数据在建立连接时将传送到远端。
lpCalleeData:指向用户数据的指针,该数据在建立连接时将从远端传送回本机。
lpSQOS:指向套接口s流描述的指针,每个方向一个。
lpGQOS:指向套接口组流描述的指针。(如果有套接口组的话)

返回值:
如果无错误发生,WSAConnect()返回0。否则的话,将返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。
对于阻塞套接口来说,返回值表示连接试图是否成功。
对于非阻塞套接口来说,连接试图不一定马上完成。在这种情况下,WSAConnect()返回SOCKET_ERROR,且WSAGetLastError()返回WSAEWOULDBLOCK. 此时应用程序可以:
1。利用select()函数,通过检查套接口是否可写来判断连接请求是否完成。或者,
2。如果应用程序已使用WSAAsyncSelect()函数来确定对连接事件的兴趣,则当连接操作完成时应用程序将收到FD _CONNECT通知。或者,
3。如果应用程序已使用WSAEventSelect()函数来确定对连接事件的兴趣,则当连接操作完成时相应的事件对象将设置信号。
对于一个非阻塞套接口来说,在连接试图完成之前,任何对该套接口的WSAConnect()调用都将以WSAEALREADY错误失败。
如果返回值指出连接试图失败(例如WSAECONNREFUSED, WSAENETUNREACH,WSAETIMEDOUT)则应用程序可对该套接口再次调用WSAConnect()函数。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEADDRINUSE        所指地址已被使用。
WSAEINTR                通过WSACancelBlockingCall()函数中止了阻塞调用。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAEALREADY            在所指定的套接口上正在进行一个非阻塞的connect()或WSAConnect()调用。
WSAEADDRNOTAVAIL    本地机器上无法获得所指定的地址。
WSAEAFNOSUPPORT        所指定地址族中的地址无法与本套接口一起使用。
WSAECONNREFUSED        连接试图被拒绝。
WSAEFAULT                name或namelen参数不是用户地址空间的一个有效部分;namelen参数太小; lpCalleeData、 lpSQOS和lpGQOS的缓冲区太小;或者lpCallerData的缓冲区太大。
WSAEINVAL                套接口已与一个地址捆绑。
WSAEINVAL                套接口未与一个地址捆绑。
WSAEINVAL                s参数为监听套接口。
WSAEISCONN            套接口已经连接(仅适用于面向连接的套接口)。
WSAENETUNREACH        当前无法从本主机联系网络。
WSAENOBUFS            无可用缓冲区,套接口未连接。
WSAENOTSOCK            描述字不是一个套接口。
WSAEOPNOTSUPP        lpSQOS和lpGQOS中的流描述无法满足。
WSAEPROTONOSUPPORT    服务提供者不支持lpCallerData参数。
WSAETIMEDOUT            连接试图超时,连接未建立。
WSAEWOULDBLOCK        套接口标志为非阻塞,连接无法立即完成。当套接口用select()函数设置为读时,可调用select()。
WSAEACCES                由于setsockopt()时未允许SO_BROADCAST,无法将一个数据报套接口与一个广播地址连接。

另请参阅: accept(), bind(), connect(), getsockname(),getsockopt(), socket(), select(),
WSAAsyncSelect(), WSAEventSelect().


7.4 WSACreateEvent()
简述:创建一个新的事件对象。

#include <winsock2.h>

WSAEVENT WSAAPI WSACreateEvent( VOID );

返回值:
如果函数成功,则返回值即是事件对象的句柄。
如果函数失败,返回WSA_INVALID_EVENT。应用程序可通过调用WSAGetLastError()函数获取进一步的错误信息。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSA_NOT_ENOUGH_MEMORY     无足够内存创建事件对象。

另请参阅: WSACloseEvent(), WSAEnumNetworkEvents(),WSAEventSelect(), WSAGetOverlappedResult(),WSARecv(), WSARecvFrom(), WSAResetEvent(),WSASend(), WSASendTo(), WSASetEvent(),WSAWaitForMultipleEvents().


7.5 WSADuplicateSocket()
简述:为一个共享套接口创建一个新的描述字。

#include <winsock2.h>

SOCKET WSAAPI WSADuplicateSocket ( SOCKET s,
WSATASK hTargetTask );

s:指定本地套接口描述字。
hTargetTask:指定使用共享套接口的目标任务的句柄。

返回值:
若无错误发生,WSADuplicateSocket()返回新的套接口描述字。否则的话,将返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEINVAL                参数中有非法值。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAEMFILE                无可用套接口描述字。
WSAENOBUFS            无可用缓冲区空间,套接口未创建。
WSAENOTSOCK            描述字不是一个套接口。

另请参阅:


7.6 WSAEnumNetworkEvents()
简述:检测所指定套接口上网络事件的发生。

#include <winsock2.h>

int WSAAPI WSAEnumNetworkEvents ( SOCKET s,
WSAEVENT hEventObject, LPWSANETWORKEVENTS
lpNetworkEvents, LPINT lpiCount);

s:标识套接口的描述字。
hEventObject:(可选)句柄,用于标识需要复位的相应事件对象。
lpNetworkEvents:一个WSANETWORKEVENTS结构的数组,每一个元素记录了一个网络事件和相应的错误代码。
lpiCount:数组中的元素数目。在返回时,本参数表示数组中的实际元素数目;如果返回值是WSAENOBUFS,则表示为获取所有网络事件所需的元素数目。

返回值:
如果操作成功则返回0。否则的话,将返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEINVAL                参数中有非法值。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAENOBUFS            所提供的缓冲区太小。

另请参阅: WSAEventSelect()


7.7 WSAEnumProtocols()
简述:获取现有传送协议的相关信息。

#include <winsock2.h>

int WSAAPI WSAEnumProtocols ( LPDWORD
lpdwProtocols, LPVOID lpProtocolBuffer, LPDWORD
lpdwBufferLength);

lpdwProtocols:一个以NULL结尾的协议标识号数组。本参数可选;如果lpdwProtocols为 NULL,则返回所有可用协议的信息,否则的话只返回数组中所开列的协议信息。
lpProtocolBuffer:一个用PROTOCOL_INFO结构填充的缓冲区。参见下文中对PROTOCOL_INFO结构的具体描述。
lpdwBufferLength:输入时,存有传递给WSAEnumProtocols()函数的lpProtocolBuffer缓冲区长度。输出时,表示为获取所有信息需传递给WSAEnumProtocols()函数的缓冲区长度。本函数不能重复调用;传入的缓冲区必须足够大以能存放所有的元素。这个规定降低了该函数的复杂度。由于一个机器上装载的协议数目往往是很小的,所以并不会产生问题。

返回值:
若无错误发生,WSAEnumProtocols()返回协议的数目。否则的话,将返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEINPROGRESS        一个阻塞WinSock调用正在进行。
WSAEINVAL                参数中有非法值。
WSAENOBUFS            缓冲区太小,无法保存所有PROTOCOL_INFO结构及其相关信息。传入的缓冲区大小至少应等于lpdwBufferLength中返回的值。


7.8 WSAEventSelect()
简述:确定与所提供的FD_XXX网络事件集合相关的一个事件对象。

#include <winsock2.h>

int WSAAPI WSAEventSelect ( SOCKET s, WSAEVENT
hEventObject, long lNetworkEvents );


s:一个标识套接口的描述字。
hEventObject:一个句柄,用于标识与所提供的FD_XXX网络事件集合相关的一个事件对象。
lNetworkEvents:一个屏蔽位,用于指定感兴趣的FD_XXX网络事件组合。

返回值:
如果应用程序指定的网络事件及其相应的事件对象成功设置,则返回0。否则的话,将返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。
在使用select()和WSAAsyncSelect()函数时,WSAEventSelect()常用来决定何时进行数据传送操作(如send()或recv()),并期望能立即成功。但是一个稳定的应用程序应该做好这样的准备,即事件对象被设置,并且一个WinSock调用以WSAEWOULDBLOCK立即返回 。举例来说,有可能发生下述操作序列:

(i) 套接口s上到达数据;WinSock设置了WSAEventSelect事件对象。
(ii) 应用程序进行其他操作。
(iii) 在进行操作时,应用程序调用了ioctlsocket(s, FIONREAD...)并发现有数据可读。
(iv) 应用程序调用一个recv(s,...)来读取数据。
(v) 最后应用程序等待WSAEventSelect()所指定的数据对象,该数据对象指出数据可读。
(vi) 应用程序调用recv(s,...),但以WSAEWOULDBLOCK错误失败。

其他的操作序列也是可能的。
成功地记录了网络事件的发生(通过设置内部网络事件记录的相应位),并且将相应的事件对象设置了信号后,不会对该网络事件作进一步的操作,直到应用程序调用了相应的函数显式地重新允许该网络事件及相应事件对象的信号。
网络事件                    重新允许函数
FD_READ                recv() 或 recvfrom()
FD_WRITE                send() 或 sendto()
FD_OOB                    recv()
FD_ACCEPT                accept() 或WSAAccept(),直到返回的错误代码为 WSATRY_AGAIN,指明条件函数返回CF_DEFER。
FD_CONNECT            NONE
FD_CLOSE                NONE
FD_QOS                    用SIO_GET_QOS 命令调用WSAIoctl()。
FD_GROUP_QOS            用SIO_GET_GROUP_QOS命令调用WSAIoctl()。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEINVAL                参数中有非法值,或者指定的套接口处于非法状态。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAENOTSOCK            描述字不是一个套接口。

另请参阅:WSACloseEvent() ,WSACreateEvent(),WSAEnumNetworkEvents(),WSAGetOverlappedResult(),WSAWaitForMultipleEvents().


7.9 WSAGetOverlappedResult()
简述:返回指定套接口上一个重叠操作的结果。

#include <winsock2.h>

BOOL WSAAPI WSAGetOverlappedResult( SOCKET s,
LPWSAOVERLAPPED lpOverlapped, LPDWORD
lpcbTransfer, BOOL fWait, LPDWORD lpdwFlags );

s:标识套接口。这就是调用重叠操作(WSARecv()、 WSARecvFrom()、WSASend()、WSASendTo() 或 WSAIoctl())时指定的那个套接口。
lpOverlapped:指向调用重叠操作时指定的WSAOVERLAPPED结构。
lpcbTransfer:指向一个32位变量,该变量用于存放一个发送或接收操作实际传送的字节数,或WSAIoctl()传送的字节数。
fWait:指定函数是否等待挂起的重叠操作结束。若为真TRUE则函数在操作完成后才返回。若为假FALSE且函数挂起,则函数返回FALSE,WSAGetLastError()函数返回 WSA_IO_INCOMPLETE。
lpdwFlags:指向一个32位变量,该变量存放完成状态的附加标志位。如果重叠操作为 WSARecv()或WSARecvFrom(),则本参数包含lpFlags参数所需的结果。

返回值:
如果函数成功,则返回值为真TRUE。它意味着重叠操作已经完成,lpcbTransfer所指向的值已经被刷新。应用程序可调用WSAGetLastError()来获取重叠操作的错误信息。
如果函数失败,则返回值为假FALSE。它意味着要么重叠操作未完成,要么由于一个或多个参数的错误导致无法决定完成状态。失败时,lpcbTransfer指向的值不会被刷新。应用程序可用WSAGetLastError()来获取失败的原因。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAENOTSOCK             描述字不是一个套接口。
WSA_INVALID_HANDLE    WSAOVERLAPPED结构的hEvent域未包含一个有效的事件对象句柄。
WSA_INVALID_PARAMETER    有不可接受的参数。
WSA_IO_INCOMPLETE    fWait假FALSE且输入/输出操作尚未完成。

另请参阅: WSACreateEvent(), WSAWaitForMultipleEvents(),WSARecv(), WSARecvFrom(), WSASend(), WSASendTo(),WSAConnect(), WSAAccept(), WSAIoctl().


7.10 WSAGetQoSByName()
简述:根据一个模板初始化QOS。

#include <winsock2.h>

BOOL WSAAPI WSAGetQOSByName( SOCKET s, LPWSABUF
lpQOSName, LPQOS lpQOS);

s:一个标识套接口的描述字。
lpQOSName:指定QOS模板的名字。
lpQOS:指向待填充QOS结构的指针。

返回值:
如果函数成功,返回真TRUE。如果函数失败,返回假FALSE。可调用WSAGetLastError()来获取进一步的错误信息。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAENOTSOCK            描述字不是一个套接口。
WSAEFAULT                lpQOS参数不是用户地址空间的一个有效部分,或lpQOS的缓冲区太小。
WSA_INVAL                所指定的QOS模板名字非法。

另请参阅: WSAConnect(), WSAAccept(), getsockopt().


7.11 WSAHtonl()
简述:将一个以主机字节顺序表示的无符号长整形数转换为网络字节顺序。

#include <winsock2.h>

u_long WSAAPI WSAHtonl ( SOCKET s, u_long
hostlong );

s:一个标识套接口的描述字。
hostlong:一个用主机字节顺序表示的32位数。

返回值:WSAHtonl()返回以网络字节顺序表示的值。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAENOTSOCK            描述字不是一个套接口。

另请参阅: htonl(), htons(), ntohs(), ntohl(), WSAHtons(),WSANtohl(), WSANtohs().


7.12 WSAHtons()
简述:将一个以主机字节顺序表示的无符号短整形数转换为网络字节顺序。

#include <winsock2.h>

u_short WSAAPI WSAHtons (SOCKET sr, u_short
hostshort );

s:一个标识套接口的描述字。
hostshort:一个以主机字节顺序表示的16位数。

返回值:WSAHtons()返回以网络字节顺序表示的值。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN         网络子系统失效。
WSAENOTSOCK             描述字不是一个套接口。

另请参阅: htonl(), htons(), ntohs(), ntohl(), WSAHtonl(),WSANtohl(), WSANtohs().


7.13 WSAIoctl()
简述:控制一个套接口的模式。

#include <winsock2.h>

int WSAAPI WSAIoctl(SOCKET s, DWORD
dwIoControlCode, LPVOID lpvInBuffer, DWORD
cbInBuffer, LPVOID lpvOutBuffer, DWORD
cbOutBuffer, LPDWORD lpcbBytesReturned,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE
lpCompletionRoutine);

s:一个套接口的句柄。
dwIoControlCode:将进行的操作的控制代码。
lpvInBuffer:输入缓冲区的地址。
cbInBuffer:输入缓冲区的大小。
lpvOutBuffer:输出缓冲区的地址。
cbOutBuffer:输出缓冲区的大小。
lpcbBytesReturned:输出实际字节数的地址。
lpOverlapped:WSAOVERLAPPED结构的地址。
lpCompletionRoutine:一个指向操作结束后调用的例程指针。

返回值:
调用成功后,WSAIoctl ()函数返回0。否则的话,将返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEINVAL                cmd不是一个合法的命令;或者一个输入参数非法;或者命令对于该种类型的套接口不适用。
WSAEINPROGRESS        在一个回调函数运行时调用了该函数。
WSAENOTSOCK            描述字不是一个套接口。
WSAEOPNOTSUPP        指定的ioctl命令无法实现,例如在SIO_SET_QOS或 SIO_SET_GROUP_QOS中指定的流描述无法实现。
WSA_IO_PENDING        一个重叠操作被成功启动,过后将报告完成情况。
WSAEWOULDBLOCK        套接口标志为非阻塞,且所需操作将产生阻塞。

另请参阅: socket(), ioctlsocket(), WSASocket(),setsockopt(), getsockopt().

7.14 WSAJoinLeaf()
简述:将一个叶节点加入一个多点会晤,交换连接数据,根据提供的流描述确定所需的服务质量。

#include <winsock2.h>

SOCKET WSAAPI WSAJoinLeaf ( SOCKET s, const struct
sockaddr FAR * name, int namelen, LPWSABUF
lpCallerData, LPWSABUF lpCalleeData,
LPQOS lpSQOS, LPQOS lpGQOS, int iFlags );


s:标识一个多点套接口的描述字。
name:将与套接口连接的远端名字。
namelen:名字长度。
lpCallerData:一个指针,指向多点会晤创建时传送给远端的用户数据。
lpCalleeData:一个指针,指向多点会晤创建时从远端传送回来的用户数据。
lpSQOS:一个指向套接口s的流描述的指针,每个方向一个。
lpGQOS:一个指向套接口组(如果存在)流描述的指针。
iFlags:标志位,用于指定套接口作为发送者。接收者或身兼二者。

返回值:若无错误发生,WSAJoinLeaf()返回新创建的多点套接口的描述字。否则的话,将返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEADDRINUSE        指定的地址已经在使用中。
WSAEINTR                通过WSACancelBlockingCall()函数取消(阻塞)调用。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAEALREADY            在指定的套接口上正在运行一个非阻塞的WSAJoinLeaf()调用。
WSAEADDRNOTAVAIL    本地主机无法获得指定的地址。
WSAEAFNOSUPPORT        所指定地址族中的地址无法与本套接口一起使用。
WSAECONNREFUSED        加入试图被强制拒绝。
WSAEFAULT                name或namelen参数不是用户地址空间的一个有效部分;namelen参数太小;lpCalleeData、 lpSQOS和lpGQOS的缓冲区太小; lpCallerData缓冲区太大。
WSAEINVAL                套接口已与一个地址捆绑。
WSAEINVAL                套接口未与一个地址捆绑。
WSAEISCONN            套接口已是多点会晤的一个成员。
WSAENETUNREACH        当前无法从本主机联系网络。
WSAENOBUFS            无可用缓冲区空间。套接口无法加入。
WSAENOTSOCK            描述字不是一个套接口。
WSAEOPNOTSUPP         lpSQOS和lpGQOS中所指定的流描述无法满足。
WSAEPROTONOSUPPORT    服务提供者不支持lpCallerData参数。
WSAETIMEDOUT            加入试图超时,未建立多点会晤。
WSAEWOULDBLOCK        套接口被标志为非阻塞,但多点会晤加入操作无法立即完成。当用select()选为读连接后,可使用select()对套接口进行操作。

另请参阅: accept(), bind(), select(), WSAAccept(),WSAAsyncSelect(), WSAEventSelect(), WSASocket().


7.15 WSANtohl()
简述:将一个以网络字节顺序表示的无符号长整形数转换为主机字节顺序。

#include <winsock2.h>

u_long WSAAPI WSANtohl ( SOCKET s, u_long netlong
);

s:一个标识套接口的描述字。
netlong:一个以网络字节顺序表示的32位数。

返回值:WSANtohl()返回一个以主机字节顺序表示的值。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAENOTSOCK            描述字不是一个套接口。

另请参阅: ntohl(), htonl(), htons(), ntohs(), WSAHtonl(),WSAHtons(), WSANtohs().


7.16 WSANtohs()
简述:将一个以网络字节顺序表示的无符号短整形数转换为主机字节顺序。

#include <winsock2.h>

u_short WSAAPI WSANtohs (SOCKET s, u_short
netshort );

s:一个标识套接口的描述字。
netshort:一个以网络字节顺序标识的16位数。

返回值:WSANtohs()返回以主机字节顺序表示的值。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAENOTSOCK            描述字不是一个套接口。

另请参阅: htonl(), htons(), ntohs(), ntohl(), WSAHtonl(),WSAHtons(), WSANtohl().


7.17 WSARecv()
简述:从一个套接口接收数据。

#include <winsock2.h>

int WSAAPI WSARecv ( SOCKET s, LPWSABUF lpBuffers,
DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd,
LPINT lpFlags, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE
lpCompletionRoutine );


s:一个标识已连接套接口的描述字。
lpBuffers:一个指向WSABUF结构数组的指针。每一个WSABUF结构包含一个缓冲区的指针和缓冲区的长度。
dwBufferCount:lpBuffers数组中WSABUF结构的数目。
lpNumberOfBytesRecvd:如果接收操作立即结束,一个指向本调用所接收的字节数的指针。
lpFlags:一个指向标志位的指针。
lpOverlapped:一个指向WSAOVERLAPPED结构的指针(对于非重叠套接口则忽略)。
lpCompletionRoutine:一个指向接收操作结束后调用的例程的指针(对于非重叠套接口则忽略)。

返回值:
若无错误发生且接收操作立即完成,则WSARecv()函数返回所接收的字节数。如果连接结束,则返回0。请注意在这种情况下完成指示(启动指定的完成例程或设置一个事件对象)将早已发生。否则的话,将返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。错误代码WSA_IO_PENDING表示重叠操作成功启动,过后将有完成指示。任何其他的错误表示重叠操作未能成功地启动,以后也不会有完成指示。
如果设置了MSG_INTERRUPT标志,则返回值的含义变化。零表示成功,具体含义同上。否则的话,返回值直接包含如下所示的错误代码。由于中断环境中无法调用WSAGetLastError(),故是必需的。请注意仅适用于Win16环境,仅适用于PROTOCOL_INFO结构中设置了XP1_INTERRUPT位的协议。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAENOTCONN            套接口未连接。
WSAEINTR                通过WSACancelBlockingCall()函数取消(阻塞)调用。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAENETRESET            由于远端的复位造成连接的中止。
WSAENOTSOCK             描述字不是一个套接口。
WSAEOPNOTSUPP        设置了MSG_OOB,但是该套接口不是诸如SOCK_STREAM流类型的,与套接口相关的通讯域不支持带外数据,或者套接口是单向的,只支持发送操作。
WSAESHUTDOWN        套接口已经关闭;一个套接口以SD_RECEIVE或 SD_BOTH的how参数shutdown()后,无法进行WSARecv()调用。
WSAEWOULDBLOCK        重叠套接口:太多重叠的输入/输出请求。非重叠套接口:套接口被标志为非阻塞,但是操作不能立即完成。
WSAEINVAL                套接口未用bind()捆绑,或者套接口未用重叠标志创建。
WSAECONNABORTED    由于超时或其他错误导致虚电路中止。
WSAECONNRESET        虚电路被远端复位。
WSAEDISCON            远端优雅的结束了连接。
WSA_IO_PENDING        成功启动一个重叠操作,过后将有完成指示。

另请参阅: WSACloseEvent(),WSACreateEvent(),WSAGetOverlappedResult(), WSASocket(),WSAWaitForMultipleEvents()


7.18 WSARecvDisconnect()
简述:中止一个套接口上的接收操作;若套接口为面向连接的,则检索中止连接数据。

#include <winsock2.h>

int WSAAPI WSARecvDisconnect ( SOCKET s, LPWSABUF
lpInboundDisconnectData );

s:一个标识套接口的描述字。
lpInboundDisconnectData:一个指向前来的中止连接数据的中止。

返回值:若无错误发生,WSARecvDisconnect()返回0。否则的话,返回SOCKET_ERROR,应用程序可通过调用WSAGetLastError()函数来获取相应的错误代码。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEFAULT                lpInboundDisconnectData参数所提供的缓冲区太小。
WSAENOPROTOOPT        指定地址族不支持中止连接数据。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAENOTCONN            套接口未连接(仅适用于面向连接的套接口)。
WSAENOTSOCK            描述字不是一个套接口。

另请参阅: connect(), socket().

----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.17:59:49   MSIE 6.0 Windows XPIP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 

7.19 WSARecvFrom()
简述:接收一个数据报并保存源地址。

#include <winsock2.h>1

int WSAAPI WSARecvFrom ( SOCKET s, LPWSABUF
lpBuffers, DWORD dwBufferCount, LPDWORD
lpNumberOfBytesRecvd, LPINT lpFlags, LPVOID
lpFrom, LPINT lpFromlen, LPWSAOVERLAPPED
lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE
lpCompletionRoutine );

s:一个标识套接口的描述字。
lpBuffers:一个指向WSABUF结构数组的指针。每个WSABUF结构包含缓冲区的指针和缓冲区的大小。
dwBufferCount:lpBuffers数组中WSABUF结构的数目。
lpNumberOfBytesRecvd:如果接收操作立即完成,则为一个指向所接收数据字节数的指针。
lpFlags:一个指向标志位的指针。
lpFrom:(可选)指针,指向重叠操作完成后存放源地址的缓冲区。
lpFromlen:指向from缓冲区大小的指针,仅当指定了lpFrom才需要。
lpOverlapped:指向WSAOVERLAPPED结构的指针(对于非重叠套接口则忽略)。
lpCompletionRoutine:一个指向接收操作完成后调用的完成例程的指针。(对于非重叠套接口则忽略)。

返回值:
若无错误发生且接收操作立即完成,则WSARecvFrom()函数返回所接收的字节数。如果连接结束,则返回0。请注意在这种情况下完成指示(启动指定的完成例程或设置一个事件对象)将早已发生。否则的话,将返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。错误代码WSA_IO_PENDING表示重叠操作成功启动,过后将有完成指示。任何其他的错误表示重叠操作未能成功地启动,以后也不会有完成指示。
如果设置了MSG_INTERRUPT标志,则返回值的含义变化。零表示成功,具体含义同上。否则的话,返回值直接包含如下所示的错误代码。由于中断环境中无法调用WSAGetLastError(),故是必需的。请注意仅适用于Win16环境,仅适用于PROTOCOL_INFO结构中设置了XP1_INTERRUPT位的协议。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEFAULT                lpFromlen参数非法;lpFrom缓冲区太小,无法容纳远端地址。
WSAEINTR                通过WSACancelBlockingCall()函数取消(阻塞)调用。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAEINVAL                套接口未用bind()捆绑,或者套接口未用重叠标志创建。
WSAENETRESET            由于远端的复位造成连接的中止。
WSAENOTCONN            套接口未连接。(仅适用于面向连接的套接口)。
WSAENOTSOCK            描述字不是一个套接口。
WSAEOPNOTSUPP        设置了MSG_OOB,但是该套接口不是诸如SOCK_STREAM流类型的,与套接口相关的通讯域不支持带外数据,或者套接口是单向的,只支持发送操作。
WSAESHUTDOWN        套接口已经关闭;一个套接口以SD_RECEIVE或 SD_BOTH的how参数shutdown()后,无法进行WSARecvFrom()调用。
WSAEWOULDBLOCK        重叠套接口:太多重叠的输入/输出请求。非重叠套接口:套接口被标志为非阻塞,但是操作不能立即完成。
WSAEMSGSIZE            消息太大无法全部装入指定的缓冲区,故被修剪。
WSAECONNABORTED    由于超时或其他错误导致虚电路中止。
WSAECONNRESET        虚电路被远端复位。
WSAEDISCON            远端优雅地中止了连接。
WSA_IO_PENDING        成功启动一个重叠操作,过后将有完成指示。

另请参阅: WSACloseEvent(),WSACreateEvent(),WSAGetOverlappedResult(), WSASocket(),WSAWaitForMultipleEvents()

7.20 WSAResetEvent()
简述:将指定的事件对象状态清除为未置信号。

#include <winsock2.h>

BOOL WSAAPI WSAResetEvent( WSAEVENT hEvent );

hEvent:标识一个开放的事件对象句柄。

返回值:
如果函数成功,返回真TRUE。如果函数失败,返回假FALSE。可调用WSAGetLastError()来获取进一步的错误信息。

错误代码:
WSA_INVALID_HANDLE    hEvent不是一个合法的事件对象句柄。

另请参阅: WSACreateEvent(), WSASetEvent(), WSACloseEvent().


7.21 WSASend()
简述:在一个已连接的套接口上发送数据。

#include <winsock2.h>

int WSAAPI WSASend ( SOCKET s, LPWSABUF lpBuffers,
DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent,
int iFlags, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE
lpCompletionRoutine );

s:标识一个已连接套接口的描述字。
lpBuffers:一个指向WSABUF结构数组的指针。每个WSABUF结构包含缓冲区的指针和缓冲区的大小。
dwBufferCount:lpBuffers数组中WSABUF结构的数目。
lpNumberOfBytesSent:如果发送操作立即完成,则为一个指向所发送数据字节数的指针。
iFlags:标志位。
lpOverlapped:指向WSAOVERLAPPED结构的指针(对于非重叠套接口则忽略)。
lpCompletionRoutine:一个指向发送操作完成后调用的完成例程的指针。(对于非重叠套接口则忽略)。

返回值:
若无错误发生且发送操作立即完成,则WSASend()函数返回所发送的字节数。(注意该数目可能小于len参数所指定的值)。如果连接结束,则返回0。请注意在这种情况下完成指示(启动指定的完成例程或设置一个事件对象)将早已发生。否则的话,将返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。错误代码WSA_IO_PENDING表示重叠操作成功启动,过后将有完成指示。任何其他的错误表示重叠操作未能成功地启动,以后也不会有完成指示。
如果设置了MSG_INTERRUPT标志,则返回值的含义变化。零表示成功,具体含义同上。否则的话,返回值直接包含如下所示的错误代码。由于中断环境中无法调用WSAGetLastError(),故是必需的。请注意仅适用于Win16环境,仅适用于PROTOCOL_INFO结构中设置了XP1_INTERRUPT位的协议。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEACCES                请求地址为广播地址,但相应标志位没有设置。
WSAEINTR                 通过WSACancelBlockingCall()函数取消(阻塞)调用。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAEFAULT                lpBuffer参数并不是用户地址空间的一个有效部分。
WSAENETRESET            由于远端复位造成连接的中止。
WSAENOBUFS            WinSock提供者报告一个缓冲区死锁。
WSAENOTCONN            套接口未连接。
WSAENOTSOCK            描述字不是一个套接口。
WSAEOPNOTSUPP        设置了MSG_OOB,但是该套接口不是诸如SOCK_STREAM流类型的,与套接口相关的通讯域不支持带外数据,或者套接口是单向的,只支持接收操作。
WSAESHUTDOWN        套接口已经关闭;一个套接口以SD_SEND或SD _BOTH的how参数shutdown()后,无法进行WSASend()调用。
WSAEWOULDBLOCK        太多的重叠输入/输出操作。
WSAEMSGSIZE            套接口是面向消息的,但是消息大于底层传送的最大值。
WSAEINVAL                套接口未用bind()捆绑,或者套接口未用重叠标志位创建。
WSAECONNABORTED    由于超时或其他错误导致虚电路中止。
WSAECONNRESET        虚电路被远端复位。
WSA_IO_PENDING        成功启动重叠操作,过后将有完成指示。

另请参阅: WSACloseEvent(),WSACreateEvent(),WSAGetOverlappedResult(), WSASocket(),WSAWaitForMultipleEvents()


7.22 WSASendDisconnect()
简述:启动套接口连接的中止操作。

#include <winsock2.h>

int WSAAPI WSASendDisconnect ( SOCKET s, LPWSABUF
lpOutboundDisconnectData );

s:一个标识套接口的描述字。
lpOutboundDisconnectData:指向发出的中止连接数据的指针。

返回值:若无错误发生,WSASendDisconnect()返回0。否则的话,将返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN         网络子系统失效。
WSAENOPROTOOPT        lpOutboundDisconnectData参数非NULL,复位提供者不支持中止连接数据。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAENOTCONN            套接口未连接(仅适用于面向连接的套接口)。
WSAENOTSOCK            描述字不是一个套接口。

另请参阅: connect(), socket().


7.23 WSASendTo()
简述:向指定地址发送数据,可能的话使用重叠输入/输出操作。

#include <winsock2.h>

int WSAAPI WSASendTo ( SOCKET s, LPWSABUF
lpBuffers, DWORD dwBufferCount, LPDWORD
lpNumberOfBytesSent, int iFlags, LPVOID lpTo, int
iToLen, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE
lpCompletionRoutine );


s:用于标识一个已连接的套接口,该套接口以WSA_FLAG_OVERLAPPED标志调用WSASocket()创建。
lpBuffers:一个指向WSABUF结构数组的指针。每个WSABUF结构包含缓冲区的指针和缓冲区的大小。
dwBufferCount:lpBuffers数组中WSABUF结构的数目。
lpNumberOfBytesSent:如果发送操作立即完成,则为一个指向所发送数据字节数的指针。
iFlags:标志位。
lpTo:(可选)指针,指向目标套接口的地址。
lpTolen:lpTo中地址的大小。
lpOverlapped:指向WSAOVERLAPPED结构的指针(对于非重叠套接口则忽略)。
lpCompletionRoutine:一个指向发送操作完成后调用的完成例程的指针。(对于非重叠套接口则忽略)。

返回值:
若无错误发生且发送操作立即完成,则WSASendTo()函数返回所发送的字节数(请注意它可能小于len所指定的值)。请注意在这种情况下完成指示(启动指定的完成例程或设置一个事件对象)将早已发生。否则的话,将返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。错误代码WSA_IO_PENDING表示重叠操作成功启动,过后将有完成指示。任何其他的错误表示重叠操作未能成功地启动,以后也不会有完成指示。
如果设置了MSG_INTERRUPT标志,则返回值的含义变化。零表示成功,具体含义同上。否则的话,返回值直接包含如下所示的错误代码。由于中断环境中无法调用WSAGetLastError(),故是必需的。请注意仅适用于Win16环境,仅适用于PROTOCOL_INFO结构中设置了XP1_INTERRUPT位的协议。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSAEACCES                请求的地址为广播地址,但未设置相应的标志位。
WSAEINTR                 通过WSACancelBlockingCall()函数取消(阻塞)调用。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAEFAULT                lpBuffer或lpTo参数不是用户地址空间的一部分;或者lpTo参数太小(小于sockaddr结构的大小)。
WSAENETRESET            远端主机复位造成连接的中止。
WSAENOBUFS            WinSock提供者报告了一个缓冲区死锁。
WSAENOTCONN            套接口未连接(仅适用于面向连接的套接口)。
WSAENOTSOCK            描述字不是一个套接口。
WSAEOPNOTSUPP        设置了MSG_OOB,但是该套接口不是诸如SOCK_STREAM流类型的,与套接口相关的通讯域不支持带外数据,或者套接口是单向的,只支持接收操作。
WSAESHUTDOWN        套接口已经关闭;一个套接口以SD_SEND或SD _BOTH的how参数shutdown()后,无法进行WSASendTo()调用。
WSAEWOULDBLOCK        太多重叠的输入/输出请求。
WSAEMSGSIZE            套接口是面向消息的,且消息大于底层传送所支持的最大长度。
WSAEINVAL                套接口未用bind()捆绑,或者套接口未用重叠标志位创建。
WSAECONNABORTED    由于超时或其他错误导致虚电路中止。
WSAECONNRESET        虚电路被远端复位。
WSAEADDRNOTAVAIL    本地主机无法获取所指定的地址。
WSAEAFNOSUPPORT        指定地址族中的地址无法与本套接口一起使用。
WSAEDESTADDRREQ    需要目的地地址。
WSAENETUNREACH        当前无法从本主机联系网络。
WSA_IO_PENDING        成功启动一个重叠操作,过后将有完成指示。

另请参阅: WSACloseEvent(),WSACreateEvent(),WSAGetOverlappedResult(), WSASocket(),WSAWaitForMultipleEvents()


7.24 WSASetEvent()
简述:将指定的事件对象状态设置为有信号。

#include <winsock2.h>

BOOL WSAAPI WSASetEvent( WSAEVENT hEvent );

hEvent:标识一个开放的事件对象句柄。

返回值:
如果函数成功,返回真TRUE。
如果函数失败,返回假FALSE。可通过调用WSAGetLastError()来获取进一步的错误信息。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSA_INVALID_HANDLE    hEvent不是一个合法的事件对象句柄。

另请参阅: WSACreateEvent(), WSAResetEvent(),WSACloseEvent().


7.25 WSASocket()
简述:创建一个与指定传送服务提供者捆绑的套接口,可选地创建和/或加入一个套接口组。

#include <winsock2.h>

SOCKET WSAAPI WSASocket ( int af, int type, int
protocol, LPPROTOCOL_INFO lpProtocolInfo, Group g,
int iFlags);


af:地址族描述。目前仅支持PF_INET格式,亦即ARPA Internet地址格式。
type:新套接口的类型描述。
protocol:套接口使用的特定协议,如果调用者不愿指定协议则定为0。
lpProtocolInfo:一个指向PROTOCOL_INFO结构的指针,该结构定义所创建套接口的特性。如果本参数非零,则前三个参数(af, type, protocol)被忽略。
g:套接口组的描述字。
iFlags:套接口属性描述。

返回值:
若无错误发生,WSASocket()返回新套接口的描述字。否则的话,返回 INVALID_SOCKET,应用程序可定调用WSAGetLastError()来获取相应的错误代码。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN         网络子系统失效。
WSAEAFNOSUPPORT        不支持指定的地址族。
WSAEINPROGRESS        一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。(参见B.3.6.6节)
WSAEMFILE                无可用的套接口描述字。
WSAENOBUFS            无可用的缓冲区空间。套接口无法创建。
WSAEPROTONOSUPPORT    不支持指定的协议。
WSAEPROTOTYPE        指定的协议对于本套接口类型错误。
WSAESOCKTNOSUPPORT    本地址族不支持指定的套接口类型。
WSAEINVAL                g参数非法。

另请参阅: accept(), bind(), connect(), getsockname(),getsockopt(), setsockopt(), listen(), recv(),recvfrom(), select(), send(), sendto(),shutdown(), ioctlsocket().


7.26 WSAWaitForMultipleEvents()
简述:只要指定事件对象中的一个或全部处于有信号状态,或者超时间隔到,则返回。

#include <winsock2.h>

DWORD WSAAPI WSAWaitForMultipleEvents( DWORD
cEvents,
const WSAEVENT FAR * lphEvents, BOOL fWaitAll,
DWORD dwTimeout,
BOOL fAlertable );

cEvents:指出lphEvents所指数组中事件对象句柄的数目。事件对象句柄的最大值为WSA_MAXIMUM_WAIT_EVENTS。
lphEvents:指向一个事件对象句柄数组的指针。
fWaitAll:指定等待类型。若为真TRUE,则当lphEvents数组中的所有事件对象同时有信号时,函数返回。若为假FALSE,则当任意一个事件对象有信号时函数即返回。在后一种情况下,返回值指出是哪一个事件对象造成函数返回。
dwTimeout:指定超时时间间隔(以毫秒计)。当超时间隔到,函数即返回,不论fWaitAll参数所指定的条件是否满足。如果dwTimeout为零,则函数测试指定的时间对象的状态,并立即返回。如果dwTimeout是WSA_INFINITE,则函数的超时间隔永远不会到。
fAlertable:指定当系统将一个输入/输出完成例程放入队列以供执行时,函数是否返回。若为真TRUE,则函数返回且执行完成例程。若为假FALSE,函数不返回,不执行完成例程。请注意在Win16中忽略该参数。

返回值:
如果函数成功,返回值指出造成函数返回的事件对象。
如果函数失败,返回值为WSA_WAIT_FAILED。可调用WSAGetLastError()来获取进一步的错误信息。

错误代码:
WSANOTINITIALISED        在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN            网络子系统失效。
WSA_NOT_ENOUGH_MEMORY    无足够内存完成该操作。
WSA_INVALID_HANDLE    lphEvents数组中的一个或多个值不是合法的事件对象句柄。
WSA_INVALID_PARAMETER        cEvents参数未包含合法的句柄数目。

另请参阅: WSACreateEvent(), WSACloseEvent().



----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.18:00:01   MSIE 6.0 Windows XPIP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 

哈哈!够了吗》恍恍惚惚和恍恍惚惚!
----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-08.18:01:54   MSIE 6.0 Windows XPIP: 已记录
帅哥哦
级别:老 站 友
威望:0
经验:0
货币:875
体力:69
来源:北京
总发帖数:353
注册日期:2001-04-26
查看 邮件 主页 QQ 消息 引用 复制 下载 

谢谢你啊,
我不会c,只会delphi啊:)
还是谢谢你,我想找点关于delphi的winsock源码

编辑 删除 发表时间发表于 2002-04-08.20:48:43   MSIE 5.01 Windows 98IP: 已记录
楚楚留香帅哥哦
级别:精灵王
威望:6
经验:1
货币:2090
体力:96
来源:218.63.89.*
总发帖数:590
注册日期:2001-04-17
查看 邮件 主页 QQ 消息 引用 复制 下载 

底层的socket编程必须用C,这是我的体会(不知道对不对)像VB、cb、dephi……等高级编程其实只是封装了socket的一些协议!什么Ftp、Smtp、http等!
----------------------------------------------------------
我竟改掉了落泪,也许是因为在黑暗中变得成熟了;成熟是因为心死了一半,心死了一半是因为没有了希望;没了希望是因为人们把我的退路断了;退路断了是因为这社会他妈的不想让我好好的活;这社会他妈的不想让我好好活是因为我从不肯妥协;我从不肯妥协是因为我还没人性的活着;我还没人性的活着是因为心中还有那么一点点的希望……

编辑 删除 发表时间发表于 2002-04-09.10:10:56   MSIE 6.0 Windows XPIP: 已记录
选择回复        
 快速回复主题: >>>高级模式
  用户名: 没有注册? 密码: 忘记密码?
记住密码
HTML语法
禁止IDB代码
禁止表情字符

[按 Ctrl+Enter 快捷键可直接提交帖子]
 投票评分: 共 0 票  
所有时间均为: 北京时间 ↑TOP 
关闭主题 拉前主题 移动主题 主题置顶 取消置顶 总固顶主题 取消总固顶 加入精华 移出精华 删除主题