16.3 使用WinSocket API
16.2节介绍了套接字库函数,WinSocket API函数为开发套接字函数提供了整套处理函数。用户在调用WinSocket API函数时,要注意函数之间的关联关系。本节介绍使用WinSocket API进行套接字编程(www.cppentry.com)的几个基本问题。
16.3.1 基本Socket系统调用
基本套接字系统调用主要分为套接字绑定、套接字监听、套接字连接、套接字接收、数据发送、数据接收、断开套接字这几部分的调用。
套接字绑定使用WSPBind()函数绑定到指定的地址,其函数原型为:
- int WSPBind ( SOCKET s, // 指定要
绑定的套接字 - const struct sockaddr FAR * name, // 指定
套接字要绑定的地址,指向sockaddr // 结构的指针 - int namelen, // 指定地址参数的长度
- LPINT lpErrno ); // 返回
操作执行的错误代码
其中,name参数是指向sockaddr结构的指针,此结构定义如下:
- sockaddr { _short sa_family; // 指定地址所属的范围
- char sa_data[14]; }; // 指定地址值
此函数用在无连接套接字或面向连接套接字的连接或监听前。当使用WSPSocket()函数创建套接字后,在命名空间中存在,但是没有为其分配地址,此函数就是建立套接字和本地地址的关联关系。
对于面向连接的套接字在绑定套接字后,就可以调用WSPListen()函数启动监听,用于接收客户端套接字的连接。其函数原型为:
- int WSPListen ( SOCKET s, // 指定要监听的套接字
- int backlog, // 指定服务器可以接收
的客户端套接字的个数,最大值为SOMAXCONN - LPINT lpErrno ); // 返回操作执行的错误代码
客户端套接字要连接到服务器套接字,则需要调用WSPConnect()函数,此函数可以建立到对端套接字的连接,用于交换数据,并指定数据交换的服务质量。其函数原型为:
- int WSPConnect ( SOCKET s, // 指定要执行连接的套接字
- const struct sockaddr FAR * name, // 指定要连接的套接字的地址
- int namelen, // 指定要连接的套接字地址的长度
- LPWSABUF lpCallerData, // 指定在建立
连接期间要传输的用户数据的指针 - LPWSABUF lpCalleeData, // 指定在建立连
接期间要接收的用户数据的指针 - LPQOS lpSQOS, // 指向套接字流控制的指针
- LPQOS lpGQOS, // 预留
- LPINT lpErrno ); // 返回操作执行的错误代码
服务器接收到客户端连接请求后,可以调用WSPAccept()函数有条件的接收套接字连接,并返回创建的与客户端相连的套接字。其函数原型为:
- SOCKET WSPAccept ( SOCKET s, // 指定要接收的客户端套接字
- struct sockaddr FAR * addr, // 指定存放
接收套接字的地址的指针 - LPINT addrlen, // 指定存
放接收套接字的地址的长度 - LPCONDITIONPROC lpfnCondition,
- // 指定进行是否接收套接字的判断条件的执行函数
- DWORD dwCallbackData,
- // 指定进行是否接收套接字的判断条件的执行函数的参数
- LPINT lpErrno );
其中条件判断回调函数的原型为:
- int CALLBACK ConditionFunc ( IN LPWSABUF
lpCallerId, IN LPWSABUF lpCaller Data, - IN OUT LPQOS lpSQOS, IN OUT LPQOS lpGQOS,
IN LPWSABUF lpCalleeId, - IN LPWSABUF lpCalleeData, OUT GROUP FAR *
g, IN DWORD dwCallbackData );
其中,lpCallerId参数和lpCallerData参数是包含连接地址和用户数据的参数。用户可以通过发送连接请求的地址和用户数据进行身份验证,从而确定是否接收连接请求。
连接建立完成后,就可以使用WSPSend()函数在面向连接套接字上发送数据了。其函数原型为:
- int WSPSend ( SOCKET s, // 表示发送数据的socket句柄
- // 指向WSABUF结构的数组指针,每个WSABUF结构
包含指向缓冲区的指针和缓冲区的长度 - LPWSABUF lpBuffers,
- DWORD dwBufferCount, // 指定lpBuffers
数组中的WSABUF结构的个数 - LPDWORD lpNumberOfBytesSent, // 返回此函数发送的数据个数
- DWORD dwFlags, // 指定发送函数的选项
- LPWSAOVERLAPPED lpOverlapped, // 指向WSAOVERLAPPED结构的指针
- // 指向发送操作执行完毕后执行的处理函数的指针
- LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
- LPWSATHREADID lpThreadId, // 指向WSATHREADID结构的指针
- LPINT lpErrno ); // 指向返回错误代码的指针
除了发送数据,还可以使用WSPRecv()函数接受来自套接字的数据。其函数原型为:
- int WSPRecv ( SOCKET s, // 表示接收数据的socket句柄
- LPWSABUF lpBuffers, // 指向WSABUF结构的数组的指针
- DWORD dwBufferCount, // 指定lpBuffers
数组中的WSABUF结构的个数 - LPDWORD lpNumberOfBytesRecvd, // 返回此函数接收数据的个数
- LPDWORD lpFlags, // 指定并返回接收函数的选项
- LPWSAOVERLAPPED lpOverlapped, // 指向WSAOVERLAPPED结构的指针
- // 指向接收操作执行完毕后执行的处理函数的指针
- LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
- // 指向WSATHREADID结构的指针,由提供程序在后续的WPUQueueApc调用中使用
- LPWSATHREADID lpThreadId,
- LPINT lpErrno); // 指向返回错误代码的指针
使用完套接字后,还需要调用断开套接字连接,此时使用WSPShutdown()函数可以关闭在套接字上的发送和接收操作。
- int WSPShutdown ( SOCKET s, // 表示要关闭的socket句柄
- int how, // 指定要禁止执行的操作类型,可以为SD_
RECEIVE或SD_SEND或SD_BOTH, // 分别表
示禁止接收数据、禁止发送数据和禁止收发数据 - LPINT lpErrno); // 指向返回错误代码的指针
调用完此函数后,套接字句柄还没有释放,还需要调用WSPCloseSocket()函数释放套接字句柄。
上面这几个函数是基本套接字系统调用的函数,要开发出各种不同需求的通信程序,则需要根据情况,使用各种Windows Socket()函数,由于篇幅原因,这里不再赘述。