一,什么是SOCKET
SOCKET即套接字,用于描述地址和端口,是一个通信链的句柄。应用程序通过套接字向网络发出请求或者回应。
sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报(数据报)套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);前两种较常用。基于TCP的套接字编程是采用的流式套接字。
(1)SOCK_STREAM表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常用的HTTP协议就使用SOCK_STREAM传输数据,因为要确保数据的正确性,否则网页不能正常解析。
(2)SOCK_DGRAM表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为SOCK_DGRAM所做的校验工作少,所以效率比SOCK_STREAM高。
QQ视频聊天和语音聊天就使用SOCK_DGRAM传输数据,因为首先要保证通信的效率,尽量减小延迟,而数据的正确性是次要的,即使丢失很小的一部分数据,视频和音频也可以正常解析,最多出现噪点或杂音,不会对通信质量有实质的影响。
注意:SOCK_DGRAM没有想象中的糟糕,不会频繁的丢失数据,数据错读只是小概率事件。
有可能多种协议使用同一种数据传输方式,所以在socket编程中,需要同时指明数据传输方式和协议。
二,客户端/服务端模式:
在TCP / IP网络应用中,通信的两个进程相互作用的主要模式是客户端/服务器模式,即客户端向服务器发出请求,服务器接收请求后,提供相应的服务。客户端/服务器模式的建立基于以下两点:
(1)建立网络的起因是网络中软硬件资源,运算能力和信息不均等,需要共享,从而就让拥有众多资源的主机提供服务,资源较少的客户请求服务这一非对等作用。
(2)网络进程通信完全是异步的,相互通信的进程间既不存在父子关系,又不共享内存缓冲区。因此需要一种机制为希望通信的进程间建立联系,为二者的数据交换提供同步,这就是基于客户/服务端模式的TCP / IP。
服务端:建立SOCKET,声明自身的端口号和地址并绑定到SOCKET,使用LISTEN打开监听,然后不断用ACCEPT去查看是否有连接,如果有,捕获SOCKET并通过recv获取消息内容,通信完成后调用关闭套接字关闭这个对应接收到的SOCKET,如果不再需要等待任何客户端连接,那么关闭自身的SOCKET。
客户端:建立SOCKET,通过端口号和地址确定目标服务器,使用CONNECT连接到服务器,send发送消息,等待处理,通信完成后关闭套接字。
三,编程步骤
(1)服务端
1,加载套接字库,创建套接字 (调用WSAStartup() /SOCEKT());
2,绑定套接字到一个IP地址和一个端口上 (bind());
3,将套接字设置为监听模式等待连接请求 (listen());
4,请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字 (accept());
5,用返回的套接字和客户端进行通信 (send()/recv());
6,返回,等待另一个连接请求;
7,关闭套接字,关闭加载的套接字库 (closesocket()/ WSACleanup());
(2)客户端
1,加载套接字库,创建套接字 (调用WSAStarup()/SOCKET());
2,向服务器发出连接请求 (connect());
3,和服务器进行通信 (send()/recv());
如图4所示,关闭套接字,关闭加载的套接字库 (closesocket()/WSACleanup());
四,相关函数与结构
进行Socket编程时,先要进行链接库WS2_32的初始化操作,也就是加上#pragma comment(lib,“Ws2_32.lib”),还有相关头文件#include <WinSock2.h>,但是当还还需要添加<WINDOWS.H>的头文件时,应注意俩者的顺序,应该将<WINDOWS.H>头文件放在<WinSock2.h>头文件的下面,避免出现一些重复定义的错误
(1) int WSAAPI WSAStartup (WORD wVersionRequested,LPWSADATA lpWSAData );
作用:初始化socket库
参数1: TBD,通常使用 MAKEWORD(2,2),里面的参数可以更改,代表的意思是请求2.2版本的WinSock库
参数2:指向 WSADATA数据结构的指针,用于接收Windows套接字实现的详细信息
返回值:成功则返回 0,失败则返回错误码
(2)WSADATA数据结构
typedef struct WSAData
{
WORD wVersion; //Ws2_32.dll期望调用者使用的Windows套接字规范的版本,高位字节指定次要版本号以及低位字节指定主要版本号
WORD wHighVersion; //Ws2_32.dll可以支持的最高版本的Windows套接字规范
unsigned short iMaxSockets; //可以打开的最大套接字数
unsigned short iMaxUdpDg; //最大数据报消息大小
char *lpVendorInfo; //指向供应商特定信息的指针。对于Windows套接字版本2及更高版本,应忽略此成员
char szDescription[WSADESCRIPTION_LEN + 1];
char szSystemStatus[WSASYS_STATUS_LEN + 1];
} WSADATA;
相关应用:检测请求版本号是否正确
//初始化socket库
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) //请求2.2版本的WinSock库,成功则返回0
{
return 0; //失败则退出
}
//检测请求版本号是否正确,通常可以不加
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup(); //不正确则清除信息
return 0;
}
(3) SOCKET socket(
int af,
int type,int protocol);
作用:创建socket操作,建立流式套接字
参数1:指定地址簇(TCP / IP只能是AF_INET,也可写成PF_INET)
参数2:套接字的类型 SOCK_STREAM,SOCK_DGRAM
参数3:特定地址家族相关协议(0为自动)
返回值:返回套接字号sockSrv
(4) SOCKET WSASocket(
int af,
int type,
int protocol,
LPWSAPROTOCOL_INFO lpProtocolInfo,
GROUP g,
DWORD dwFlags);
参数1:地址族规范
AF_UNSPEC(地址系列未指定)
AF_INET (Internet协议版本4(IPv4)地址系列)
AF_INET6 (Internet协议版本6(IPv6)地址系列)
.......
参数2:套接字的类型 SOCK_RAW,SOCK_STREAM(TCP),SOCK_DGRAM(UDP)......
参数3:要使用的协议。协议参数的可能选项特定于指定的地址族和套接字类型,如果指定值0,则调用者不希望指定协议,服务提供者将选择要使用的协议
IPPROTO_TCP
IPPROTO_UDP
......
参数4:指向WSAPROTOCOL_INFO结构的指针,该结构定义要创建的套接字的特征。如果此参数不为NULL,则套接字将绑定到与指示的WSAPROTOCOL_INFO结构关联的提供程序
参数5:现有套接字组ID或在创建新套接字和新套接字组时要采取的相应操作。如果克是现有套接字组ID,请将新套接字加入此套接字组,前提是满足此组设置的所有要求。如果克不是现有套接字组ID,则可以使用以下值,通常用0作为参数
0 没有执行组操作
SG_UNCONSTRAINED_GROUP 创建一个无约束的套接字组
SG_CONSTRAINED_GROUP 创建受约束的套接字组
参数6:用于指定其他套接字属性的标志,可以设置这些标志的组合,但是不允许某些组合
WSA_FLAG_OVERLAPPED 创建一个支持重叠I / O操作的套接字
WSA_FLAG_MULTIPOINT_C_ROOT 创建一个在多点会话中将成为c_root的套接字
WSA_FLAG_MULTIPOINT_C_LEAF 创建一个在多点会话中将成为c_leaf的套接字
WSA_FLAG_MULTIPOINT_D_ROOT 创建一个在多点会话中将成为d_root的套接字
WSA_FLAG_MULTIPOINT_D_LEAF 创建一个在多点会话中将成为d_leaf的套接字
WSA_FLAG_ACCESS_SYSTEM_SECURITY 创建一个套接字,允许在包含安全访问控制列表(SACL)的套接字上设置安全描述符,而不仅仅是自主访问控制列表(DACL)
WSA_FLAG_NO_HANDLE_INHERIT 创建一个不可继承的套接字
返回值:若无错误发生,WSASocket()返回新套接口的描述字。否则的话,返回INVALID_SOCKET,应用程序可以调用WSAGetLastError()来获取相应的错误代码
错误代码:
WSANOTINITIALISED在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN网络子系统失效.WSAINFNOSUPPORT
不支持指定的地址族
.WSAEINPROGRESS一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数
WSAEMFILE无可用的套接口描述字。
WSAENOBUFS无可用的缓冲区空间。套接口无法创建。
WSAEPROTONOSUPPORT不支持指定的协议。
WSAEPROTOTYPE指定的协议对于本套接口类型错误。
WSAESOCKTNOSUPPORT本地址族不支持指定的套接口类型
.WSAEINVAL g参数非法。
Socket与WSAsocket俩者区别:
WSAsocket支持异步操作,Socket只能同步操作,socket()函数创建一个通讯端点并返回一个套接口,但是在socket库中例程在应用于阻塞套接口时会阻塞。WSAsocket()的发送操作和接收操作都可以被重叠使用。接收函数可以被多次调用,发出接收缓冲区,准备接收到来的数据。发送函数也可以被多次调用,组成一个发送缓冲区队列。可是socket()却只能发过之后等待回消息才可做下一步操作
(5) int WSAAPI bind(
SOCKET s,
const struct sockaddr * name,
int namelen);
作用:套接字sockSrv本地与地址相连
参数1:指定需要绑定的套接字
参数2:指定该套接字的本地地址信息,地址该结构会随所用的网络协议的不同而不同
参数3:地址的长度
返回值:如果没有错误发生,bind()返回0。否则,它返回SOCKET_ERROR,并且可以通过调用WSAGetLastError来检索特定的错误代码
(6)套接字地址信息结构体 SOCKADDR_IN
struct sockaddr_in
{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero [8];
};
作用:作为绑定等函数的传入参数:
参数1:地址族,对于IP地址,sin_family成员将一直是AF_INET。
参数2:将要分配给套接字的端口。
参数3:套接字的主机IP地址,将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据,如果想只让套接字使用多个IP中的一个地址,可用inet_addr()函数指定实际地址。
参数4:给出填充数,让SOCKADDR_IN与SOCKADDR结构的长度一样。
(7)int WSAAPI listen (
SOCKET s,
int backlog );
作用:监听传入连接状态
参数1:绑定后的未连接的套接字
参数2:挂起连接队列的最大长度。如果设置为 SOMAXCONN,则负责套接字的底层服务提供程序会将积压设置为最大合理值。如果设置为 SOMAXCONN_HINT(N)(其中N是数字),则积压值将为N,调整为在范围(200,65535)内。请注意, SOMAXCONN_HINT可用于将积压设置为比SOMAXCONN更大的值
返回值:没有错误发生则返回0,否则返回SOCKET_ERROR
(8) SOCKET WSAAPI accept(
SOCKET s,
sockaddr * addr,
int * addrlen );
参数1:处于监听状态下的套接字
参数2:指向缓冲区的可选指针,该缓冲区接收连接实体的地址(与客户端连接的地址)
参数3:指向整数的可选指针,该整数包含地址参数指向的结构长度
返回值:如果没有错误发生,则返回一个新套接字(通信套接字),此返回值是进行实际连接的套接字的句柄。否则,返回值INVALID_SOCKET
(9)SOCKET WSAAPI WSAAccept(
SOCKET s,
sockaddr * addr,
LPINT addrlen,
LPCONDITIONPROC lpfnCondition,
DWORD_PTR dwCallbackData);
参数1:处于监听状态下的套接字
参数2:指向sockaddr结构的可选指针
参数3:指向整数的可选指针,该整数包含addr参数指向的sockaddr结构的长度(以字节为单位)
参数4:可选的应用程序指定的条件函数的地址,它将根据作为参数传入的调用者信息做出接受/拒绝决定,并可选择通过为此函数的结果参数分配适当的值来创建或加入套接字组,如果此参数为NULL,则不调用任何条件函数
参数5:作为传递给条件函数的参数,仅当lpfnCondition参数不为NULL时,此参数才适用
(10) int WSAAPI connect (
SOCKET s,
const sockaddr * name,
int namelen );
参数1:未连接的套接字
参数2:指向应建立连接的sockaddr结构的指针
参数3:name参数指向的sockaddr结构的长度(以字节为单位)
返回值:如果没有错误发生,则 connect返回0。否则,它返回SOCKET_ERROR
(11) int WSAAPI send (SOCKET s,
const char * buf,
int len,
int flags );
参数1:连接的套接字
参数2:指向包含要传输的数据的缓冲区的指针
参数3: buf参数指向的缓冲区中数据的长度(以字节为单位)
参数4:一组标志,指定进行调用的方式,通常用0,可以用OR运算符连接
MSG_DONTROUTE 指定数据不应受路由限制
MSG_OOB 发送OOB数据(仅限SOCK_STREAM等流式套接字)
返回值:如果没有错误发生, send返回发送的总字节数,该字节数可能小于len参数中请求发送的数字。否则,返回值SOCKET_ERROR
(12) INT WSAAPI WSASend (
SOCKET S,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD dwFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
参数1:连接套接字的描述符
参数2:指向 WSABUF结构数组的指针
参数3: pBuffers 数组中的 WSABUF结构数
参数4:发送字节数,函数运行后将设置该值
参数5:标志位,同send()
参数6:指向 WSAOVERLAPPED结构的指针
参数7:发送操作完成时调用的指向完成
返回值:没有发生错误并且发送操作立即完成,则 WSASend返回零,否则,返回值SOCKET_ERROR
(13)WSABUF结构( Winsock的功能中使用的数据结构)
typedef struct _WSABUF {
ULONG len; // 缓冲区的长度,以字节为单位
CHAR * buf; //指向缓冲区的指针
} WSABUF,* LPWSABUF ;
(14)int sendto (
SOCKET s,
const char * buf,
int len,
int flags,
const sockaddr * to,
int tolen );
参数1:发送端的套接字描述符
参数2:指向包含要传输的数据的缓冲区的指针
参数3: buf参数指向的数据的长度(以字节为单位)
参数4:一组标志,指定进行调用的方式,通常默认为0
参数5:指向sockaddr结构的可选指针,该结构包含目标套接字的地址,向谁发送就添加谁的地址
参数6:到参数指向的地址的大小(以字节为单位)
返回值:没有错误的情况下,sendto将返回发送的总字节数,该字节数可能小于len指示的数字,否则,返回值SOCKET_ERROR
(15) int WSAAPI recv (
SOCKET s,
char * buf,
int len,
int flags );
参数1:已连接套接字的描述符
参数2:指向缓冲区的指针,用于接收传入数据
参数3: buf参数指向的缓冲区的长度(以字节为单位)
参数4:影响此函数行为的标志,通常用0
MSG_PEEK 窥视传入的数据
MSG_OOB 处理带外(OOB)数据
MSG_WAITALL
返回值:没有错误发生, recv返回接收的字节数,buf参数指向的缓冲区将包含接收到的数据。如果已正常关闭连接,则返回值为零。否则,返回值SOCKET_ERROR
(16) INT WSAAPI WSARecv(
SOCKET S,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
参数1:连接套接字的描述符
参数2:指向WSABUF结构数组的指针
参数3:lpBuffers 数组中的WSABUF结构数,设置为1即可
参数4:接收的数据的数字(以字节为单位)
参数5:指向用于修改WSARecv函数调用行为的标志的指针
参数6:指向WSAOVERLAPPED结构的指针(对于非重叠套接字,将被忽略)
参数7:指向完成接收操作完成时调用的完成例程的指针(对于非重叠套)接字,将被忽略)
返回值:如果没有发生错误并且接收操作立即完成,则WSARecv返回0,在这种情况下一旦调用线程处于可警告状态,就已经调度完成例程,否则返回值为SOCKET_ERROR
(17) int recvfrom (
SOCKET s,
char * buf,
int len,
int flags,
sockaddr * from,
int * fromlen );
参数1:接收端套接字描述符
参数2:传入数据的缓冲区
参数3: buf参数指向的缓冲区的长度(以字节为单位)
参数4:一组选项,用于修改函数调用的行为,通常使用默认值 0
参数5:指向 sockaddr的结构中缓冲区的可选指针,该结构将在返回时保存源,到接收地址的
参数6:指向从参数指向的缓冲区大小(以字节为单位)的可选指针
返回值:如果没有错误发生, recvfrom的返回接收的字节数如果已正常关闭连接,则返回值为零否则,返回值。SOCKET_ERROR
(18) in_addr:IPv4互联网地址结构体
struct in_addr
{
union
{
struct
{
u_char s_b1;
u_char s_b2;
u_char s_b3;
u_char s_b4;
} S_un_b;
struct
{
u_short s_w1;
u_short s_w2;
} S_un_w;
u_long S_addr;
} S_un;
};
(19) unsigned long WSAAPI inet_addr (const char* cp );
作用:将一个点分十进制的IP转换成一个长整数型数(u_long类型),即转化为一个in_addr结构中的 S_un结构中的 S_addr字段类型,也可以叫将网络主机地址(如192.168.1.10)转化为为网络字节序二进制值
示例:sock.sin_addr.S_un.S_addr = inet_addr(“192.168.1.111”);
返回值:如果没有错误发生,则 inet_addr函数返回一个无符号长值,否则返回值为 INADDR_NONE,如果ip地址中某一项超过255,则此函数返回INADDR_NONE,这个函数在处理地址为255.255.255.255时返回-1,虽然255.255.255.255是一个有效的地址,不过inet_addr无法处理。
注意:当使用这些老函数,如inet_addr,inet_ntoa时,编译器会发出错误或者警告,可以使用 #pragma warning(disable:4996)来屏蔽这些警告
(20) char * WSAAPI inet_ntoa (in_addr in );
作用:将一个in_addr类型的数据转化成为一个点分十进制数(IP字符串),也可以说将网络字节排序的地址转至标准的ASCII以点分开的地址
示例:printf(“inet_ntoa ip =% s \ n“,inet_ntoa(sock.sin_addr));
返回值:如果没有错误发生, inet_ntoa会返回一个字符指针,否则,它返回 NULL
注意:该字符串的空间为静态分配的,这意味着在第二次调用该函数时,上一次调用将会被重写(复盖),所以如果需要保存该串最后复制出来自己
(21) int inet_aton(const char * cp,struct in_addr * inp);
作用:将网络主机地址ip(如192.168.1.10)转化为二进制数值(注意此时不是网络字节序),并存储在struct in_addr结构中,即第二个参数* inp
返回值:函数返回非0表示CP主机有地有效,返回0中主机表示地址无效
注意:这个转换完后不能用于网络传输,还需要调用htons或htonl函数才能将主机字节顺序转化为网络字节顺序
(22) INT WSAAPI inet_pton (INT Family,PCSTR pszAddrString,PVOID pAddrBuf );
作用:将 IPv4的或IPv6的互联网网络地址转换成数字二进制形式(网络字节序),将参数2转换后存在在参数3中
参数1:地址族,目前支持的值是AF_INET和AF_INET6
参数2:指向要转换为数字二进制形式的IP地址的文本,当家庭参数为AF_INET时,pszAddrString参数必须以标准点分十进制表示法指向IPv4地址的文本表示形式。当家庭参数为AF_INET6时,pszAddrString参数必须以标准表示法指向的IPv6地址的文本表示形式
参数3:指向缓冲区的指针,用于存储IP地址的数字二进制表示形式.IP以地址网络字节顺序报道查看,当家庭参数为AF_INET时,此缓冲区应足够大以容纳IN_ADDR。结构当家庭参数为AF_INET6时,此缓冲区应足够大以容纳IN6_ADDR结构
返回值:如果没有错误发生,函数返回值1,pAddrBuf参数指向的缓冲区包含网络字节顺序的二进制数字IP地址。如果所指定的系列而言输入字符串不是有效的表达式格式,那么返回值为0,否则,返回值-1
示例: inet_pton(AF_INET,ip,&foo.sin_addr);
(23) PCSTR WSAAPI inet_ntop (INT Family,const VOID * pAddr,PSTR pStringBuf,size_t StringBufSize );
作用:将IPv4的IPv6的或互联网网络地址转换成互联网标准格式
参数1:地址族,目前支持的值是AF_INET和AF_INET6
参数2:要转换为字符串的网络字节中IP地址的指针,当家庭参数为AF_INET时,pAddr参数必须指向具有要转换的IPv4地址的IN_ADDR结构。当族参数为AF_INET6时,pAddr参数必须指向具有要转换的IPv6地址的IN6_ADDR结构
参数3:指向缓冲区的指针,用于存储IP地址的字符串表示形式,对于IPv4地址,此缓冲区应足够大,至少可容纳16个字符。对于IPv6的地址,此缓冲区应足够大,至少可容纳46个字符
参数4:指向的缓冲区的长度(字符以为单位)
返回值:如果没有错误发生,函数返回一个指向缓冲区的指针,该缓冲区包含标准格式的IP地址字符串表示形式。否则,返回NULL值
示例: char str [INET_ADDRSTRLEN];
char * ptr = inet_ntop(AF_INET,&foo.sin_addr,str,sizeof(str));
有关htonl、htons、ntohl、ntohs函数(后面的s代表为无符号短整型数据,l代表无符号长整型数据)在使用的过程中,htons、ntohs用于修饰端口,htonl用于修饰ip地址
如:address.sin_addr.s_addr=htonl(INADDR_ANY);
address.sin_port=htons(st_config.n_port); si.sin_port=::ntohs(m_nPort);
(24) 关闭socket
int shutdown(
SOCKET s,
int how);
参数1:所要关闭的socket
参数2:以某种方式关闭 SD_SEND:关闭连接的发送 SD_RECEIVE:关闭连接的接收 SD_BOTH:发送和接收都关闭
int closesocket(SOCKET s);
为了避免丢失数据需要先进行shutdown再进行closesocket
shutdown与closesocket的区别:
shutdown:为了保证通信双方都能够收到应用程序发出的所有数据,一个合格的应用程序的做法是通知接受双方都不再发送数据!这就是所谓的“正常关闭 ”套接字的方法,而这个方法就是由shutdown函数,传递给它的参数有SD_RECEIVE,SD_SEND,SD_BOTH三种,如果是 SD_RECEIVE就表示不允许再对此套接字调用接受函数。这对于协议层没有影响,另外对于tcp套接字来说,无论数据是在等候接受还是即将抵达,都要重置连接(注意对于udp协议来说,仍然接受并排列传入的数据,因此udp套接字而言shutdown毫无意义)如果选择SE_SEND,则表示不允许再调用发送函数。对于tcp套接字来说,这意味着会在所有数据发送出并得到接受端确认后产生一个FIN包。如果指定SD_BOTH,答案不言而喻。
closesocket:对此函数的调用会释放套接字的描述,因此,调用此函数后,再是用此套接字就会发生调用失败,通常返回的错误是WSAENOTSOCK。此时与被closesocket的套接字描述符相关联的资源都会被释放,包括丢弃传输队列中的数据!对于当前进程中的线程来讲,所有被挂起的操作,或者是被挂起的重叠操作以及与其关联的任何事件,完成例程或完成端口的执行都将调用失败!另外 SO_LINGER标志还影响着closesocket的行为。
(24) int ioctlsocket(
int s,
long cmd,
u_long* argp);
参数1:所要操作的套接字
参数2:对套接字s的操作命令
参数3:指向cmd命令所带参数的指针
功能:控制套接字的模式,可用于任一状态的任一套接字,它用于获取与套接字相关的操作参数,而与具体协议或通讯子系统无关。支持下列命令(为cmd参数赋值):
FIONBIO:
允许或禁止套接字s的非阻塞模式,如允许非阻塞模式则argp所指数据为非零,如禁止非阻塞模式则argp所指数据为零,当创建一个套接字时(利用socket函数创建),默认处于阻塞模式(也就是非阻塞模式被禁止),这与BSD套接口是一致的,WSAAsySelect()函数将套接字自动设置为非阻塞模式,如果已对一个套接口进行了WSAAsynSelect()操作,则任何用ioctlsocket()来把套接口重新设置成阻塞模式的试图,将以WSAEINVAl失败,为了把套接口重新设置成阻塞模式,应用程序必须首先用WSAAsynSelect()调用(IEvent参数置为0)来禁止WSAAsynSelect()
FIONREAD:
确定套接字s自动读入的数据量,argp其中存有ioctlsocket()的返回值,如果s是SOCKET_STREAM类型,则FIONREAD返回在一次recv()中所接收的所有数据量,这通常与套接字中排队的数据总量相同,如果s是SOCK_DGRAM型,则FIONREAD返回套接字上排队的第一个数据报大小
SIOCATMARK:
确认是否所有的带外数据都已被读入,这个命令仅适用SOCK_STREAM类型的套接字,且该套接字已被设置为可以在线接收带外数据(SO_OOBINLINE)。如无带外数据等待读入,则该操作返回TRUE,否则返回FALSE,下一个recv()或recvfrom()操作将检索"标记"前一些或所有数据,应用程序可用SIOCATMARK操作来确定是否有数据剩下,如果在"紧急"(带外)数据前有常规数据,则按序接收这些数据(请注意,recv()和recvfrom()操作不会在一次调用中混淆常规数据域带外数据),argp指向一个BOOL型数,ioctlsocket()在其中存入返回值
返回值:成功后,ioctlsocket()返回0,否则的话返回-1,可以用WSAGetLastError()获取相应错误代码
错误代码:
WSANOTINITIALISED:在使用此API之前应首先成功调用WSAStarup()
WSAENETDOWN:套接字实现检测到网络子系统失效
WSAEINVAL:cmd为非法命令,或者argp所指参数不适用该cmd命令,或者该命令不适用于此种类型的套接字
WSAEINPROGRESS:一个阻塞的套接字正在运行中
WSAENOTSOCK:描述字不是一个套接字
(24) BOOL WINAPI GetExitCodeThread(
_In_ HANDLE hThread,
_Out_ LPDWORD lpExitCode
);
参数1:线程的句柄
参数2:指向接收线程终止状态的变量的指针,用于装载线程退出代码的一个长整数变量,如线程尚未中断,则设为常数STILL_ACTIVE,此函数立即返回,如果指定的线程尚未终止且函数成功,则返回的状态为STILL_ACTIVE,如果线程已终止且函数成功,则返回的状态为以下值之一:
1.ExitThread或TerminateThread函数中指定的退出值
2.线程函数的返回值
3.线程进程的退出值
返回值:函数成功则返回非零值,函数失败返回值为零
注意:GetExitCodeThread函数仅在线程终止后才返回应用程序定义的有效错误代码,因此应用程序不应使用STILL_ACTIVE作为错误代码,如果一个线程将STILL_ACTIVE作为错误代码返回,那么测试该值的应用程序可能会将其解释为线程仍在运行并在线程终止后继续测试线程的完成情况,这可能会导致应用到无限循环中