发信人: richwxy(风吟)
整理人: wenbobo(2002-05-17 17:59:41), 站内信件
|
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 */
第四章 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())也会
立刻
完成。对于这些操作,阻塞方式和非阻塞方式并没有什么两样。其他一些操作(例如re
cv())可能立刻完成,也可能需要阻塞一段随机的时间才能完成。这都取决于不同的传
输情况。当用于阻塞套接口时,这些操作被认为是工作于阻塞方式的,所有会阻塞的例
程在以前或以后的列表中都打上了星号作标记。
在Windows Sockets实现中,一个无法立刻完成的阻塞操作是按如下方式处理的。D
LL先
初始化操作,然后进入一个循环,在循环中发送收到的任何信息-为了使在必要时把处
理器交给其他线程,然后检查Windows Sockets功能是否完成。如果功能完成了,或者W
SACancelBlockingCall()被唤起,阻塞操作以一个适当的返回值结束。完整的关于这种
机制的描述,请参见5.3.13节,WSASetBlockingHook(),这一部分还包括了对于各种函
数伪代码的讨论。
如果一个正在运行某一阻塞操作的进程收到了一个Windows消息,那么应用程序有
可能
试图发出另一个Windows Sockets调用,由于很难安全地处理这种情形,Windows Socke
ts规范不支持这种应用程序的工作方式。在这种情况下,有两个函数可以帮助程序员。
WSAIsBlocking()可以用来确定在该进程上是否有阻塞的Windows Sockets调用。WSACan
celBlookingCall()可以用来取消在线的阻塞调用,如果有的话。任何其他的Windows S
ockets函数如果在这种情况下被调用,则会失败并返回错误代码WSAEINPROGRESS。要强
调的是,这一限制适用于所有阻塞和非阻塞的操作。
虽然这种机制对于简单的应用程序已经足够了,但这不能支持高级应用程序的复杂
的消
息发送要求。(例如,那些MDI模型的用户)对于这样的应用程序,Windows Sockets A
PI设计了WSASetBlockingHook()函数,这个函数可以允许程序员定义特殊的阻塞钩子来
代替上面讨论的缺省消息发送例程。
只有在以下都为真时,Windows Sockets DLL才调用阻塞钩子函数:例程是被定义
为可
以阻塞的,指定的套接口也是阻塞套接口,而且请求不能被立刻完成。(套接口是被缺
省地设为阻塞方式的,但IOCTL FIONBIO和WSAAsyncSelect()都可以把套接口设置成为非
阻塞模式)。如果应用程序只使用非阻塞方式的套接口,而且使用WSAAsyncSelect()和
/或WSAAsyncGetXByY()例程,而不是使用select()和/或getXbyY()例程,那么阻塞钩子
函数就永远也不会被调用,应用程序也不用再操心由于阻塞钩子函数而带来的重入问题
。
如果一个应用程序在唤起异步或非阻塞方式调用时使用了一个内存对象的指针(例
如一
个缓冲区,或者一个全程变量)作为参数,那么应用程序要保证那个对象在Windows So
ckets实现的整个操作中都可得到并使用。应用程序不能再唤起可能影响到内存唤射或寻
址能力的其他的Windows函数。在多线程系统中,应用程序也有责任使用某种同步机制来
协调对内存对象的存取。Windows Sockets实现不能,也不会提出这种事情。没有遵守这
条规则,所可能产生的后果已不在规范讨论的范围之内。
4.2 数据库函数
Windows Sockets规范定义了如下数据库例程。正如我们先前提出的,Windows Soc
ket
s提供者有可能不采用依赖于本地数据库的方式来实现这些函数。某些数据库例程返回的
指针(例如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()和WSACle
anup()除外)但我们推荐应用程序开发者遵循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实现以这样一种方式阻塞一个操作,Wind
ows
消息处理可以继续,发出调用的应用程序仍然可以收到Windows消息。但在某些情况下,
应用程序可能希望影响或改变这种伪阻塞的实现方式。WSASetBlockingHook()函数就提
供了这样一种功能。它使得应用程序可以替换Windows Sockets实现在"阻塞"操作中放弃
处理器时调用的例程。 |
|