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

本文详细介绍了Windows Sockets(WinSock)编程中的支持例程,包括WSAStartup()和WSACleanup()的使用,字节顺序转换函数htons(), htonl()等,以及服务名称、端口号和协议解析的相关函数。强调了初始化WinSock的重要性以及服务和协议解析在实际应用中的作用。" 44001817,4933649,Objective-C的@property与点语法解析,"['ios开发', 'objective-c', '类', '属性']

第十章 支持例程

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报头中协议字段的标准值一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值