网络编程实用教程_第3章.ppt_第1页
网络编程实用教程_第3章.ppt_第2页
网络编程实用教程_第3章.ppt_第3页
网络编程实用教程_第3章.ppt_第4页
网络编程实用教程_第3章.ppt_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第3章 Windows环境的网络编程,Windows Sockets 规范 WinSock 规范与Berkeley套接口的区别 Winsock 1.1 的库函数,3.1 Windows Sockets规范,3.1.1 概述 Microsoft公司以Berkeley Sockets规范为范例,定义了Windows Socktes规范,简称Winsock规范。 这是Windows操作系统环境下的套接字网络应用程序编程接口(API)。 包含: Berkeley Socket 风格的库函数; 针对Windows操作系统的扩展库函数。 可以充分利用Windows的消息驱动机制编程。,图3.1 网络应用进

2、程利用Windock进行通信,3.1.2 Windows Sockets规范 Windows Sockets 规范是一套开放的、支持多种协议的Windows下的网络编程接口。 从1991年到1995年,从1.0版发展到2.0.8版,已成为Windows网络编程的事实上的标准。 1Windows Sockets 1.1版本 在Winsock.h包含文件中,定义了WinSock 1.1版本库函数的语法、相关的符号常量和数据结构。 库函数的实现在Winsock.dll动态链接库文件中。 (1) WinSock 1.1 全面继承了Berkeley Sockets规范。 见表 3.1,表3.1 WinS

3、ock 1.1继承Berkeley Sockets的函数,表3.1 WinSock 1.1继承Berkeley Sockets的函数(cont.),(2) 数据库函数 表3.2列出了Winsock规范定义的数据库查询函数。 其中六个采用getXbyY()的形式,大多要借助网络上的数据库来获得信息,,表3.2 数据库查询函数,getXbyY( )形式的数据库例程都返回一个指针,指向某种类型的结构区域,用来存放函数返回的数据信息。 这些结构区域是由winsock实现分配的,由系统管理,所以指针指向的结构数据是易失的,只在该线程的下一个Winsock API调用前有效。 一个线程中只有一个该结构的副

4、本,因此应用程序在发出下一个Winsock API调用前应把所需的信息复制下来。 应用程序不应试图修改或释放这个结构。,(3) WinSock 1.1 扩充了Berkeley Sockets规范 针对微软 Windows的特点,WinSock 1.1定义了一批新的库函数,提供了对消息驱动机制的支持,有效地利用Windows多任务多线程的机制。 扩充主要是提供了一些异步函数,增加了符合Windows消息驱动特性的网络事件异步选择机制,使得开发高性能网络通信程序成为可能。 这些扩充函数的名字都以WSA开头,后面跟async表示是专为实现异步机制而设置的。 编程时必须使用WSAStartup()和W

5、SACleanup(),其它函数随意使用。,10,表3.3 Winsock 1.1的常用扩展函数,(4) WinSock 1.1只支持TCP/IP协议栈 Winsock 1.1的实现,即Winsock.dll和底层协议栈的接口是唯一的,且是独占的,只能访问TCP/IP协议栈。 因此, Winsock 1.1套接字仅支持单一的通信域,即Internet域。,2WinSock 2.0规范 WinSock 2.0在源码和二进制代码方面与WinSock 1.1兼容,此外还增强了许多功能。 (1)支持多种协议 (2)引入了重叠I/O的概念 (3)使用事件对象异步通知 (4)服务的质量(QOS) (5)套

6、接口组 (6)扩展的字节顺序转换例程 (7)分散/聚集方式I/O (8)新增了许多函数。,3. WinSock 1.1中的阻塞问题 阻塞是在把应用程序从Berkeley套接口环境中移植到Windows环境中的一个主要焦点。 阻塞,是指唤起一个函数,该函数直到相关操作完成时才返回。 在Berkeley套接口模型中,一个套接口的操作的缺省行为是阻塞方式的,除非程序员显式地请求该操作为非阻塞方式。 在Windows环境下,强烈推荐程序员尽可能使用非阻塞方式(异步方式)的操作,因为非阻塞方式的操作能够更好地在非占先的Windows环境下工作。,3.1.3 WinSock规范与Berkeley套接口的区

7、别 1套接口数据类型和该类型的错误返回值 在UNIX中,包括套接口句柄在内的所有句柄,都是非负的短整数; 在WinSock规范中定义了一个新的数据类型,称作SOCKET,用来代表套接字描述符。 typedef u_int SOCKET; socket( )和accept( )函数返回时,返回的就是SOCKET类型。 SOCKET可以取从0到INVALID_SOCKET-1之间的任意值。,要判断socket()和accept()是否正确执行,可以将返回值和INVALID_SOCKET来比较,该常量已在Winsock.h中定义。 例如: 在UNIX套接字规范中 s=socket(); if(s=-

8、1)执行错误处理代码 在Winsock套接字规范中: s=socket(); if(s=INVALID_SOCKET)执行错误处理代码,2select()函数和FD_*宏 在Winsock中,使用select()函数时,应用程序应坚持用FD_XXX宏来设置、初始化、清除和检查fd_set结构, fd_set结构用来代表一组套接口。 3错误代码的获得 UNIX套接字规范中,如果函数执行时发生了错误,会把错误代码放到errno或h_errno变量中。 在Winsock中错误代码可以使用WSAGetLastError()调用得到。 4指针 所有应用程序与Windows Sockets使用的指针都必须

9、是FAR指针。,5. 重命名的函数 有两个伯克利套接口函数改了名字,避免与其它API冲突。 close()改变为closesocket() ioctl()改变为ioctlsocket() 6. Winsock支持的最大套接口数目 一个特定的Windows Sockets提供者所支持的套接口的最大数目是由实现确定的;任何一个应用程序都不应假设某个待定数目的套接口可用。 一个Windows Sockets应用程序可以使用的套接口的最大数目在Winsock.h中缺省值是64,在编译时由常量FD_SETSIZE决定。,7. 头文件 Berkeley头文件被包含在Winsock.h中。 一个Window

10、s Sockets应用程序只需简单地包含Winsock.h就足够了。 8原始套接口 Windows Sockets规范并没有规定Windows Sockets DLL必须支持原始套接口(用SOCK_RAW打开的套接口),但是鼓励提供原始套接口支持。 9、Winsock规范对于消息驱动机制的支持 体现在异步选择机制、异步请求函数、阻塞处理方法、错误处理、启动和终止等方面。,3.2 Winsock 1.1的库函数,3.2.1 Winsock的注册与注销 1初始化函数WSAStartup() Winsock应用程序要做的第一件事,就是必须首先调用WSAStartup()函数对Winsock进行初始化

11、。 初始化也称为注册,注册成功后,才能调用其他的Winsock API函数。 (1) WSAStartup()函数的调用格式 int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData ); wVersionRequested:应用程序要使用的winsock最高版本号; lpWSAData:指向WSADATA结构,返回Winsock API实现细节。,图3.2 在一台计算机中,使用同一Winsock实现的多个网络应用程序,(2) WSAStartup()函数的初始化过程,21,(3) WSADATA结构的定义 #define WSAD

12、ESCRIPTION_LEN 256 #define WSASYS_STATUS_LEN 128 typedef struct WSAData WORD wVersion; WORD wHighVersion; char szDescriptionWSADESCRIPTION_LEN+1; char szSystemStatusWSASYS_STATUS_LEN+1; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char * lpVendorInfo; WSADATA;,22,(4) 初始化函数可能返回的错误代码 WSASYSNO

13、TREADY: 网络通信依赖的网络子系统没有准备好。 WSAVERNOTSUPPORTED: 找不到所需的Winsock API相应的动态连接库。 WSAEINVAL: DLL不支持应用程序所需的Winsock版本。 WSAEINPROGRESS: 正在执行一个阻塞的Winsock 1.1操作。 WSAEPROCLIM: 已经达到Winsock支持的任务数上限。 WSAEFAULT: 参数lpWSAData不是合法指针。,23,(5)初始化Winsock的示例 #include / 对于Winsock 2.0,应包括 Winsock2.h文件 aa() WORD wVersionRequest

14、ed; / 应用程序所需的Winsock版本号 WSADATA wsaData; / 用来返回Winsock 实现的细节信息 Int err; / 出错代码。 wVersionRequested =MAKEWORD(1,1); / 生成版本号1.1 err = WSAStartup(wVersionRequested, /* 至此,可以确认初始化成功,Winsock.DLL可用。 ,2注销函数WSACleanup() 程序使用完Winsock.DLL提供的服务后,应用程序必须调用WSACleanup()函数,来解除与Winsock.DLL库的绑定,释放Winsock实现分配给应用程序的系统资源

15、,中止对Windows Sockets DLL的使用。 (1) WSACleanup()函数的调用格式 int WSACleanup ( void ); 返回值:如果操作成功返回0,否则返回SOCKET_ERROR.,24,(2) WSACleanup()函数的功能 对应于一个任务进行的每一次WSAStartup()调用,必须有一个WSACleanup()调用。 只有最后的WSACleanup()做实际的清除工作;前面的调用仅仅将Windows Sockets DLL中的内置引用计数递减。 一个简单的应用程序为确保WSACleanup()调用了足够的次数,可以在一个循环中不断调用WSAClea

16、nup()直至返WSANOTINITIALISED。 (3) WSACleanup()函数可能返回的错误代码 WSANOTINITIALISED:在调用本API之前应成功调用WSAStartup()。 WSAENETDOWN:检测到网络子系统失效。 WSAEINPROGRESS:一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。,25,3.2.2 Winsock的错误处理函数 Winsock函数执行时都有一个返回值,只能说明函数的执行成功与否,不能从返回值了解出错原因。 1WSAGetLastError()函数 int WSAGetLastError ( void )

17、; 返回本线程进行的上一次Winsock函数调用时,产生的错误代码。 在Winsock.h中定义了所有的错误代码,基数是10000 2WSASetLastError()函数 void WSASetLastError ( int iError ); 本函数允许应用程序为当前线程设置错误代码,并可由后来的WSAGetLastError()调用返回。,3.2.3 主要的Winsock函数 1创建套接口SOCKET() SOCKET socket (int af, int type, int protocol); 返回值:创建成功,返回套接字描述符,否则返回SOCK_ERROR。 举例: SOCKET

18、 sockfd=SOCKET( AF_INET, SOCK_STREAM, 0); /* 创建一个流式套接字。 SOCKET sockfd=SOCKET( AF_INET, SOCK_DGRAM, 0); /* 创建一个数据报套接字。,2将套接口绑定到指定的网络地址BIND() int bind( SOCKET s, const struct sockaddr * name, int namelen); 有许多函数都需要套接字的地址信息,像UNIX 套接字一样,Winsock也定义了三种关于地址的结构,经常使用。 通用的Winsock地址结构,针对各种通信域的套接字,存储它们的地址信息。 st

19、ruct sockaddr u_short sa_family; /* 地址家族 char sa_data14; /* 协议地址 ,28,专门针对Internet 通信域的Winsock地址结构 struct sockaddr_in short sin_family; /*指定地址家族,一定是AF_INET. u_short sin_port; /*指定将要分配给套接字的传输层端口号, struct in_addr sin_addr; /*指定套接字的主机的IP 地址 char sin_zero8; /* 全置为0,是一个填充数。 专用于存储IP地址的结构 Struct in_addr Uni

20、on Struct u_char s_b1,s_b2,s_b3,s_b4; S_un_b; Struct u_short s_w1,s_w2; S_un_w; U_long S_addr;,29,在使用Internet域的套接字时,这三个数据结构的一般用法是: 首先,定义一个Sockaddr_in的结构实例变量,并将它清零; 然后,为这个结构的各成员变量赋值; 第三步,在调用BIND()绑定函数时,将指向这个结构的指针强制转换为 sockaddr*类型。,30,举例: SOCKET serSock; / 定义了一个SOCKET 类型的变量。 sockaddr_in my_addr; / 定义一

21、个Sockaddr_in型的结构实例变量。 int err; / 出错码。 int slen=sizeof( sockaddr); / sockaddr 结构的长度。 serSock = SOCKET(AF_INET, SOCK_DGRAM,0 ); / 创建数据报套接字。 memset(my_addr,0); / 将Sockaddr_in的结构实例变量清零。 my_addr.sin_family = AF_INET; / 指定通信域是Internet。 my_addr.sin_port = htons(21); / 指定端口,将端口号转换为网络字节顺序。,31,/* 指定IP地址,将IP地址

22、转换为网络字节顺序。 my_addr.sin_addr.s_addr = htonl( INADDR-ANY); /* 将套接字绑定到指定的网络地址,对 /* 以下可以报错,进行错误处理。 ,32,3启动服务器监听客户端的连接请求LISTEN() int listen( SOCKET s, int backlog); 仅适用于支持连接的SOCK_STREAM类型的套接口。套接口s处于一种“变动”模式,申请进入的连接请求被确认,并排队等待被接受。 这个函数特别适用于同时有多个连接请求的服务器;如果当一个连接请求到来时,队列已满,那么客户将收到一个WSAECONNREFUSED错误。 4接收连接请

23、求ACCEPT() SOCKET accept( SOCKET s, struct sockaddr* addr, int* addrlen);,33,5请求连接CONNECT() int connect( SOCKET s, struct sockaddr * name, int namelen); 举例 structsockaddr_indaddr; memset(void*),34,35,6向一个已连接的套接口发送数据SEND() int send( SOCKET s, char * buf, int len, int flags);,图3.3 同步套接字的Send()函数的执行流程,3

24、6,7从一个已连接套接口接收数据RECV() int recv( SOCKET s, char * buf, int len, int flags); 图3-4说明了send和recv的作用,套接字缓冲区与应用进程缓冲区的关系,以及协议栈所作的传送。,图3.4 Send()和Recv()都是对本地套接字的操作,8按照指定目的地向数据报套接字发送数据SENDTO() int sendto( SOCKET s, char * buf, int len, int flags, struct sockaddr * to, int tolen); 9接收一个数据报并保存源地址,从数据报套接字接收数据RE

25、CVFORM() int recvfrom( SOCKET s, char * buf, int len, int flags, struct sockaddr* from, int* fromlen);,37,10关闭套接字CLOSESOCKET() int closesocket( SOCKET s); 关闭一个套接口,释放套接口描述字s,以后对s的访问均以WSAENOTSOCK错误返回。 若本次为对套接口的最后一次访问,则相应的名字信息及数据队列都将被释放。 closesocket()的语义受SO_LINGER与SO_DONTLINGER选项影响,对比如下: 选项间隔 关闭方式 等待关闭

26、与否 SO_DONTLINGER 不关心 优雅 否 SO_LINGER 零 强制 否 SO_LINGER 非零 优雅 是,38,11禁止在一个套接口上进行数据的接收与发送SHUTDOWN() int shutdown( SOCKET s, int how); 用于任何类型的套接口禁止接收、禁止发送或禁止收发。 如果how参数为0,则该套接口上的后续接收操作将被禁止。 若how为1,则禁止后续发送操作,对于TCP,将发送FIN。 若how为2,则同时禁止收和发。 请注意shutdown()函数并不关闭套接口,且套接口所占有的资源将被一直保持到closesocket()调用。,39,3.2.4 W

27、insock的辅助函数 1Winsock中的字节顺序转换函数,图3-5 两种本机字节顺序,Winsock API特为此设置了四个函数, (1)htonl() 将主机的无符号长整型数本机顺序转换为网络字节顺序 (Host to Network Long),用于IP地址。 u_long PASCAL FAR htonl( u_long hostlong); hostlong是主机字节顺序表达的32位数。htonl()返回一个网络字节顺序的值。 (2)htons() 将主机的无符号短整型数转换成网络字节顺序(Host to Network Short),用于端口号。 u_short PASCAL F

28、AR htons( u_short hostshort); hostshort:主机字节顺序表达的16位数。htons()返回一个网络字节顺序的值。,41,(3)ntohl() 将一个无符号长整型数从网络字节顺序转换为主机字节顺序。(Network to Host Long),用于IP地址。 u_long PASCAL FAR ntohl( u_long netlong); netlong是一个以网络字节顺序表达的32位数,ntohl()返回一个以主机字节顺序表达的数。 (4)ntohs() 将一个无符号短整型数从网络字节顺序转换为主机字节顺序。(Network to Host Sort),用

29、于端口号 u_short PASCAL FAR ntohs( u_short netshort); netshort是一个以网络字节顺序表达的16位数。ntohs()返回一个以主机字节顺序表达的数。,42,2获取与套接口相连的端地址GETPEERNAME() int getpeername( SOCKET s, struct sockaddr * name, int * namelen); 3获取一个套接口的本地名字GETSOCKNAME() int getsockname( SOCKET s, struct sockaddr * name, int * namelen);,43,4将一个点分

30、十进制形式的IP地址转换成一个长整型数INET_ADDR() unsigned long inet_addr (const char * cp); 5将网络地址转换成点分十进制的字符串格式INET_NTOA() char * inet_ntoa( struct in_addr in);,44,3.2.5 Winsock的信息查询函数 Winsock API提供了一组信息查询函数,让我们能方便地获取套接口所需要的网络地址信息以及其它信息, (1)Gethostname() 用来返回本地计算机的标准主机名。 int gethostname(char* name, int namelen); (2)

31、Gethostbyname() 返回对应于给定主机名的主机信息。 struct hostent* gethostbyname(const char* name);,(3)Gethostbyaddr() 根据一个IP地址取回相应的主机信息。 struct hostent* gethostbyaddr(const char* addr, int len, int type); (4)Getservbyname() 返回对应于给定服务名和协议名的相关服务信息。 struct servent* getservbyname(const char* name, const char* proto); (5

32、)Getservbyport() 返回对应于给定端口号和协议名的相关服务信息。 struct servent * getservbyport(int port,const char *proto);,46,(6)Getprotobyname() 返回对应于给定协议名的相关协议信息。 struct protoent * getprotobyname(const char * name); (7)Getprotobynumber () 返回对应于给定协议号的相关协议信息。 struct protoent * getprotobynumber(int number);,47,除Gethostname

33、()外,其它六个函数有以下共同特点: 函数名都采用GetXbyY的形式。 如果函数成功地执行,就返回一个指向某种结构的指针,该结构包含所需要的信息。 如果函数执行发生错误,就返回一个空指针。应用程序可以立即调用WSAGetLastError()来得到一个特定的错误代码。 函数执行时,可能在本地计算机上查询,也可能通过网络向域名服务器发送请求,来获得所需要的信息,这取决于用户网络的配置方式。 为了能让程序在等待响应时能作其他的事情,Winsock API扩充了一组作用相同的异步查询函数,不会引起进程的阻塞。并且可以使用Windows的消息驱动机制。也是六个函数,与GetXbyY各函数对应,在每个

34、函数名前面加上了WSAAsync前缀,名字采用WSAAsyncGetXByY()的形式。它们的工作机制在后面详述。,48,3.2.6 WSAAsyncGetXByY类型的扩展函数 WSAAsyncGetXByY类型的扩展函数是GetXByY函数的异步版本,这些函数可以很好地利用Windows的消息驱动机制。 Winsock的实现启动WSAAsyncGetXByY()操作后立即返回调用方,并传回一个异步任务句柄,应用程序可以用该句柄标识操作; 当操作完成时,如果有结果将会把结果复制到调用方提供的缓冲区buf中,同时向应用程序的窗口发一条消息; 应用程序窗口hWnd接收到消息wMsg,该消息结构的

35、wParam参数包含了初次函数调用时返回的异步任务句柄;Iprarm参数的高16位包含错误代码。,49,1WSAAsyncGetHostByName()函数 gethostbyname()的异步版本,用于获取对应一个主机名的主机名称和地址信息。 HANDLE WSAAsyncGetHostByName ( HWND hWnd, unsigned int wMsg, const char * name, char * buf, int buflen ); hWnd:异步请求完成时,接收消息的窗口句柄; wMsg:异步请求完成时,将要接收的消息; name:指向主机名的指针; buf:接收host

36、ent数据的数据区指针,需大于hostent结构大小,因为hostent结构引用的数据也都在该区域内。 buflen:数据区的大小。 若操作成功地初启,返回一个HANDLE类型的非0值,作为请求需要的异步任务句柄。 该HANDLE类型的值可通过WSACancelAsyncRequest()用来取消操作,也可通过检查wParam消息参数,以匹配异步操作和完成消息.,50,2WSAAsyncGetHostByAddr()函数 本函数是gethostbyaddr()的异步版本,用来获取对应于一个网络地址的主机名和地址信息. HANDLE WSAAsyncGetHostByAddr ( HWND hW

37、nd, unsigned int wMsg, const char * addr, int len, int type, char * buf, int buflen ); hWnd 异步请求完成时,应该接收消息的窗口句柄. wMsg 异步请求完成时,将要接收的消息. addr 主机网络地址的指针,以网络字节次序存储. len 地址长度.对于PF_INET来说必须为4. type 地址类型,必须是PF_INET. buf 接收hostent数据的数据区指针. buflen 上述数据区的大小. 返回值指出异步操作是否成功地初启. 若操作成功地初启,WSAAsyncGetHostByAddr()返

38、回一个HANDLE类型的非0值,作为请求需要的异步任务句柄. 如果异步操作不能初启,WSAAsyncGetHostByAddr()返回一个0值,并且可使用WSAGetLastError()来获取错误号.,51,3WSAAsyncGetServByName()函数 本函数是getservbyname()的异步版本,用来获取对应于一个服务名的服务信息. HANDLE WSAAsyncGetServByName ( HWND hWnd, unsigned int wMsg, const char * name, const char * proto, char * buf, int buflen )

39、; hWnd 异步请求完成时,应该接收消息的窗口句柄. wMsg 异步请求完成时,将要接收的消息. name 指向服务名的指针. proto 指向协议名称的指针.它可能是NULL,在这种情况下,WSAAsyncGetServByName()将搜索第一个服务入口(满足s_name或s_aliases之一和所给的名字匹配.)否则, WSAAsyncGetServByName()将和名和协议同时匹配. buf 接收hostent数据的数据区指针. buflen 上述数据区的大小. 返回值指出异步操作是否成功地初启.,52,4WSAAsyncGetServByPort() 本函数是getservbyp

40、ort()的异步版本,用来获取对应于一个端口号的服务信息. HANDLE WSAAsyncGetServByPort ( HWND hWnd, unsigned int wMsg, int port, const char * proto, char * buf, int buflen ); hWnd 异步请求完成时,应该接收消息的窗口句柄. wMsg 异步请求完成时,将要接收的消息. port 服务的接口.以网络字节序. proto 指向协议名称的指针.它可能是NULL,在这种情况下,WSAAsyncGetServByName()将搜索第一个服务入口(满足s_name或s_aliases之一和所给的名字匹配.)否则, WSAAsyncGetServByName()将和名和协议同时匹配. buf 接收hostent数据的数据区指针. buflen 上述数据区的大小. 返回值指出异步操作是否成功地初启.,53,5WSAAsyncGetProtoByName()函数 本函数是getprotobyname()的异步版本,用来获取对应于一个协议名的协议名称和代号. HAN

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论