Windows Sockets 网络编程——第十章 支持例程

来源:互联网 发布:时间碎片软件 编辑:程序博客网 时间:2024/05/22 01:58

第十章 支持例程

10.1 启动与清除

每一个WinSock应用程序必须在开始操作前初始化WinSock的动态链接库(DLL),并在操作完成后通知DLL进行清除操作。WinSock应用程序在开始时必须调用WSAStartup()函数,结束时必须调用WSACleanup()函数。可以多次调用这两个函数,但是必须保证它们是成对出现的。
在系统内部,WSAStartup()函数从0开始增加引用计数量,而WSACleanup()函数减少引用计数量。当引用计数量减为0时,WinSock DLL才释放资源。

10.1.1 WSAStartup()

WSAStartup()函数必须是WinSock应用程序使用的第一个WinSock函数。否则,其他的WinSock API函数都将会失败并返回错误值WSAENOTNITIALISED。WinSock DLL使用这个函数来注册调用任务并分配必要的资源。
下面是WSAStartup()函数的原型:

int WSAAPI WSAStartup(    WORD wVersionRequired,    //hightest version of WinSock API     LPWSADATA lpWSAData       //ptr to struct to receive details on Windows Sockets implementation);

wVersionRequired:应用程序要求的主要和次要的版本号。LSB是主要的版本号,MSB是次要的版本号。
lpWSAData:指向WinSock应用程序提供的缓存区,缓存区中填入的内容为有关WinSock实现的特有信息。
这个函数与其它很多函数有所不同,它在成功时返回0值,返回错误值表示失败。函数的每次成功调用,都将会使引用计数增加1.

typedef struct WSAData {        WORD                    wVersion;      //version app expected to use        WORD                    wHighVersion;  //hightest version available#ifdef _WIN64        unsigned short          iMaxSockets;   //max sockets available        unsigned short          iMaxUdpDg;     // max datagram size        char FAR *              lpVendorInfo;  //no format        char                    szDescription[WSADESCRIPTION_LEN+1];//no format        char                    szSystemStatus[WSASYS_STATUS_LEN+1];//no format#else        char                    szDescription[WSADESCRIPTION_LEN+1];//no format        char                    szSystemStatus[WSASYS_STATUS_LEN+1];//no format        unsigned short          iMaxSockets;   //max sockets available        unsigned short          iMaxUdpDg;     // max datagram size        char FAR *              lpVendorInfo;  //no format#endif} WSADATA, FAR * LPWSADATA;

应用程序所请求的版本号不能低于WinSock DLL所支持的最低版本号,但可以高于WinSock DLL所支持的最高版本号。例如,WinSock DLL最高支持2.2的版本,我们可以请求2.2以上的版本3.0。最低支持1.1版本,这时我们请求1.0版本的将会请求失败。

10.1.2 WSACleanup()

每一个WSAStartup()函数的成功调用,都要有一个与之相匹配的WSACleanup()函数调用。WSACleanup()函数的成功调用都将使引用计数减1.

int WSAAPI WSACleanup( void );// 0 on success, or SOCKET_ERROR

WSACleanup()函数调用总是立即返回。若执行成功,WSACleanup()会返回0值。若执行失败,WSACleanup()会返回SOCKET_ERROR。

10.2 字节顺序

主机字节顺序采用的是小尾端在前。网络字节顺序采用的是大尾端在前。通常情况下,WinSock函数的任何多字节输入参数和WinSock结构的多字节域值都应该采用网络字节顺序。协议号、getprotobynumber()函数或WSAAsyncGetProtoByNumber()函数的输入参数例外。
字节顺序函数

  • htons()函数将16位短整型数据从主机顺序转为网络顺序,ntohs()函数将16位短整型数据从网络顺序转为主机顺序。
  • htonl()函数将32位长整型数据从主机顺序转为网络顺序,htohl()函数将32位短整型数据从网络顺序转为主机顺序.
u_short WSAAPI htons( u_short hostshort );u_short WSAAPI ntohs( u_short netshort );u_long WSAAPI ntohl( u_long netlong );u_long WSAAPI htonl( u_long hostlong );

这些函数调用不会失败,它们返回与输入参数值大小相同的值,但是它们并不总是执行重排序操作。如果当前系统的原生字节顺序是大尾端,那么上面的这些函数将不会执行任何操作。

10.3 服务名称与端口号

每一个服务都有一个端口号,有很多服务还有名称。WinSock API有4个服务解析函数:getservbyname()、WSAAsyncGetServByName()、getservbyport()和WSAAsyncGetServByPort()。

10.3.1 servent 结构

所有的服务解析函数都会在servent结构中返回它们的结果。

struct  servent {        char    FAR * s_name;           /* official service name */        char    FAR * FAR * s_aliases;  /* alias list */#ifdef _WIN64        char    FAR * s_proto;          /* protocol to use */        short   s_port;                 /* port # */#else        short   s_port;                 /* port # */        char    FAR * s_proto;          /* protocol to use */#endif};typedef struct servent SERVENT;typedef struct servent *PSERVENT;typedef struct servent FAR *LPSERVENT;

s_name:服务的公开名称。通常服务的名称有小写字母构成。
s_aliases:以NULL结束的别名字符串数组(即可替换的服务名称),它以空指针结束。
s_port:访问服务的端口号。
s_proto:以空字符结束的协议名称字符串,与用户要求的名称效用。

10.3.2 服务解析

getservbyname(),阻塞至服务名称解析操作完成。这个函数通常反问本地的磁盘文件进行解析,所以返回结果的速度很快。
WSAAsyncGetServByName(),立即返回,在服务名称解析操作完成后异步地通知应用程序。

  1. getservbyname()
struct servent FAR * WSAAPI getservbyname(        const char FAR * name,    //pointer to service name        const char FAR * proto    //pointer to protocol name or NULL to return first match of service name);

getservbyname()函数返回一个指向servent结构的指针。调用getservbyname()函数后,在其他的WinSock函数调用改变servent结构内容前,需要将servent的内容读取进行保存。一定不能试图释放或者改变结构的内容,否则会出现保护故障。
2.WSAAsyncGetServByName()

HANDLE WSAAPI WSAAsyncGetServByName(    HWND hWnd,             // handle of window to rcv msg on completion    u_int wMsg,            // message to be rcvd on completion    const char FAR * name, // ptr to service name to be resolved    const char FAR * proto,// ptr to protocol name (may be NULL)    char FAR * buf,        // ptr to data area to rcv servent data    int buflen             // size of data area buf above);

WSAAsyncGetServByName() 函数如果操作成功,将返回一个非0异步查询句柄,当操作完成后,可以使用这个句柄来标识应答结果。

10.3.3 端口解析

  1. getservbyport()
struct servent FAR * WSAAPI getservbyport(    int port,              // port number, in network byte order    const char FAR * proto // pointer to protocol name or NULL to return first match of service port);

此函数一直阻塞至端口号解析操作完成。其返回值是一个指向系统缓存区的指针。操作失败,返回值为空指针。
2. WSAAsyncGetServByPort()

HANDLE WSAAPI WSAAsyncGetServByPort(    HWND hWnd,       // handle to window to rcv msg on completion    u_int wMsg,      // message to be rcvd on completion    int port,        // port number to be resolved (net byte order)            const char FAR * proto,// ptr to protocol name (may be NULL)    char FAR * buf,  // ptr to data area to rcv servent data    int buflen       // size of data area buf above);

WSAAsyncGetServByPort()函数与WSAAsyncGetServByName()函数相比,除了使用端口号替换服务名称和别名,其他内容都相同。这两个函数在同种情况下返回结果相同。

10.4 协议名称与协议号

每一个协议都有一个协议号。这些号码在Winsock[2].h中定义。协议解析与服务和主机解析类似,但很少使用协议解析,因为协议值不能变化。

10.4.1 protoent结构

所有的协议解析函数都将返回值放入protoent结构中。

struct  protoent {        char    FAR * p_name;           /* official protocol name */        char    FAR * FAR * p_aliases;  /* alias list */        short   p_proto;                /* protocol number */};typedef struct protoent PROTOENT;typedef struct protoent *PPROTOENT;typedef struct protoent FAR *LPPROTOENT;

p_name:协议的正式名称,通常有小写字母构成。
p_aliases:一空字符结束的别名字符串数组,它以空指针结束。通常,用大写字符的协议名称是别名。
p_proto:以主机字节顺序描述的协议号。

10.4.2 协议名称解析

协议名称解析函数getprotobyname()和WSAAsyncGetProtoByName(),前者是阻塞模式,后者以异步方式进行。由于协议值不会像端口号那样发生改变,所以,这两个函数实际上是很少被使用到的。
1. getprotobyname()

struct protoent FAR * WSAAPI getprotobyname(    const char FAR * name    // pointer to protocol name);

name:以空字符结束的字符串,它包含有需要解析的协议名称和别名。
getprotobyname()函数操作失败返回0值(空指针)。可以通过WSAGetLastError()函数得到错误值,从而找到失败的原因。如果操作成功,函数将返回一个指针,指向系统缓存中protoent结构。在获得返回值后,应该立即引用protoent结构中的内容。
2. WSAAsyncGetProtoByName()

HANDLE WSAAPI WSAAsyncGetProtoByName(    HWND hWnd,       // handle to window to rcv msg on completion    u_int wMsg,      // message to be rcvd on completion          const char FAR * name,// ptr to protocol name to be resolved    char FAR * buf,  // ptr to data area to rcv protoent data    int buflen       // size of data area buf above);

WSAAsyncGetProtoByName()总是在协议名称解析操作完成前立即返回。如果操作成功,WSAAsyncGetProtoByName()函数将返回一个非0异步查询句柄。

10.4.3 协议号解析

getprotobynumber()与WSAAsyncGetProtoByNumber()函数都用协议号作为输入参数,并将正确的值填入协议的结构中。

struct protoent FAR * WSAAPI getprotobynumber(    int number    // protocol number, in host byte order);HANDLE WSAAPI WSAAsyncGetProtoByNumber(    HWND hWnd,       // handle to window to rcv msg on completion    u_int wMsg,      // message to be rcvd on completion          const char FAR * name,// ptr to protocol name to be resolved    char FAR * buf,  // ptr to data area to rcv protoent data    int buflen       // size of data area buf above);

number:协议号值与IP报头中协议字段的标准值一致。

0 0
原创粉丝点击