当前位置:首页 >> 工学 >>

第3章 基于TCPIP协议的网络编程new_图文

网络编程与开发技术 第三章 基于TCP/IP协议的网络编程

第三章 基于TCP/IP协议的网络编程
内 容 提 要
1、TCP/IP概述 2、协议简介 3、地址与名字解析 4、网间进程通信及端口号 5、Winsock基本概念 6、常用Winsock函数 7、会话通信程序设计 8、数据报通信程序设计 9、Winsock多播与广播通信程序设计 10、Winsock I/O模型

§3.1 TCP/IP概述
1、TCP/IP协议簇 TCP/IP协议簇与ISO/OSI模型的对应关系图: OSI/RM
7 6 5 4 3 2 1
应用层 表示层 会话层 传输层

TCP/IP协议层
应用层 传输层 IGMP
ARP

Upper Protocol TCP IP 网络接口

Program UDP ICMP RARP

网络层 数据链路层

网络层

链路层 物理层 线缆

§3.1 TCP/IP概述
?

网络中两节点间的数据传送先从发送节点的高层向底层发送, 每向下传送一层,根据本层的协议添加本层的报头,达到物理层 以后,再水平传送到接收节点的物理层,在接收节点的物理层垂 直向上发送,向上传送一层则去掉一个报头。 一个节点的各层只能实现与上下相邻层间的通信,相邻层之 间也仅仅是相互通信,对其内部的工作过程是不必知道的。
?

除物理层为水平通信外,其它各层都是垂直通信。也就是说, 网络中各节点之间的直接接口只能是物理层。
?

节点的各层都有自己的协议,而且这些协议都是相互独立的, 各层的功能任务也是十分明确的。每一层利用相邻的下一层提供 的服务完成一组特定的功能,每一层功能实现的服务仅仅提供给 相邻的上一层。
?

§3.1 TCP/IP概述
每一层的功能:
?链路层:也称数据链路层或网络接口层,通常包括设备驱

动程序和网络接口卡,它们一起处理与电缆(或其他任何传
输媒介)的物理接口细节。
?具体使用哪种协议,该层并没有规定。具体的实现方法随着网络

类型的不同而不同,如802.3以太网,802.5令牌环网等,广域网的 帧中继、X.25等。
?ARP、RARP

?网络层:负责分组在网络中的活动,定址/寻址,路由选择
?包括IP协议、ICMP、IGMP协议(Internet组管理协议)。

§3.1 TCP/IP概述
传输层:该层主要为两台主机上的应用程序提供端到端的数 据通信,它分为两个不同的协议:TCP和UDP。
?

TCP协议提供端到端的、质量保证的数据传输,该层负责 数据的分组、质量控制和超时重发等。
? ?

UDP协议则只提供简单的把数据报从一端发送到另一端,

至于数据是否到达或按时到达、数据是否损坏都必须由应用 层来做。
?

应用层:该层负责处理实际的应用程序细节,包括Telnet、

HTTP、SMTP、FTP和SNMP(简单网络管理协议)等。

§3.1 TCP/IP概述
2、Winsock概述
? ?

Winsock是从Unix系统的Socket接口演变而来的。

在20世纪90年代初,网络软件开发商决定为Windows开发一套通用 TCP/IP编程接口,类似于Unix下的Socket,到1994年,它被正式称 为Winsock,这一接口迅速被所有的软件商所接受,通过C语言动态 链接库方式提供给用户及软件开发者。
?

Winsock继承了Berkeley Sockets的主要特征,同时进行了重要扩 充。主要是提供了一些异步函数,并增加了符合Windows消息驱动 机制的网络事件异步选择方式。 Winsock 接 口 包 含 了 一 组 网 络 I/O 和 获 取 网 络 信 息 的 库 函 数 , Winsock库函数包含在Winsock.dll动态链接库中,网络应用程序通 过调用Winsock API函数实现相互通信。 Winsock 利用下层的网络 通信协议和操作系统调用实现实际的通信。
?

§3.2 协议简介

?IP协议 ?传输层协议 ?客户机/服务器模式

§3.2.1 IP协议
IP层提供无连接的数据报传输机制。所以IP协议比较简单,不保 证传输的可靠性。IP协议解决的最大的一个问题是路由选择问题。
?

TCP/IP技术是为包容物理网络技术的多样性而设计的,而这种 包容性主要体现在IP层当中。各种网络技术的帧格式、地址格式等 上层协议差别很大, TCP/IP的重要思想 之一就是 提供一致的IP数据
?

报格式,屏蔽底层细节。
在TCP/IP协议簇中,所有上层协议都必须通过IP层传输外出数 据报,而所有下层协议接收到的信息首先必须交给IP层处理(判断 是接受还是抛弃)。
?

IP协议的这一特点充分体现了它在TCP/IP分层结构中的重要作 用,作为通用的数据报传输手段,IP协议可以说是TCP/IP协议簇的 核心。
?

§3.2.1 IP协议
IP数据报格式
版本号 首部长度 标识 生存时间 协议 源站IP地址 目的站IP地址 IP选项 数据 填充 服务类型 标志 总长度 分片偏移量 首部校验和

……

§3.2.2 传输层协议
TCP/IP协议簇中在传输层有两个重要的协议:TCP和UDP,前者提供高可 靠性服务,后者提供高效率服务。对二者的选择取决于应用环境和需求。

(1)TCP协议
TCP提供面向连接的流传输,可靠性很高,几乎可以解决所有的传输可 靠性问题。面向连接的TCP对可靠性的保证首先是它在进行实际数据传输前, 必须在信源端和信宿端建立一条连接。 假如由于种种原因,连接建立不成功,则信源端不会像UDP一样贸然向 信宿端发送数据。其次,面向连接传输的每一个报文都需接收端确认,未确 认报文被认为是出错报文。TCP协议建立在不可靠的IP协议之上的,IP不能 提供任何可靠性机制,所以TCP的可靠性完全由自己实现。

TCP采用的最基本的可靠性技术是:确认与重传。
TCP流是无结构的字节流, TCP采用累计确认机制,每个确认指出下一 个希望接收的字节序号。

TCP报文段的格式
源端口号 序号 确认号 头长度 保留 TCP校验和 标志 窗口大小 紧急指针 目的端口号

任选(如果有)
数据

填充

……

§3.2.2 传输层协议
(2)UDP协议
用户数据报协议UDP(User Datagram Protocol)建立在 IP协议之上,同IP协议一样提供无连接数据报传输。相对于IP 协议,它唯一增加的能力是提供协议端口,以保证进程通信。
UDP是不可靠的,因此,基于UDP的应用程序在不可靠 子网上必须自己解决可靠性(报文丢失、重复、失序和流控等) 问题。

§3.2.3 客户机/服务器模式
很多数据处理系统都采用开放系统结构的客户机/服务器 (Client/Server)网络模型。 服务器是一个进程,它一直等待着客户进程的请求以便为客户进程服 务,客户进程向服务器提出请求,服务器对请求做相应的处理并执行被请 求的任务,然后将结果返回给客户机。 典型的情况如下: a.服务器进程开始执行,首先初始化本身,然后进入睡眠状态以等待 客户进程的请求。通常是在一个众所周知的地址监听客户对服务的请求。 b.在本系统或与服务器相连的其它系统上,某一客户进程开始执行, 把请求发送给服务器进程要求服务。 c. 服务器进程“惊醒”并且为客户提供服务,作出适当的反应。

§3.3 地址与名字解析

?IP地址 ?地址解析 ?域名解析

§3.3.1 IP地址
在TCP协议栈中,编址由IP协议规定,IP标准分配给每台主 机一个32位的二进制数作为该主机的IP地址。在新的IPv6中IP地 址升至128位,这样IP地址资源更加丰富,但离实用化还有一段 距离。 32位IP地址被分割为两部分:前缀和后缀。 ? 前缀用于确定计算机所从属的物理网络,称为网络标识。 互连网中每一个物理网络都有一个唯一的值作为网络标识。 ? 后缀用于确定网络上一台单独的计算机,称为主机标识。 IP地址的层次性保证了以下两个重要性质:

?每台计算机分配一个唯一的地址。 ?虽然网络号分配必须全球一致,但主机标识可本地分配,不须全球一致。

§3.3.1 IP地址
IP地址分为五类:A类、B类、C类、D类和E类。其中A类、B类和C类为基本 类,D类多用于多播传送,E类属于保留类,现在不用。 格式如下(其中,*代表网络标识位数): A类:0******* xxxxxxxx xxxxxxxx xxxxxxxx B类:10****** ******** xxxxxxxx xxxxxxxx C类:110***** ******** ******** xxxxxxxx D类:1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx E类:1111xxxx xxxxxxxx xxxxxxxx xxxxxxxx IP地址一般采用点分十进制的表示方法。 类型 A类 B类 C类 D类 E类 范围 0.0.0.0 —— 127.255.255.255 128.0.0.0——191.255.255.255 192.0.0.0——223.255.255.255 224.0.0.0——239.255.255.255 240.0.0.0——247.255.255.255
各类IP地址的表示范围

§3.3.1 IP地址
几个特殊的IP地址:

?网络地址:IP中主机地址为0的地址表示网络地址。如128.211.0.0 ?广播地址:网络标识后的所有位都是1的后缀,就是直接广播地址。
如128.211.255.255

?回送地址:127.0.0.1用于测试。
子网:
子网的划分是通过子网掩码来确定的。

子网掩码是一个32bit的值,其中值为1的比特留给网络号和子网号, 为0的比特留给主机号。 网络/子网地址 = 主机IP地址 && 子网掩码

§3.3.2 地址解析
地址解析用来实现IP地址和物理地址(MAC地址)的转换。

物理地址:在任何一个物理网络中,各站点都有一个机器可识别 的地址,该地址叫物理地址。该物理地址被分配给每一块网络适配卡, 它是一个48位的二进制数,它唯一地标识网络中的主机。
IP地址:是一个32位的二进制数,通常由网络管理员分配给网络 中的设备。 ARP(Address Resolution Protocol):

地址解析协议,完成从IP地址到物理地址的映射。
RARP(Reverse Address Resolution Protocol): 反向地址解析,完成从物理地址到IP地址的映射。

§3.3.3 域名解析
在TCP/IP协议簇中,域名系统(DNS)用来完成主机名(及电子邮 件地址)到IP地址之间的映射。 DNS的核心是分级的、基于域的命名机制和为了实现这个命名机制 的分布式数据库系统。DNS是典型的客户/服务器模式,安装了DNS,提 供域名解析功能的计算机就是域名服务器。因特网域名结构是按照层次 结构组织的。首先把整个因特网划分为多个域,我们称为顶级域,并为 每个顶级域规定了国际通用的域名。顶级域名的划分采用了两种划分模 式:组织模式和地理模式。
顶级域名 Com Edu Gov Mil Net Org Int 国家代码 分配情况 商业组织 教育机构 政府部门 军事部门 主要网络支持中心 上述以外的组织 国际组织 各个国家

§3.4 网间进程通信及端口号
由于通信主机有多于一个进程在其控制之下,为了实现进程

间通信,网络通信的最终地址就不能仅仅是主机的网络地址了,
还必须包括可以描述的各个进程的某种描述符,使之识别所有进 行网络通信的主机进程。这个附加的编址信息作为UDP或TCP数据

报头的一部分传输。
为此,TCP或UDP提出了协议端口的概念,用于标识通信主机中 不同的通信进程,简称端口。

各个通信进程通过系统调用与某些端口建立绑定,传输层传
送到该端口的所有数据相应地被与其绑定的进程接收。也可以说,

端口用于标识用户希望访问的进程。

§3.4 网间进程通信及端口号
TCP和UDP采用16bit的端口号来识别进程。
TCP和UDP是完全独立的两个软件模块,因此各自的端口号 也相互独立。例如,TCP有一个1001号端口,同样UDP也可以有

一个1001号端口,它们并不冲突。
端口与协议在进程通信中是密不可分的,不同协议的端口 之间没有任何的联系。对TCP和UDP而言,可以提供65535(216) 个端口。

§3.5 Winsock基本概念
(1)套接字Socket

一个套接字是通信的一个端点。

套接字是通信的基石,是支持TCP/IP协议网络通信的基本
操作单元。 网络化的应用程序在开始任何通讯之前都必需要创建套接 字。

§3.5 Winsock基本概念
套接字存在于通信域中,Winsock规范只支持单一的通信

域:Internet域,套接字通常和同一域中的套接字交换数据。
每一个Socket都用一个半相关描述: {协议,本地地址,本地端口} 一个完整的Socket则用一个相关描述: {协议,本地地址,本地端口,远程地址,远程端口}

§3.5 Winsock基本概念
一个正在被使用的套接字都有它的类型和与其相关的进程。 套接字根据通信性质分为两类:会话套接字和数据报套接字: 会话套接字提供一种可靠的面向连接的数据传输方法,通常 会话套接字使用TCP协议。通信双方进行数据交换前,必须建
?

立一条连接。
数据报套接字支持双向通信,提供不可靠的、非连接的数据 报通信方式。数据报套接字通常使用UDP协议,具有向多个目
?

标地址发送广播数据报的能力。数据报并不十分可靠,需要应 用程序负责管理数据报的排序和可靠性。

§3.5 Winsock基本概念
(2)带外数据

也称为TCP紧急数据。带外数据是独立于普通数据传送给用户 的,对于仅支持带内数据的通信协议来说(例如紧急数据是与 普通数据在同一序列中发送),系统通常把紧急数据从普通数 据中分离出来单独存放。这就允许用户在顺序接收紧急数据和 非顺序接收紧急数据之间作出选择。

§3.5 Winsock基本概念
(3)广播 要实现这种功能,网络本身必须支持广播功能。 广播信息将会给网络造成极重的负担,因为它们要求网络上 的每台主机都为它们服务,所以发送广播数据包的能力被限制于 那些用显式标记了允许广播的套接字中。 广播通常应用于两种情况:一种是应用程序希望在本地网络

数据报套接字可以用来向许多系统支持的网络发送广播数据。

中找到一个资源,而应用程序对该资源的地址有没有任何先验知 识。而另一种是一些重要的功能,例如路由要求把它们的信息发 送给所有可以找到的邻机。

§3.5 Winsock基本概念
(4)字节顺序 不同的计算机使用不同的字节顺序存储数据。Intel处理器 使用的字节顺序称为“Little-Endian”,而Internet网络的字节 顺序称为“Big-Endian”,即主机序和网络序它们的字节顺序是 相反的。例如: 1234H的主机序:3412H 1234H的网络序:1234H 任何Winsock函数对IP地址和端口号的使用均是按照 网络字 节顺序组织的。 在很多情况下,用户要在主机字节顺序和网络字节顺序之间 进行转换,程序员应该使用Winsock API中标准的转换函数,而 不要自己编写转换代码。

§3.5 Winsock基本概念
(5)原始套接字

原始套接字是用SOCK_RAW打开的套接字,利用原始套接 字(Raw Socket)可访问位于基层的传输协议(例如IP协议、 ICMP协议、IGMP协议等),而不象其它套接字类型只能访问 传输层TCP和UDP协议。

§3.5 Winsock基本概念
(6)阻塞和非阻塞 套接字可以处于阻塞模式或非阻塞模式。 在阻塞模式下,I/O操作完成前,执行操作的Winsock函数 会一直等待下去,不会立即返回(将控制权交还给程序),这 就意味着任何一个线程在某一时刻只能执行一个I/O操作,而 且应用程序很难同时通过多个建好连接的套接字进行通信。
?

而非阻塞模式下没有这样的要求,Winsock函数无论如何 都会返回并交出程序的控制权。在默认的情况下,套接字是阻 塞模式。
?

§3.5 Winsock基本概念
(7)错误检查与控制

对编写成功的Winsock应用程序而言,错误检查和控制是至关重要 的。对于Winsock函数而言,返回错误是非常常见的。但是多数情况下, 这些错误是无关紧要的,通信仍可在套接字上进行。 返 回 的 错 误 值 可 以 有 多 种 , 但 最 常 见 的 错 误 是 SOCK_ERROR 。 SOCK_ERROR的值是-1。调用一个Winsock函数,如果发生错误,就可用 WSAGetLastError函数来获得错误代码,这个代码明确地表明产生错误 的原因。该函数的定义为:

int WSAGetLastError(void);
发生错误后调用这个函数,就会返回所发生的特定错误代码。这些 错误都是已经预定义的常量值。

§3.6 常用Winsock函数

?Winsock初始化函数 ?基本Winsock函数 ?数据传输函数 ?字节顺序及地址转换函数 ?网络信息查询函数

§3.6.1 Winsock初始化函数
(1)WSAStartup()
功能:加载Winsock DLL的相应版本。
格式:
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

参数说明: wVersionRequested:用于指定准备加载的Winsock版本。 lpWSAData:指向WSADATA结构的指针,用来接收有关Winsock库版 本的细节信息。

§3.6.1 Winsock初始化函数
(2)WSACleanup()
功能:终止对Winsock DLL的使用,并释放资源。 格式:int WSACleanup(void); 注释:该函数不带任何参数,若调用成功则返回0,否则返回 错误。

§3.6.2 基本Winsock函数
Winsock基本函数包括: ?socket ?bind ?connect ?listen ?accept ?closesocket ?shutdown

§3.6.2 基本Winsock函数
(1)socket() 功能:创建一个套接字。 格式: SOCKET socket(int af, int type, int protocol); 参数说明: af : 指 定 网 络 地 址 类 型 , 一 般 取 AF_INET , 表 示 该 套 接 字 在 Internet域中进行通信。 type : 用 于 指 定 套 接 字 类 型 , 套 接 字 类 型 可 以 取 五 个 值 : SOCK_STREAM(会话套接字)、SOCK_DGRAM(数据报套接字)、 SOCK_SEQPACKET、SOCK_RAW(原始套接字)和SOCK_RDM。 protocol:指定网络协议,一般取0,表示默认为TCP/IP协议。

Winsock API是建立在套接字基础上的。套接字从实质上讲, 就是一个指向传输提供者的句柄。Win32中套接字不同于其他文 件描述符,它是一个独立的类型——SOCKET。

§3.6.2 基本Winsock函数
(2)bind() 功能:将一本地地址与一套接字绑定。 格式: int bind(SOCKET s, const struct sockaddr *name, int namelen); 参数说明: s:标识一未绑定套接字的句柄。 name : 赋 予 套 接 字 的 地 址 。 它 是 用 struct sockaddr 结 构 定 义 的 , sockaddr结构定义如下: struct sockaddr{ u_short sa_family; char sa_data[14]; };

namelen:代表name指向的地址结构的长度。

一般情况下另一个与该地址结构大小相同的sockaddr_in结构更为常用, sockaddr_in结构用来标识TCP/IP协议下的地址。

Sockaddr_in的结构定义如下:
struct sockaddr_in { short sin_family; u_short sin_port; //16位端口号,网络字节顺序 struct in_addr sin_addr; //32位IP地址,网络字节顺序 char sin_zero[8]; } sin_family:必须设为AF_INET,表示该socket处于Internet域。 sin_port:用于指定服务端口,在选择服务端口时必须特别小心,因为有些 可用端口已为固定的服务保留。如果把端口号设为0,则Winsock将为应用程 序分配一个在1024——5000之间的端口值。 sin_addr:用于把一个IP地址保存为一个4字节的数,是无符号整数类型。
? sin_addr结构体中只有一个唯一的字段s_addr,表示IP地址,该字段是一个

整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsigned long 型的整数值后再置给s_addr。

sin_zero:填充项,使sockaddr_in结构和sockaddr结构的长度一样。

§3.6.2 基本Winsock函数

bind将指定的套接字同一个已知地址绑定在一起。一旦出错, bind函数会返回SOCKET_ERROR。

对bind来说,最常见的错误是WSAEADDRINUSE。如果使用 TCP/IP,那么该错误表示另一个进程已经同本地的IP地址及端口 号绑定到了一起,或者那个IP地址和端口号处于TIME_WAIT状 态。假如对一个已绑定套接字调用bind,便会返回WSAEFAULT 错误。

举例说明如何在一个TCP连接上进行套接字绑定: SOCKET s; Struct sockaddr_in tcpaddr; int port=5150; int nSockErr; s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); tcpaddr.sin_family = AF_INET; tcpaddr_sin_port = htons(port); tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY); //调用bind函数分配地址 if( bind(s,(SOCKADDR *)&tcpaddr,sizeof(tcpaddr))==SOCKET_ERROR) nSockErr=WSAGetLastError(); //处理错误
本例中,先创建了一个套接字,然后设置了IP地址,最后把套接字绑定到默

认IP地址的5150端口上。IP地址为常量INADDR_ANY,就是指定地址为0.0.0.0 的地址,这个地址表示不确定地址,或“所有地址”、“任意地址”。 一般来说, 在各个系统中均定义成为0值。表示允许服务进程监听主机上面每个网络接口上 的客户机活动。

§3.6.2 基本Winsock函数
(3)listen()
功能:将套接字置为侦听模式,等待客户端提出的连接申请。 格式: int PASCAL FAR listen( SOCKET s, int backlog); 参数说明: s:用于标识一个已绑定但未连接套接字的句柄。 backlog:表示等待连接队列的最大长度。 处于监听状态的流
套接字s将维护一个客户连接请求队列,该队列最多容纳backlog个 客户连接请求。 如果无错误发生,listen返回0,否则返回SOCKET_ERROR错误, 最常见的错误是WSAEINVAL,该错误通常表示listen之前没有调用 bind。应用程序可通过WSAGetLastError()获取相应错误代码。

§3.6.2 基本Winsock函数
(4)accept()
功能:在指定套接字上接受一个连接,并返回一个新的套接字。 格式:SOCKET PASCAL FAR accept ( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen); 参数: s:套接字句柄,该套接字在listen()后已经侦听连接。 addr:连接请求方SOCKADDR_IN结构的地址。

addrlen:SOCKADDR_IN结构长度。
说明:对于该客户端后续的所有操作,都应使用这个新套接字。 至于原来那个套接字,它仍然用于接受其它客户端连接,而且仍然 处于监听状态。

例:accept()的调用过程。 SOCKET sServSock; sockaddr_in addr; int nSockErr, nNumConns; SOCKET sConns[5]; sockaddr Connaddrs[5]; int nAddrLen=sizeof(sockaddr); sServSock = socket(AF_INET, SOCK_STREAM, 0); //建立socket对象 addr.sin_family = AF_INET; addr.sin_port = htons(5050); //为socket分配端口 addr.sin_addr.s_addr = htonl(INADDR_ANY); if( bind(sServSock, (LPSOCKADDR) &addr, sizeof(addr))==SOCKET_ERROR) { nSockErr=WSAGetLastError(); return; //Handle error,Do not continue } if( listen (sServSock, 2)= =SOCKET_ERROR) //侦听客户连接请求 { nSockErr=WSAGetLastError(); return; //处理错误,不再继续 }

while(nNumconns<5) { sConns[nNumConns] = accept (sServSock, ConnAddrs[nNumConns], &nAddrLen); //接受连接 if (sConns[nNumConns]=INVALID_SOCKET ) { nSockErr=WSAGetLastError(); return; //处理错误 } else { //新socket已经成功地连接 StartNewHandleThread(sConns[nNumConns]); nNumConns++; } }

§3.6.2 基本Winsock函数
(5)connect()
功能:用于建立与一个服务器端的连接。
格式:int connect( SOCKET s, const struct sockaddr FAR* name, int namelen);

参数说明:
s: 标识一个未连接套接字的描述字。 name: 是针对TCP的套接字地址结构(SOCKADDR_IN),标 识服务进程IP地址及端口信息。 namelen:标识name参数的长度。

§3.6.2 基本Winsock函数
(6)closesocket()
功能: 关闭一个套接字。
格式: int PASCAL FAR closesocket( SOCKET s); 参数: s:准备关闭的套接字。 注释:closesocket的调用会释放套接字描述字s,以后再对s 的访问均以WSAENOTSOCK错误返回。

§3.6.2 基本Winsock函数
(7)shutdown()
功能:禁止在一个套接字上进行数据的接收与发送。
格式:int PASCAL FAR shutdown( SOCKET s, int how); 参数说明: s:套接字句柄。 how:标志,用于描述禁止哪些操作。可能的取值是: SD_REVIEVE、SD_SEND或SD_BOTH。SD_RECEIVE表示不允许再调 用接收函数。SD_SEND表示不允许再调用发送函数。SD_BOTH表 示取消连接两端的收发操作。

§3.6.3 数据传输函数
(1)send() 功能:向一个已连接的套接字发送数据。 返回发送数据的字 节数,若发生错误,就返回SOCKET_ERROR。 格式:int PASCAL FAR send( SOCKET s, const char FAR* buf, int len, int flags); 参数说明: s: 是已连接套接字的描述字。 buf:字符缓冲区,包含待发送数据。

len:即将发送缓冲区中的字符数。 flags : 调 用 的 执 行 方 式 。 可 以 为 0 、 MSG_DONTROUTE 、 MSG_OOB或这些标志按位“或”运算的结果。 0表示无特殊行为, MSG_DONTROUTE要求传输层不要把发出的包路由出去,MSG_OOB 标志数据应被带外发送。

§3.6.3 数据传输函数
(2)recv()
功能:从已建立连接的套接字接收数据 — 面向连接的会话套接字 格式:int PASCAL FAR recv( SOCKET s, char FAR* buf, int len, int flags); 参数说明:

s:标识已连接套接字的描述字。
buf:用于接收数据的字符缓冲区。 len:准备接收的字节数或buf缓冲区长度。

flags:指定调用方式。可以是0、MSG_PEEK、MSG_OOB或这些标志 按位“或”运算的结果。0表示无特殊行为,MSG_PEEK使有用的数据 复制到所提供的接收端缓冲区内,但没有把它从系统缓冲区中删除。 recv()调用成功则返回发送的字节数。

§3.6.3 数据传输函数
(3)sendto()
功能:向一指定目的地发送数据。—— 用于数据报通信 格式:int PASCAL FAR sendto( SOCKET s, const char FAR* buf, int len, int flags,const struct sockaddr FAR* to, int tolen); 参数说明:

s:标识套接字的描述符。
buf:待发送数据的缓冲区。 len:发送的字节数。

flags:调用方式标志位。可以为0、MSG_DONTROUTE、MSG_OOB或这些标志 按位“或”运算的结果。
to:(可选)指针,指向目的套接字(SOCKADDR结构)的地址。 tolen:to所指地址结构的长度。

§3.6.3 数据传输函数
(4)recvfrom()
功能:接收一个数据报并保存源地址。 格式:int PASCAL FAR recvfrom( SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen); 参数说明 s:标识套接字的描述符。 buf:接收数据缓冲区。 len:准备接收的字节数或buf缓冲区长度。 flags:调用操作方式。可以是0、MSG_PEEK、MSG_OOB或这些标志按位 “或”运算的结果。0表示无特殊行为,MSG_PEEK使有用的数据复制到所提 供的接收端缓冲区内,但没有把它从系统缓冲区中删除。 from:(可选)指针,接收发送方套接字地址结构信息。 fromlen:(可选)指针,指向SOCKADDR地址结构的长度。 函数返回时,from指向的单元便填入发送数据端的地址。buf获得接收 数据。函数调用成功则返回实际接收的字节数。

§3.6.4 字节顺序及地址转换函数
(1)字节顺序转换函数

把一个数从主机字节顺序转换为网络字节顺序:
4字节: u_long

htonl(u_long hostlong);

2字节: u_short

htons(u_short hostshort);

把网络字节顺序转换为主机字节顺序:
4字节: u_long 2字节: u_short

ntoh(u_long netlong);
ntohs(u_short netshort);

§3.6.4 字节顺序及地址转换函数
(2)地址转换函数

?inet_addr()
功能:将点分式IP地址转换为一个32位的无符号长整数。 格式:unsigned long inet_addr(const char FAR *cp);

cp: 以Internet标准“.”间隔的IP地址字符串。这个函数的返 回是一个网络字节顺序的32位长整数。

?inet_ntoa()
功能:将32为无符号长整数转换为点分式IP地址。 格式:char *inet_ntoa(struct int_addr inaddr); inaddr:是32为长整数。

§3.6.5 网络信息查询函数
(1)getpeername()
功能:获取通信对方的套接字地址信息。 格 式 : int PASCAL FAR getpeername( SOCKET s, struct sockaddr FAR* name,int FAR* namelen); s:已连接的套接字。 name:通信对方的套接字地址结构。 namelen:一个指向地址结构长度的指针。

(2)getscokname()
功能:该函数是getpeer的对应函数。获取指定套接字的本地地址信息。 格 式 : int PASCAL FAR getsockname( SOCKET s, struct sockaddr FAR* name,int FAR* namelen); s:标识一个已绑定套接字的描述字。 name:套接字的地址结构。 namelen:一个指向地址结构长度的指针。

§3.6.5 网络信息查询函数
(3)gethostbyname()
功能:返回对应于给定主机名的主机信息。在已知主机名而打算查找其IP 地址的时候,可以使用这个函数。 格式:struct hostent FAR * gethostbyname(const char FAR *name);

name:指向主机名的指针。

(4)gethostbyaddr()
功能:返回对应于给定IP地址的主机信息。在已知IP地址,打算查找其主 机名的时候,可以使用这个函数。 格 式 : struct hostent FAR *PASCAL FAR gethostbyaddr(const char FAR * addr, int len, int type); addr:是一个指向网络字节顺序IP地址的指针。

len: 地址的长度,在AF_INET类型地址中为4。
type:地址类型,应为AF_INET。

§3.6.5 网络信息查询函数
(5)getservbyname() 功能:返回对应于给定服务名和协议名的相关服务信息。在已知服务的 情况下,要查找其对应的端口号,可以使用这个函数。 格式:struct servent FAR * PASCAL FAR getservbyname(const char FAR * name, const char FAR *proto); name:一个指向服务名的指针。 proto: 指向协议名的指针(可选)。

如果没有错误发生,getservbyname()返回一个指向servent结构的 指针,否则,返回一个空指针。应用程序可以通过WSAGetLastError() 来得到一个特定的错误代码。

§3.7 会话通信程序设计
?会话通信程序结构 ?会话通信程序实例

§3.7.1 会话通信程序结构
会话套接字定义了一种可靠的面向连接的服务,实现了无 差错无重复的顺序数据传输。 会话套接字的服务进程和客户进程在通信前必须建立各自 的套接字并建立连接,然后才能对相应的套接字进行“读”、 “写”操作,实现数据的传输。 在会话通信程序中使用的基本 Winsock函数有 socket、 bind、listen、accept、connect、shutdown和closesocket等, 使用的数据传输函数有send和recv。

§3.7.1 会话通信程序结构
会话通信程序的步骤如下: 第一步:要将指定协议的套接字绑定到它已知的名字上。这个名 字就是本地的IP地址加端口号。这个过程通过bind函数来完成。 第二步:服务进程要处于监听状态,等待任意数量的客户端连接, 以便为它们的请求提供服务。服务进程必须在所绑定的名字上进 行监听。因此需要把套接字置为监听模式。通过listen函数来实 现。 第三步:服务进程调用函数accept或WSAAccept准备接受来自客户 端的连接。如果一个客户端这时用connnect函数试图建立连接, 服务进程就可以接受连接。 第四步:连接建立后,服务器端和客户端之间就可以使用send和 recv函数进行通信。

§3.7.1 会话通信程序结构
WinSock服务进程

建立套接字socket()
绑定套接字bind() 监听listen() 准备接受连接accept() 阻塞,等待建立连接

WinSock客户进程
建立套接字socket() 建立连接connect() 请求数据 应答数据

recv()
处理服务请求

send()
recv() 关闭closesocket ()

send() 关闭closesocket()

会话套接字的编程模型

§3.7.2 会话通信程序实例1
例3.3:该实例完成一个服务器端和客户端简单的会话通信过程。要求主 机上安装TCP/IP协议,并设定IP地址为24.85.151.24。服务器端和客户端使 用同一台主机的不同端口号,服务器端使用的端口号为2000,客户端使用的 端口号为3000。 服务器端程序先运行,首先初始化Winsock,然后创建套接字,在2000

端口上进行绑定,接着在2000端口进行监听,并进入等待连接状态。
客户端后运行,同样首先初始化Winsock,然后创建套接字,接着尝试 和服务器端的连接。连接一旦建立,就可以在服务器端和客户端之间进行通

信。客户端提示输入一个数字10,那么客户端会从“data0”到“data9”发送
10个字符串,输入0退出。

§3.7.2 会话通信程序实例1
程序中所包含的头文件: “Winsock.h”:Winsock头文件,版本1.1使用Winsock.h,版本2使用

Winsock2.h。
“windows.h”:创建一个Windows console程序所必需的头文件。 #pragma comment(lib, "wsock32.lib") : 指 定 连 接 的 库 文 件 。 版 本

Winsock 1.1使用wsock32.lib,版本Winsock 2使用ws2_32.lib。

实例1 运行界面

服务器端先运行,在2000端口侦听,进入等待连接状态。 客户端后运行,尝试和服务器端连接。 连接建立。

实例1 运行界面

数据传输

§3.7.3 会话通信程序实例2
实现模拟的FTP功能:

? 服务器端程序在2000端口侦听,进入等待连接状态。 ? 客户端尝试和服务器端连接。 ? 连接建立后,两者之间就可以进行文件传输。要求
文件小于32KB。

实例2 运行界面

建立连接

实例2 运行界面

传输文件

§3.8 数据报通信程序设计

?数据报通信程序结构 ?数据报通信实例

§3.8.1 数据报通信程序结构
数据报套接字是无连接的,它的编程过程比流套接字模型要简 单一些。数据报套接字编程模型使用的基本Winsock函数和会话套 接字模型一致。使用的数据传输函数则和会话套接字不同,发送 数据用sendto函数,接收数据使用revfrom函数。

数据报通信程序步骤如下:
第一步:服务器端和客户端都要创建一个数据报套接字。 第二步:服务器端调用bind函数给套接字分配一个公认的端口。在 开发应用程序时,这个公认端口通常是指定的。客户端同样要绑 定套接字。 第三步:客户端和服务器端都可以使用sendto函数发送消息,使用 recvfrom接收消息,完成数据报传递。最后调用closesocket关闭套 接字。

数据报套接字编程模型
服务器

socket( )
bind( ) recvfrom( ) 阻塞,等待客户数据 服务请求

客户机
socket( )

bind( )

sendto( )

处理服务请求 sendto( ) close( ) 数据报套接字编程模型
服务应答 recvfrom( ) close( )

§3.8.2 数据报通信实例
该实例完成一个服务器端和客户端简单的数据报通信过程。 要求主机上安装TCP/IP协议,并设定IP地址为24.85.151.24。服 务器端和客户端使用同一台主机的不同端口号,服务器端使用的 端口号为2000,客户端使用的端口号为3000。

服务器端首先初始化Winsock,然后创建套接字,在2000端 口上进行绑定,然后等待接收客户端发送的数据。

客户端后运行,同样首先初始化Winsock,然后创建套接字, 在3000端口上进行绑定,然后就可以向服务器端发送数据。 客户端提示按任意键进行发送,按任意键后,客户端会从 “data0”到“data49”发送50个字符串,再按任意键退出。

这个程序包含了一个数据报通信程序最基本的框架。

§3.8.2 数据报通信实例

创建、绑定套接字

§3.8.2 数据报通信实例

发送接收数据

§3.9 Winsock多播与广播通信程序设计

?广播通信 ?多播通信

§3.9.1 广播通信
(1)广播通信的实现
现在大多数网络都支持广播(broadcast)通信,但对于许 多TCP/IP实现来说,要实现广播通信,首先必须设置好子网掩码 和广播地址,这样底层网络才能够接收广播信息。

TCP/IP协议是通过IP地址来对计算机进行识别的,IP地址分 为标识网络的网络标识和标识网络内唯一主机的主机标识两部分。 广播地址是主机标识全为1的IP地址。

§3.9.1 广播通信

使用广播通信进行数据通信的最大好处在于:当多台计算机 进行交叉数据传送时连接数不会造成级数增长,数据传输量相对 减少,参与通信的计算机数量对程序透明并且增加、减少计算机 不会影响程序的执行。此外,广播通信是对等(peer to peer), 没有客户和服务器之分,参与通信的计算机可以使用同一个通信 程序。因此,对于可靠性要求不是很高的多机通信系统,使用广 播通信是一个很好的解决方案。

广播通信也有其缺点: ? 当广播在整个网段上发送时,容易引起网络的阻塞。 ? 广播数据不能越过路由器,位于不同网段的计算机不能收到 广播消息。 ? 作为一种对等通信,它没有权限设置,不利于网络维护。

§3.9.1 广播通信
使用Winsock实现广播通信一般分为五个步骤: 第一步:创建数据报套接字。在Winsock支持的套接字中,只有 数据报套接字(SOCK_DGRAM)才支持广播通信。 第二步: 绑定数据报套接字于指定的地址和端口。 第三步:通过套接字选项设置数据报套接字的广播属性。 int setsockopt ( SOCKET s, int level, int optname, const char

FAR * optval, int optlen );
level :选项定义的层次,SOL_SOCKET or IPPROTO_TCP optname:需设置的选项 SO_BROADCAST optval:指针,指向存放选项值的缓冲区。 optlen:optval缓冲区的长度。

§3.9.1 广播通信
下面的程序段就是创建一个广播套接字的过程: BOOL optval=TRUE; SOCKET s; SOCKETADDR_IN in; s=socket(AF_INET,SOCK_DGRAM,0) //创建套接字 in.sin_family=AF_INET; in.sin_addr.s_addr=INADDR_ANY; in.sin_port=htons(PORT); //PORT为预定义的变量 if(bind(s,(LPSOCLADDR)&in,sizeof(in))){ //绑定失败 closesocket(s); return FALSE; } if(setsockopt(s,SOL_SOCKET,SO_BROADCAST, (char *)&optval,sizeof(optval)= =SOCKET_ERROR) { closesocket(s); //套接字选项设置失败 return FALSE; }

§3.9.1 广播通信
第四步:通过sendto函数发送广播信息。
发送广播信息和流套接字的数据发送不同,它只能使用sendto函 数,而不能使用send函数。发送地址必须为INADDR_BROADCAST(广播 地址)。 下面的程序段显示了广播消息发送的实现过程: int length; SOCKADDR to; //发送目标 to.sin_family=AF_INET; to.sin_addr.s_addr=INADDR_BROADCAST; to.sin_port=htons(PORT); //s是通过前面三个步骤创建的广播套接字描述字 if((length=sendto(s,lpBuffer,len,0,(LPSOCKADDR)&to, sizeof(SOCKADDR)))= =SOCKET_ERROR) { closesocket(s); return FALSE; //发送失败 }

§3.9.1 广播通信
第五步:通过recvfrom()函数接收广播消息。
接收广播消息的过程: int erron,len,fromlen; SOCKADDR_IN from; fromlen=sizeof(SOCKADDR) if((len = recvfrom(s,lpBuffer,length,0, (LPSOCKADDR) &from,&fromlen) )= =SOCKET_ERROR) { closesocket(s); //接收失败 return FALSE; } else { //接收成功 }

广播通信实例
发送方程序例3.12 接收方程序例3.7

§3.9.2 多播通信
(1)概念 “多播”亦称“多点传送”(Multicasting),是一种让数据从一个成员 送出,然后复制给其他多个成员的技术。 多播作为一点对多点的通信,是节省网络带宽的有效方法之一。在网络 音频/视频广播的应用中,当需要将一个节点的信号传送到多个节点时,无论 是采用重复点对点通信方式,还是采用广播方式,都会严重浪费网络带宽, 只有多播才是最好的选择。 多播能使一个或多个多播源只把数据包发送给特定的多播组,而只有加 入该多播组的主机才能接收到数据包。目前,IP多播技术被广泛应用在网络 音频/视频广播、AOD/VOD、网络视频会议、多媒体远程教育、“push”技术 (如股票行情等)和虚拟现实游戏等方面。 然而,并非所有协议都支持多播通信,对Win32平台而言,仅两种可从 Winsock内访问的协议(IP和ATM)才提供了对多播通信的支持。

§3.9.2 多播通信
多播通信具有两个层面的重要特征:控制层面和数据层面。 “控制层面”(Control Plane)定义了组成员的组织方式; 而“数据层面”(Data Plane)决定了在不同的成员之间数据如 何传送。 这两方面的特征既可以是“有根的”(Rooted),也可以 是“无根的”(Nonrooted)。

§3.9.2 多播通信
有根和无根控制层模型比较 根节点 邀请 叶节点 A 有根控制层结构 叶节点 叶节点

邀请 叶节点

邀请 叶节点

多播组
叶节点 B 无根控制层结构 叶节点

§3.9.2 多播通信
在一个“有根的”控制层面内,存在着一个特殊的多播组成 员,叫作根节点,而剩下的每个组成员都叫作叶节点。 大多数情况下,根节点需负责多播组的建立,其间涉及到建

立同任意数量的叶节点的连接。对任何一个具体的组来说,都只
能存在一个根节点。在有根控制层面中,根节点必须明确邀请每 个叶节点加入该组,叶节点才能加入。ATM协议便是“有根控制

层面”的典型例子。
而对一个“无根的”控制层面来说,任何节点都能自由加入 多播组。

§3.9.2 多播通信
数据层面也存在着“有根的”和“无根的”两种形式。 对一个有根数据层面而言,它有一个参与者叫作根节点。数据传 输只能在数据根和多播会话的其他所有成员叶节点之间进行。这种传 输既可单向进行,也可双向进行。 而自一个叶节点发出的数据却只会发往根节点,不会“蔓延”到其他 叶节点。

在有根数据层面中,自根节点发出的数据会传送给每一个叶节点;

在无根数据层面中,从任何节点发出的数据都会“蔓延”到整个多
播组,每个节点对于数据的发送也没有任何限制,可以向任何组成员 发送数据。

IP多播采用的就是两个层面上的“无根”方式。

§3.9.2 多播通信
有根数据和无根数据层面数据流比较 根节点 发送数据 叶节点 叶节点 发送数据 …… 发送数据 叶节点

A 有根数据层面数据流向

叶节点
数据流 数据流 叶节点 B 无根数据层面数据流向

叶节点

叶节点

§3.9.2 多播通信
IP多播通信需要依赖一个特殊的地址,称为多播地址。 多播IP地址是一个D类IP地址,范围在224.0.0.0到 239.255.255.255之间。 其中还有许多地址是为特殊用途而保留的。比如: 224.0.0.0 根本没有使用(也不能使用) 224.0.0.1 代表子网内的所有系统(主机) 224.0.0.2 代表子网内的所有路由器。 在实际应用中,可使用除这三个保留多播地址之外的任何地 址。

§3.9.2 多播通信
为了支持多播,专门引入了一个特殊的协议( Internet Group Management Protocol,IGMP ),以便管理多播客户机,以及它们在 组内的成员关系。 多播主机利用IGMP通知路由器,路由器所在子网的一台计算机想 加入一个特定的多播组。IGMP是IP多播方案的基础,两个多播节点之 间的所有路由器都必须提供对IGMP的支持。 若一个应用加入了多播组,一条IGMP“加入”命令便会发给子网 上那个特殊的“所有路由器”地址(224.0.0.2)。该命令用于通知所有 路由器,有客户机对一个特定的多播地址产生了兴趣,即它想加入那 个多播组。以后,假如路由器收到了发给这个多播地址的数据,便会 将其转发给所有多播客户机。

§3.9.2 多播通信
如果一个端点加入多播组,便会同时指定一个“存在时间” (TTL)参数。通过该参数,我们便知道对于在端点机器上运行的多播 应用程序来说,为了收发数据,中途需要经历多少个路由器。 例如,假定我们编写了一个IP多播应用,令其加入组Group,同时 TTL值设为3。这时,一个加入命令会传给本地子网上的“所有路由器” 组。子网内的路由器会根据这一命令,判断出自己以后应将多播数据 转发到该主机地址。随后,路由器会将T T L值减1,再将一条加入命 令传给与自己相邻的各个网络。那些网络上的路由器会如法炮制,最 后又将T T L值减去1,直到TTL值已经变成了0,该命令不会再继续传 递下去(不再传给相邻的网络)。从中可以看出,TTL实际限制了多 播数据能够蔓延得多“远”。

§3.9.2 多播通信
(2)Winsock多播通信的实现 针对IP多播,Winsock提供了两种不同的实现方法,具体取 决于使用的是哪个版本的Winsock。 ? 第一种方法是Winsock 1.1提供的,要求通过套接字选项 来加入一个组。 ? Winsock 2.2则引入了一个新函数,专门负责多播组的加 入。这个函数便是WSAJoinLeaf,它与基层协议是无关的。 只介绍Winsock 1.1方法,是应用得最广的一种方法(特别 由于它是自Berkeley套接字衍生出来的)。

§3.9.2 多播通信

Winsock1.1中,实现IP多播通信所需的基本步骤: 第一步:用socket函数创建一个套接字,注意要设为AF_INET地址 家族以及SOCK_DGRAM套接字类型。 第二步:如果想从组内接收数据,将套接字同一个本地端口绑定 到一起。 第三步:调用setsockopt函数,同时设置IP_ADD_MEMBERSHIP选项, 指定想加入的那个组的地址结构。

§3.9.2 多播通信
假如一个应用程序只是打算发送数据,便不必加入一个IP多
播组。向多播组发送数据时,网络中传输的数据包与普通UDP包 大致相同,只是目的地址换成了一个特殊的多播地址而已。 但假如想接收多播数据,便必须加入一个组。但是除了对组 成员资格的要求之外,IP多播通信与普通的UDP协议通信并无什

么区别:两者都是无连接的、不可靠的。

§3.9.2 多播通信
在Winsock 1.1中,IP多播组的加入和离开是用setsockopt 命令来完成的,同时要用到IP_ADD_MEMBERSHIP(加入组)和 IP_DROP_MEMBERSHIP(脱离组)这两个选项。 使用这两个套接字选项时,必须传递一个ip_mreq结构,它 的定义如下: struct ip_mreq{ struct in_addr imr_multiaddr;//要加入的多播组地址 struct in_addr imr_interface; } imr_interface:指定要通过它送出多播数据的本地(或本机)接 口。如果将imr_interface设为INADDR_ANY,便会自动使用默 认接口;否则,请指定本地接口具体要使用哪个IP地址。

§3.9.2 多播通信
下面的程序段展示了如何加入一个多播组234.5.6.7: SOCKET s; struct ip_mreq ipmr; SOCKETADDR_IN local; int len=sizeof(ipmr); s=socket(AF_INET,SOCK_DGRAM,0); local.sin_family=AF_INET; local.sin_addr.s_addr=htonl(INADDR_ANY); local.sin_port=htons(10000); bind(s,(SOCKETADDR *)&local,sizeof(local)); ipmr.imr_multiaddr.s_addr=inet_addr(“234.5.6.7”); ipmr.imr_interface.s_addr=htonl(INADDR_ANY); setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&ipmr,&len);

§3.9.2 多播通信
要离开一个多播组,只需调用setsockopt,同时设置 IP_DROP_MEMBRSHIP。注意仍然要传递一个ip_mreq结构,其中 的值与加入那个组时是一样的。 setsockopt(s, ,IPPROTO_IP, IP_DROP_MEMBRSHIP, (char *)&ipmr,&len); 其实,采用广播方式也能实现组播。采用的方法是选用特 定的子网编址作为广播地址,这样就可以在该子网内部实现广 播通信,从整个网络来看,就实现了组播。

多播通信举例
?例3.13 多播通信的发送方程序 ?例3.14 多播通信的接收方程序

§3.9.2 多播通信
(3)组播专用套接字选项

?IP_MULTICAST_TTL
该选项用于设置多播数据的TTL(存在时间)值。默认情况下,TTL=1。也 就是说,多播数据不允许传出本地网络之外,即只有同一个网络内的多播成员 才会收到数据。假如增大T T L的值,多播数据就可经历多个路由器传到其他 网络。 路由器每收到一个数据包,并判断出自己应将这个包转发给相邻的网络, 那么TTL值随即会被减1。若减1之后,发现T T L的新值变成了0,路由器便会 将这个包即时地丢弃。若多播数据报的目标地址在224.0.0.0到224.0.0.255之 间,那么多播路由器不会对其进行转发,无论它们的TTL有多大。 若调用setsockopt,level参数便是IPPROTO_IP,optname参数是 IP_MULTICAST_TTL,而optval参数是一个整数,用于指定新的TTL值。例如: int optval=8; setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,(char *)optval,sizeof(int))

§3.9.2 多播通信
?IP_MULTICAST_LOOP

该选项决定了应用程序是否接收自己的多播数据。如应用程序加入了一个 多播组,并向那个组发送数据,应用程序便会接收到那些数据 要注意的是,若只是想让应用程序将数据发给一个多播组,那么它并不一 定非要加入多播组。只有在希望接收发给那个组的数据时,才需要加入那个组。 设计IP_ MUTLICAST_ LOOP套接字选项的目的便是禁止将数据返还给本地接口。 使用这个选项,可将一个整数值作为optval参数传递,它其实仅仅是一个“布 尔”值,用于指出到底允许还是禁止多播返还。示范代码: int optval; optval =0; //禁用回环 if(setsockopt(s,IPROTO_IP,IP_MULTICAST_LOOP,(char *)optval, sizeof(int))= =SOCKET_ERROR) { //错误 }

§3.10 Winsock I/O模型
?套接字的阻塞与非阻塞模式 ?I/O模型

§3.10.1 套接字的阻塞与非阻塞模式
Winsock套接字在两种模式下执行I/O操作:阻塞和非阻塞。 在阻塞模式下,在I/O操作完成前,执行操作的Winsock函 数(比如send和recv)会一直等候下去,不会立即返回程序 (将控制权交换给程序)。这就意味着任一个线程在某一时刻 只能执行一个I/O操作,而且应用程序很难同时通过多个建好连 接的套接字进行通信。在默认的情况下,套接字为阻塞模式。 在非阻塞模式下,Winsock函数无论如何都会立即返回并

交出程序的控制权。

§3.10.1 套接字的阻塞与非阻塞模式
(1)阻塞模式 大多数Winsock应用都是遵照一种“生产者/消费者”模型来编制的。在这种 模型中,应用程序需要读取或写入指定数量的字节,然后以它为基础执行一些计 算。如下所示是一个典型的例子。 SOCKET sock; char buff[256]; int done=0; … while(!done){ nBytes = recv(sock,buff,65); if(nBytes = = SOCKET_ERROR)
{ printf(“recv failed!”); return; }

DoComputationOnData(buff); }
这段代码的问题在于,只有从系统缓冲区读取数据,才可执行if语句并返回。如果 系统没有读取数据,可能永远无法返回。 解决这个问题的办法是将应用程序划分为一个“读线程”和一个“计算线程”。两 个基本点线程共享一个数据缓冲区。“读线程”的职责是从网络连续地读入数据,并将 其置入共享缓冲区中。在“读线程”将计算线程开始工作需要的数据量拿到手后,便会 触发一个事件,通知计算进程可以工作了。随后,计算线程从缓冲区中取走(删除)一 个数据块,然后进行计算。 但是这种方案存在缺点,扩展性差。

§3.10.1 套接字的阻塞与非阻塞模式
(2)非阻塞模式 将一个套接字置为非阻塞模式时后,Winsock函数调用立即会 返回。大多数情况下,函数调用其结果会“失败”,并返回一个 WSAEWORLDBLOCK错误。意味着请求的操作在调用期内没有时间完 成。 比如,系统的输入缓冲区内没有“待读”的数据,那么recv (接收数据函数)调用就会返回WSAEWOULDBLOCK错误。所以,通 常我们需要重复调用一个函数,直至获得一个成功返回代码。所 以程序员需要通过不断地检查函数返回代码以判断一个套接字何 时可以供读写。 为了免去这样的麻烦,Winsock提供了不同的套接字I/O模型 对I/O进行管理,包括:select(选择)、WSAAsyncSelect(异步 选择)、WSAEventSelect(事件选择)、Overlapped(重叠)以 及Completion port(完成端口)。

§3.10.2 I/O模型
(1)WSAAsyncSelect模型

WSAAsyncSelect是Winsock提供的一个常用的I/O模型。利用这个模型, 应用程序可以在一个套接字上,接收以Windows消息为基础的网络事件 通知。该模型的实现方法是通过调用WSAAsyncSelect函数自动将套接字 设置为非阻塞模式,并向Winsock DLL注册一个或多个感兴趣的网络事 件,并提供一个通知时使用的窗口句柄,当注册的网络事件发生时,对 应的窗口将收到一个基于消息的通知。

要 想 使 用 WSAAsyncSelect 模 型 , 在 应 用 程 序 中 , 首 先 必 须 用 CreateWindow函数创建一个窗口,再为该窗口提供一个窗口例程支持函 数(Winproc)。也可以使用一个对话框,为其提供一个对话例程,因 为本质上将,对话框也是窗口。设置好窗口的框架后,便可开始创建套 接字,并调用WSAAsyncSelect函数,打开窗口消息通知。

§3.10.2 I/O模型
WSAAsyncSelect函数定义如下:
int WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent); 参数说明: s:指定需要事件通知的套接字的描述符。 hWnd:标识一个在网络事件发生时需要接收消息的窗口句柄。

WMsg:指定在发生网络事件时,打算接收的消息。该消息会投递到由 hWnd窗口句柄指定的那个窗口。
lEvent:指定一个位掩码,对应于一系列事件的组合。

§3.10.2 I/O模型
用于WSAAsyncSelect函数的网络事件类型
事件类型 FD_READ FD_WRITE FD_OOB FD_ACCEPT FD_CONNECT FD_CLOSE FD_QOS FD_GROUP_QOS FD_ROUTING_I NTERFACE_CHA NGE FD_ADDRESS_C HANGE 含义 应用程序想要接收有关是否可读的通知,以便读入数据 应用程序想要接收有关是否可写的通知,以便读入数据 应用程序想要接收是否有带外(OOB)数据抵达的通知 应用程序想要接收与进入连接有关的通知 应用程序想要接收与一次连接或者多点join操作完成的通知 应用程序想要接收与套接字关闭有关的通知 应用程序想要接收套接字“服务质量”(QOS)发生更改的通 知 应用程序想要接收套接字组“服务质量” 发生更改的通知(现 在没有用,为将来套接字组的使用保留) 应用程序想接收在指定的方向上,与路由接口发生变化的通知

应用程序想要接收针对套接字的协议家族,本地地址列表发生 变化的通知

§3.10.2 I/O模型

大多数应用程序感兴趣的网络事件类型包括:FD_READ、FD_WRITE、 FD_ACCEPT、FD_CONNECT和FD_CLOSE。如果应用程序同时对多个网络事 件有兴趣,需对各种类型执行一次简单的按位OR(或)运算,然后将 它们分配给lEvent。举例来说: WSAAsyncSelect(s,hWnd,WM_SOCKET, FD_READ| FD_WRITE| FD_CLOSE) 调用这个函数,应用程序就可以在套接字s上,接收到有关发送、接收 以及套接字关闭等网络事件的通知。特别要注意的是,多个事件必须 在套接字上一次注册。一旦某个套接字上允许了事件通知,那么这个 事件通知一直有效,除非以后明确调用closesocket命令,或者由应用 程序再次调用WSAAsyncSelect,更改注册网络事件类型。 若应用程序调用了WSAAsyncSelect,那么套接字的模式从“阻塞” 自动变成“非阻塞”。应用程序在一个套接字上成功调用了 WSAAsyncSelect之后,应用程序会在hWnd窗口句柄参数对应的例程 中,以Windows消息的形式,接收网络事件通知。

§3.10.2 I/O模型
窗口例程通常定义如下: LRESULT CALLBACK WindowProc(HWND hWnd, UNIT uMsg, WPARAM wParam, LPARAM lParam); 参数说明: hWnd:窗口句柄,对窗口例程的调用正是由那个窗口发出的。 uMsg:指定需要对那些消息进行处理。对WSAAsyncSelect模型来说,感兴趣 的是WSAAsyncSelect调用中定义的消息。 wparam:指定在其上发生网络事件的套接字. lParam:包含了两方面的信息。低位字指定了已经发生的网络事件,高位字包 含了可能出现的错误代码。 网络事件消息抵达一个窗口例程后,应用程序首先应检查lParam的高字 位 , 判 断 是否 在 套 接 字上 发 生 了 一个 网 络 错 误 。 这 里 有 一个 特 殊的宏: WSAGETSELECTERROR,可用它返回高字位包含的错误信息。若应用程 序发现套接字上没有产生任何错误,接着便判断发生了哪个网络事件,具体 的 做 法 便 是 读 取 lParam之 低 字 位 的 内 容 。 此 时 可 使 用 另 一 个 特 殊 的 宏 : WSAGETSELECTEVENT,用它返回lParam的低字部分。

§3.10.2 I/O模型
下面的例子演示了如何使用WSAAsyncSelect这种I/O模型,来实现窗口消息的 管理。在源程序中,我们着重强调的是开发一个基本服务器应用要涉及到的 基本步骤,忽略了开发一个完整的Windows应用需要涉及到的大量编程细节。 这个例子只给出如何开发一个简单的服务器程序。它会监听2000端口上的连 接请求,并且接受TCP连接。当这个程序收到来自客户端的/数据,直到客户 端关闭连接。客户端程序使用前面会话套接字例子中的客户端程序。 //服务器端程序 #include <winsock2.h> #include <windows.h> #include <stdio.h> #include <conio.h> #pragma comment(lib,"ws2_32.lib") #define PORT 2000 #define DATA_BUFSIZE 80 #define WM_SOCKET (WM_USER + 1)

§3.10.2 I/O模型
void main(void){ MSG msg; DWORD Ret; SOCKET Listen; SOCKADDR_IN InternetAddr; HWND Window; WSADATA wsaData; if ((Window = MakeWorkerWindow()) == NULL) //建立窗口 return; WSAStartup(0x0202, &wsaData)) != 0) //初始化套接字 Listen = socket (AF_INET, SOCK_STREAM, 0)) //创建套接字 //异步选择 WSAAsyncSelect(Listen, Window, WM_SOCKET, FD_ACCEPT|FD_CLOSE); //本地地址填充 InternetAddr.sin_family = AF_INET; InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY); InternetAddr.sin_port = htons(PORT);

§3.10.2 I/O模型
//套接字绑定 bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) listen(Listen, 5) //置套接字为监听模式 //为应用程序解释和分发窗口消息 while(Ret = GetMessage(&msg, NULL, 0, 0)) { if (Ret == -1) return; TranslateMessage(&msg); DispatchMessage(&msg); } }

§3.10.2 I/O模型
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { SOCKET Accept; char DataBuf[80]; memset(DataBuf,0,80); if (uMsg == WM_SOCKET) { //发生winsock消息 switch(WSAGETSELECTEVENT(lParam)){ case FD_ACCEPT: Accept = accept(wParam, NULL, NULL)) WSAAsyncSelect(Accept, hwnd, WM_SOCKET, FD_READ|FD_WRITE|FD_CLOSE); break; case FD_READ: // 读数据 recv(wParam,DataBuf,80,0); break; case FD_CLOSE: closesocket(wParam); break; } } return DefWindowProc(hwnd, uMsg, wParam, lParam); }

§3.10.2 I/O模型

(2)select 模型 select模型是Winsock中最常见的模型。利用select函数,可以判断套接 字上是否存在数据,或者能否向一个套接字写入数据。select函数格式如下: int select(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout); nfds:忽略,提供这个套接字是为了与早期的Berkeley套接字程序相兼容。 readfds: 指向等待可读性检查的套接组。 writefds: 指向等待可写性检查的套接组。 Exceptfds:指向等待错误检查的套接组。 在这三个参数中,至少有一个不为空值(NULL)。在任何不为空的集合中, 必须包含至少一个套接字句柄。否则,select函数便没有任何东西可以等待了。 timeout:是一个timeval结构,用于决定select最多等待I/O操作完成的时间。 如果timeout是一个空指针,那么select调用会无限期地“阻塞”或等待下去, 直到至少有一个描述符符合纸定的条件后结束。

§3.10.2 I/O模型
Timeval结构的定义如下: struct timeout { long tv_sec; long tv_usec; } tv_sec: 以秒为单位指定等待时间。 tv_usec:以毫秒(ms)为单位指定等待时间。 select()调用成功完成后,fd_set结构中将存有满足一定 条件的套接字组的子集。并且select()返回满足条件的套接字 的数目。若超过timeout设定的时间,便会返回0,若调用失败, 则返回SOCKET_ERROR。

§3.10.2 I/O模型
Winsock提供了FD_SETSIZE变量用于确定一个集合中最多的套 接字描述字数目(FD_SIZE缺省值为64,可以在包含Winwock.h前 用#define FD_SETSIZE来改变该值)。此外,还提供了四个宏对 fd_set结构进行操作。它们分别为: ?FD_CLR(s,*set):从集合set中删除套接字s。 ?FD_ISSET(s,*set):若s为集合set中一员,非零,否则为零。 ?FD_SET(s,*set):向集合set添加描述字s。 ?FD_ZERO(*set):将set初始化为空集NULL。 假如,我们想知道是否可以从一个套接字中安全地读取数据, 而不会处于无休止的“阻塞”状态,便可使用FD_SET宏,将自己 的套接字分配给fd_read集合,再来调用select。要想检测自己的 套接字是否仍属fd_read集合的一部分,可使用FD_ISSET宏。

§3.10.2 I/O模型
完成select操作一个或多个套接字句柄的全过程: 第一步:使用FD_ZERO宏,初始化自己感兴趣的每一个fd_set。 第三步:调用select函数,然后等待在指定的fd_set集合中,I/O活动 设置好一个或多个套接字句柄。Select完成后,会返回在所有fd_set集

第二步:使用FD_SET宏,将套接字句柄分配给自己感兴趣的每个fd_set。

合中设置的套接字句柄总数,并对每个集合进行相应的更新。
第四步:根据select的返回值,应用程序便可判断出哪些套接字存在着 尚未完成的I/O操作,具体的办法是使用FD_ISSET宏,对每个fd_set集

合进行检查。
第五步:知道了每个集合中“待决”的I/O操作之后,对I/O进行处理, 然后返回第一步,继续进行select处理。

§3.10.2 I/O模型
下面的例子说明了将一个套接字设置成select模型所需的基本步骤。 SOCKET s; fd_set fdread; int ret; while(true) //创建套接字,并且接受一个连接,管理套接字上的I/O操作 { FD_ZERO(&fdread); //在调用select()之前清除读列表 FD_SET(s,&fdread); //在读列表中添加套接字s ret= =select(0,&fdread,NULL,NULL,NULL) if(ret>0) { //发生读事件 //对于这个简单的例子,select()应该返回值1。 //处理多个套接字的应用程序应该返回比1大的值。 if(FD_SET(s,&fdread)){ //套接字s上发生读事件 } } } 若想要在这个应用程序中添加更多的套接字,只需为额外的套接字维护它们的 一个列表。

§3.10.2 I/O模型
(3)WSAEventSelect模型 WSAEnentSelect 模 型 是 Winsock 提 供 的 另 一 个 有 用 的 异 步 模 型 。 它 和 WSAAsyncSelect模型类似的是,它也允许应用程序在一个或多个套接字上, 接收以事件为基础的网络事件通知,并且它支持的网络事件与 WSAAsyncSelect模型一样,它与WSAAsyncSelect模型最主要的差别在于网络 事件会被发送到一个事件句柄,而不是发送到一个窗口。使用 WSAAEventSelect模型,要经过以下几步: 第一步:首先创建一个事件对象。调用WSACreateEvent函数创建事件对象 WSAEVENT WSACreateEvent(void); 第二步:创建好事件对象句柄后,必须将其与某个套接字关联在一起,同时注 册自己感兴趣的网络事件类型,方法是调用WSAEventSelect函数. int WSAEventSelect(SOCKET s,WSAEVENT hEvent,long NetworkEvents); hEvent:指定要与套接字关联在一起的事件对象。 lNetworkEvents:对应一个“位掩码”,用于指定应用程序感兴趣的各种网 络事件类型的一个组合。

§3.10.2 I/O模型
为WSAEventSelect创建的事件拥有两种工作状态,以及两种工作模式。 两种工作状态:有信号(signaled)、无信号(nonsignaled)。 两种工作模式:人工重设(manual reset)、自动重设(auto reset)。 WSACreateEvent最开始的工作状态为无信号,并用一种人工重设模式,来创 建事件句柄。随着网络事件触发了与一个套接字关联在一起的事件对象,工 作状态便会从“无信号”转变成“有信号”。由于事件对象是在一种人工重 设模式中创建的,所以在完成了一个I / O请求的处理之后,我们的应用程序 需要负责将工作状态从已传信更改为未传信。要做到这一点,可调用 BOOL WSAResetEvent(WSAEVENT hEvent); 该函数唯一的参数便是一个事件句柄;基于调用是成功还是失败,会分别返 回TRUE或FALSE。应用程序完成了对一个事件对象的处理后,便应调用 WSACloseEvent函数,释放由事件句柄使用的系统资源。 BOOL WSACloseEvent(WSAEVENT hEvent); 该函数也要拿一个事件句柄作为自己唯一的参数,并会在成功后返回TRUE, 失败后返回FALSE。

§3.10.2 I/O模型
第三步:套接字同事件对象关联在一起后,程序便可开始I/O处理;方法是等 待网络事件触发事件对象句柄的工作状态。WSAWaitForMultipleEvents函数 便是用来等待一个或多个事件对象句柄,并在事先指定的一个或所有句柄进 入“有信号”状态后,或在超过了一个规定的时间周期后,立即返回。 WSAWaitForMultipleEvents函数的定义如下: DWORD WSAWaitForMultipleEvents( DWORD cEvents, const WSAEVENT FAR *lphEvent, BOOL fwaitAll, DWORD dwTimeout, BOOL fAlertable ); cEvents:指定的是事件对象的数量。 lphEvents:是一个指针,用于直接由WSAEVENT对象构成的一个数组。

§3.10.2 I/O模型
WSAWaitForMultipleEvents只能支持由WSA_MAXIMUM_WAIT_EVENTS对象规定 的一个最大值64 。因此针对发出WSAWaitForMultipleEvents调用的每个线程, 该I/O模型一次最多都只能支持64个套接字。假如想让这个模型同时管理不止 64个套接字,必须创建额外的工作者线程,以便等待更多的事件对象。 fWaitAll:指定了WSAWaitForMultipleEvents如何等待在事件数组中的对象。 若设为TRUE,那么只有等lphEvents数组内包含的所有事件对象都已进入“有 信号”状态,函数才会返回;但若设为FALSE,任何一个事件对象进入“有信 号”状态,函数就会返回。就后一种情况来说,返回值指出了到底是哪个事 件对象造成了函数的返回。通常,应用程序应将该参数设为FALSE。 dwTimeout:规定了等待的最长时间,以毫秒为单位。超过规定的时间,函数 就会立即返回,即使由fWaitAll参数规定的条件尚未满足。如超时值为0,函 数会检测指定事件对象的状态,并立即返回。这样一来,应用程序实际便可 实现对事件对象的“轮询”。如dwsTimeout设为WSA_INFINITE(永远等待), 那么只有在一个网络事件传信了一个事件对象后,函数才会返回。

§3.10.2 I/O模型

fAlertable:使用WSAEventSelect模型的时候,它是可以忽略的, 且应设为FALSE。该参数主要用于在重叠式I/O模型中,在完成例 程的处理过程中使用。后面还会对此详述。 若WSAWaitForMultipleEvents收到一个事件对象的网络事件通 知,便会返回一个值,指出造成函数返回的事件对象。这样一来, 我们的应用程序便可引用事件数组中有信号的事件,并检索与那 个事件对应的套接字,判断到底是在哪个套接字上,发生了什么 网络事件类型。 对事件数组中的事件进行引用时,应该用 WSAWaitForMultipleEvents的返回值,减去预定义值 WSA_WAIT_EVENT_0,得到具体的引用值(即索引位置)。

§3.10.2 I/O模型
fAlertable:使用WSAEventSelect模型的时候,它是可以忽略的,且 应设为FALSE。该参数主要用于在重叠式I/O模型中,在完成例程的处 理过程中使用。后面还会对此详述。 若WSAWaitForMultipleEvents收到一个事件对象的网络事件通知, 便会返回一个值,指出造成函数返回的事件对象。这样一来,我们的 应用程序便可引用事件数组中有信号的事件,并检索与那个事件对应 的套接字,判断到底是在哪个套接字上,发生了什么网络事件类型。 对事件数组中的事件进行引用时,应该用 WSAWaitForMultipleEvents的返回值,减去预定义值 WSA_WAIT_EVENT_0,得到具体的引用值(即索引位置)。 index=WSAWaitForMultipleEvents(…); MyEvent=EventArray[index-WSA_WAIT_EVENT_0];

§3.10.2 I/O模型
知道了造成网络事件的套接字后,接下来可调用WSAEnumNetworkEvents函数, 调查发生了什么类型的网络事件。该函数定义如下: INT WSAEnumNetworkEvent( SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVNETS lpNetworkEvents ); s:对应于造成了网络事件的套接字。 hEventObject:是可选的;它指定了一个事件句柄,对应于打算重设的那个 事件对象。由于事件对象处在一个“有信号”状态,所以可将它传入,令其 自动成为“无信号”状态。如果不想用hEventObject参数来重设事件,那么 可使用WSAResetEvent函数。 lpNetworkEvents:代表一个指针,指向WSANETWORKEVENTS结构,用于接收套 接字上发生的网络事件类型以及可能出现的任何错误代码。

§3.10.2 I/O模型
WSANETWORKEVENTS结构的定义: typedef struct _WSANETWORKEVENTS { long lNetworkEvents;

int iErrorCode[FD_MAX_EVENTS];
}WSANETWORKEVENTS,FAR *LPWSANETWORKEVENTS; lNetworkEvents:指定了一个值,对应于套接字上发生的所有网络事件类型。 注意一个事件进入有信号状态时,可能会同时发生多个网络事件类型。 iErrorCode:指定的是一个错误代码数组,同lNetworkEvents中的事件关联 在一起。针对每个网络事件类型,都存在着一个特殊的事件索引,名字与事 件类型的名字类似,只是要在事件名字后面添加一个“_ BIT”后缀字串即可。 例如,对FD_READ事件类型来说,iErrorCode数组的索引标识符便是 FD_READ_BIT。

§3.10.2 I/O模型
下述代码片断对此进行了阐释(针对FD_READ事件): //处理FD_READ通知事件 if(NetworkEvents.lNetworkEvents & FD_READ) {

if(NetworkEvents.iErrorCode[FD_READ_BIT]!=0)
{ printf(“FD_READ failed with error %d\n”, NetworkEvent.iErrorCode[FD_READ_BIT]); } } 完成了对WSANETWORKEVENTS结构中的事件的处理之后,我们的应用程序应在 所有可用的套接字上,继续等待更多的网络事件。

习题与思考题
3-1 简述TCP协议和UDP协议各有什么特点,适用于什么样的场合? 3-2 一个B类网络的网络地址为154.100.0.0,现在主机标识中取出5位作为 子网标识,试计算其子网掩码和网络/子网地址。 3-3 地址解析和域名解析各指什么意思? 3-4 什么叫套接字,套接字按通信性质分,可以分为哪几类? 服务器端程序段(面向连接),使其可以侦听客户端的连接请求。 3-6 编写一个会话通信程序,完成该服务器端程序和客户端程序,客户端 发送的数据从“data0”到“data9”(字符串),服务器端每收到一个数据, 回应一个echo信号,如“echo data0”。 3-7 完成数据报通信程序。 3-9 广播和组播通信程序中套接字类型应该是什么类型,组播地址应该是 哪一类地址,范围为多少? 3-10 套接字的阻塞模式和非阻塞模式各是什么含义?


相关文章:
第3章 TCPIP协议new.doc
第3章 TCPIP协议new - 第 3 章 TCP/IP 协议 TCP/IP(
P3 基于TCP协议的程序设计解析_图文.ppt
P3 基于TCP协议的程序设计解析 - 计算机网络编程技术 第3章 基于TCP协议的程序设计 12/12/2018 1 本章的主要内容 ? ? ? ? 回顾TCP协议的基本特点 了解阻...
TCPIP协议及网络编程技术_图文.ppt
TCPIP协议网络编程技术 - 第11章 电子邮件 11.1 11.2 11.3 11.4 11.5 电子邮件系统结构 TCP/IP电子邮件地址 电子邮件格式 SMTP协议 邮箱访问 11...
基于tcpip协议的qte网络编程_图文.ppt
基于TCP/IP协议的 基于TCP/IP协议的QtE网络编程 协议的QtE网络编程 刘学钢 1 内容概要→ QtE的网络编程演示 QtE的网络编程演示 → 讲解主要内容: 讲解主要内容:...
TCP IP协议与网络编程 任泰明第3章 传输层协议UDP和TCP.ppt
第3章 传输层协议UDP和TCP 3.1 端到端通信和端口号 3.1.1 端到端
TCPIP协议网络编程样卷及参考答案.doc
河北联合大学 ~ 学年 秋 季学期考试试卷课程名称:TCP/IP 协议网络编程 试卷...30页 免费 第3章 基于TCPIP协议的网... 127页 1下载券 喜欢...
第01章 TCPIP网络编编程基础_图文.ppt
机工出版社 网络编程与分层协议设计》.刘飚 《Linux/Unix网络编程》.甘刚 .水利水电出版社 《用TCP/IP进行网际互联. 第一卷》 《用TCP/IP进行网际互联. 第三...
tcpip网络编程答案.doc
tcpip 网络编程答案【篇一:tcpip 课后习题答案整理版】 rnet 是以 arpanet 网络为雏形建立的 2、现在 internet 中广泛使用的 tcp/ip 协议族,其版本为 v4 3...
TCPIP协议及网络编程技术.ppt
TCPIP协议网络编程技术 - 第3章 IP 协议◆◆◆ IP协议的目的 IP协议的工作原理 IP地址 IP数据包格式 3.1 IP协议的目的与工作原理 ? 3.1.1 IP...
基于TCPIP协议的QtE网络编程.ppt
两种方法的比较: 15 第二章 QtE网络编程一、TCP与UDP协议 TCP/IP协议将经典...特别注意的:void newConnection ( int socket ) 19 、实例---QtE下是如何...
第7讲 CH5-传输层与TCPIP网络编程_图文.ppt
第7讲 CH5-传输层与TCPIP网络编程 - 第三章 面向连接的网络通信实现 1. 万维网通信的特点 2. 传输层协议概述 3. 传输控制协议TCP 4. TCP传输协议报文头...
第6讲 CH5-传输层与TCPIP网络编程_图文.ppt
第6讲 CH5-传输层与TCPIP网络编程 - 第三章 面向连接的网络通信实现 1. 万维网通信的特点 2. 传输层协议概述 3. 传输控制协议TCP 4. TCP传输协议报文头...
TCPIP协议及网络编程技术_图文.ppt
TCPIP协议网络编程技术 - 第13章 网络文件 13.1 FTP 文件传输协议 13.2 TFTP 13.3 NFS 13.1 FTP 文件传输协议 ? ? FTP 协议位于 OSI...
第7章 TCP网络编程基础_图文.ppt
第7章 TCP网络编程基础 - 第7 章 TCP网络编程基础 TCP网络编程基础 TCP协议是 协议是TCP/IP协议中很重要的一个协议,它由于 协议中很重要的一个协议, 协议是...
TCPIP协议及网络编程技术_图文.ppt
TCPIP协议网络编程技术 - 第16章 常见操作系统TCP/IP协议实 现
TCPIP协议及网络编程技术_图文.ppt
TCPIP协议网络编程技术 - 第6章 路由协议 6.1 6.2 6.3 6.
TCPIP协议及网络编程技术_图文.ppt
TCPIP协议网络编程技术 - TCP/IP协议 及网络编程技术 本书主要面向
TCPIP网络编程与C代码示例.pdf
3.网络层:IP 因为 OSI 七层结构较为复杂,所以使用较多的是 TCP/IP 模型,...基于 UDP 协议 基于 TCP(面向连接)的 Socket 编程服务器端顺序: 1.加载套接...
基于TCPIP的网络文字聊天程序设计.doc
网络编程 课程设计说明书 设计题目 基于 TCP/IP 的...{ CClientSocket* psocket = new CClientSocket()...第3章 基于TCPIP协议的网... 127页 1下载券 喜欢...
基于TCPIP的网络聊天工具设计.doc
基于TCP/IP 的网络聊天工具设计【摘要】本系统是实现在局域网内多人在线聊天的聊天工具,主要是采用套接字的原 理进行编程,将 Visual C++6.0 作为开发平台,并结合...
更多相关标签: