一,函数根据功能的不同可以分为如下几类:
(1) 套接字函数:此类函数包括socket(),bind(),getpeername(),getsockname()和closesocket(),它们主要完成创建,关闭套接字功能,以及对套接字命名与名字获取。
(2) 网络连接函数:此类函数包括listen(),accept(),connect()和shutdown(),它们完成网络连接(如虚电路)的建立与关闭。此类函数中有部分可阻塞。
(3) 数据传输函数:此类函数包括send(),recv(),sendto()和recvfrom(),它们完成网络数据的发送与接收,全部是可以阻塞的函数。
(4) 字节定序函数:此类函数包括htonl(),htons(),ntohl()和ntohs(),它们完成主机和网络之间数据字节顺序的转换。
(5) 地址转换函数:此类函数包括inet_addr(),inet_ntoa(),它们完成网络字符串地址和Internet地址之间的转换。
(6) 套接字控制函数:此类函数包括getsockopt(),setsockopt(),ioctlsocket()和select(),它们设置/获取套接字的选项,控制/检测套接字的工作状态。其中select()函数在必要时可能阻塞。
函数附表:
accept() * |
确认外来连接,并将它与一个立即建立的数据套接字联系起来。原始套接字返回到监听状态。 |
bind() |
给未命名套接字赋一个本地名。 |
closesocket() * |
从进程对象参考表中删去一个套接字,只有当SO_LINGER设置时才阻塞。 |
connect() * |
在指定套接字上初始化连接。 |
getpeername() |
获取与指定套接字连接的对等方的名字。 |
getsockname() |
获取指定套接字的当前名字。 |
getsockopt() |
获取与指定套接字相关的选项。 |
htonl() |
将一个32位数从主机字节顺序转换为网络字节顺序。 |
htons() |
将一个16位数从主机字节顺序转换为网络字节顺序。 |
inet_addr() |
将一个用网际标准点分表示法表示的字符串地址转换成网际地址值。 |
inet_ntoa() |
将一个网际地址值转换成一个用点分十进制表示法表示的字符串地址 |
ioctlsocket() |
为套接字提供控制。 |
listen() |
在指定套接字上监听外来连接。 |
ntohl() |
将一个32位数从网络字节顺序转换为主机字节顺序。 |
ntohs() |
将一个16位数从网络字节顺序转换为主机字节顺序。 |
recv() * |
从一个连接的套接字上接收数据。 |
recvfrom() * |
从一个连接或未连接的套接字上接收数据。 |
select() * |
执行多路同步I/O。 |
send() * |
给一个连接套接字发送数据。 |
sendto() * |
给一个连接或未连接套接字发送数据。 |
setsockopt() |
设置与指定套接字相关的选项。 |
shutdown() |
关闭全双工连接的一部分。 |
socket() |
建立一个通讯用的末端点,返回一个套接字。 |
二,常用函数解析
1)socket( ) 打开socket
语法: SOCKET WSAAPI socket (
IN int af, //一种地址格式描述。现在支持的格式只有PF_INET,它是ARPA网际地址格式
IN int type, //要建立的套接字的类型描述SOCK_DGRAM -> UDP ; SOCK_STREAM -> TCP ;SOCK_RAW -> 其他
IN int protocol //套接字使用的特定协议,如果调用者不希望指定协议,则置为0,使用默认的连接模式
);
作 用:此函数建立一个套接字,它给指定的地址族、数据类型和协议分配一个套接字描述符以及相关的资源。
返回值:如果没有错误发生,socket()返回一个与建立的套接字相关的描述符。否则它返回值INVALID_SOCKET,错误码可通过调用WSAGetLastError()函数得到。
注 释: WinSock 1.1只支持一种地址族PF_INET,但是,地址族可以给定为AF_UNSPEC(不指定),在这种情况下,参数protocol必须指定。使用的协议号是针对发生通讯的“通讯区域”来说的,大多数情况下,一种协议只支持特定地址族中特定类型的套接字。
SOCK_STREAM:流套接字既是主动的,又是被动的。
主动的套接字被客户用来使用connect()函数初始化连接请求,默认情况下,socket()建立主动套接字。
服务器使用被动套接字来接收connect()调用的连接请求。要将主动套接字变为被动套接字可以先用bind()调用将套接字与一名字相连接,再用listen()调用表明希望接收连接。
一旦套接字变为被动,它将不能用于初始化连接请求。在连接建立成功后,就可以使用send()和recv()调用进行数据传输。当会话结束,必须执行closesocket()函数关闭套接字
带外数据也可以使用send()进行发送,使用recv()进行接收。实现了SOCK_STREAM的通讯协议可以确保数据不会丢失和重复。如果对等方协议有缓冲区空间,而数据不能在一个合理的时间内发送,则连接被认为是中断了,后续的调用将以错误码WSAETIMEDOUT失败返回。
SOCK_DGRAM类型的套接字无主动和被动之分,它允许使用sendto()和recvfrom()函数与任意的对等方发送或接收数据
如果这样的套接字已经连接(connect())到一个特定的对等方,则数据报也可以通过send()发送数据到对等方,或通过recv()从对等方接收数据。
数据报套接字可以向多个接收者广播消息。将目的地址置为广播地址与网络接口有关(取决于地址的类别和是否有子网)。如果基本网被设置为支持广播,可以使用INADDR_BROADCAST来向基本网进行广播。
示例:SOCKET serversoc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
SOCKET serversoc = socket(AF_INET, SOCK_STREAM, 0) // TCP
SOCKET serversoc = socket(AF_INET, SOCK_DGRAM,0) // UDP
2)bind( ) // 将本地地址与套接字相连,建立半连接
语 法: int WSAAPI bind (
IN SOCKET s, //指示未连接的数据报或流套接字的描述符
IN const struct sockaddr FAR* name, //本地名字
IN int namelen // 地址缓存长度 sizeof(socketaddr)
);
作 用:此函数用于未连接的数据报或流套接字,它将一本地地址与套接字连接,即建立半相关。当一套接字用socket()创建后,它存在于一名字空间(地址族), 但它没有赋予名字。bind()通过将一本地名字赋予一未命名的套接字, 建立起套接字的本地连接(主机地址/端口号)。
返回值: 如果没有错误发生,bind()返回0。否则返回值SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
注 释: 在Internet地址族中,一个名字有几个部分 。对于SOCK_DGRAM和SOCK_STREAM类型的套接字来说,名字分为三部分:主机地址,协议号(分别默认设置为UDP和TCP),以及一个标志应用程序的端口号。在WinSock 2中,参数name并不严格地解释为指向“sockaddr”结构的指针,但为了与Windows Sockets的兼容性仍用这种表示。服务提供者可以把它当作一个指向长度为namelen的内存块而自由处理,在此内存块的前两个字节(对应结构sockaddr定义中的sa_family元素)必须为包含建立套接字的地址族,否则将产生错误WSAEFAULT。
如果应用程序不关心赋予它的地址,则可指定一个等于常数INADDR_ANY的网际地址,和/或等于0的端口。 如果网际地址等于INADDR_ANY,任何合适的网络接口都可用,这就简化了在多宿主机上的应用程序设计。当一个服务器向几个网络提供服务时,这将变得很重要。在不指定地址的情况下,服务器可以接收发向其端口的所有UDP数据包和TCP连接请求,而不必关心请求是从哪一个网络接口到达的。如果端口指定为0,Windows Sockets实现将为应用程序指定一界于1024和5000之间的端口值。应用程序可在bind()后使用getsockname()来得到赋给它的地址,但要注意的是,对于网际地址等于INADDR_ANY的情况,getsockname()只有当套接字连接后才填入网际地址(Internet address),原因是当主机是多地址家族时,几个网际地址都是合法的。对客户应用程序来说,不鼓励将其绑扎到一个指定的端口,因为这样存在与已经使用了该端口的其它套接字冲突的危险。
数据结构:
struct sockaddr {
u_short sa_family;
char sa_data[14];//根据地址族值定义地址结构
};
由于Windows Sockets只支持AF_INET地址域,因此名字缓冲区的格式只能是sockaddr_in结构。此结构在winsock.h中定义如下:
struct in_addr
{
u_long s_addr;
};
struct sockaddr_in
{
u_char sin_len;
u_char sin_family; //AF_INET
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8]; //字段未用,必须为全0
};
示例:
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(9102); //htons(n)本函数将一个16位数从主机字节顺序转换成网络字节顺序 (如:12 34 --> 34 12) host to net
serveraddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
serveraddr.sin_addr.s_addr=INADDR_ANY;
serveraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
3) listen( ) //TCP服务端监听函数
语法: int WSAAPI listen (
IN SOCKET s, //已经命名的socket(使用了bind)尚未连接
IN int backlog //等待连接的队列的长度 大于1小于5
);
返回值: 如果没有错误发生,listen()返回0。否则它返回SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
注释: listen()调用表明套接字准备好接收客户连接请求。它将主动套接字变为被动套接字,一旦变换后,s将再不能作为主动套接字来初始化连接请求。调用listen()是服务器接收一个连接请求的四个步骤中的第三步。它在调用socket()函数分配一个流套接字,且调用bind()函数给s连接一个名字之后调用,而且一定要在函数accept()之前调用。
此函数用于同一时刻有多个连接请求的服务器:如果一个连接在请求到达时队列已满,此连接请求被忽略,并且客户将收到WSAECONNREFUSED指示的错误。
当没有可用描述符时,listen()试图继续执行下去,它接收连接直到队列变空为止。如果又有可用描述符(系统释放了一些描述符),稍后调用的listen()或accept()将队列重新填到当前的或最近的“backlog(后备日志)”,如果可能的话,恢复对外来连接的监听。
应用程序可以对同一个套接字调用多次listen()函数,其结果是更新了监听套接字的当前“后备日志”。如果未处理的连接比新的backlog值还多,则超出部分被重置或丢弃。
在WinSock 1.1中,backlog的最大值是5。如果backlog小于1,则backlog被置为1;若backlog大于SOMAXCONN(定义在winsock.h中,值为5),则backlog被置为SOMAXCONN。在WinSock 2中,没有指定具体值,它由服务提供者决定。
示例:
if(listen(serversoc, 1) != 0) //监听客户端的请求
{
printf("Listen fail!\n");
}
4)connect( ) //TCP客户端发起连接
语法: int WSAAPI connect (
IN SOCKET s, //发出连接请求的套接字的描述符
IN const struct sockaddr FAR* name, //对等方的套接字的地址
IN int namelen //socket address结构的字节数
);
作用:此函数用来与对等方建立一个连接。如果套接字s没有绑扎,则系统赋予本地相关一个唯一值,并且套接字被表示为已绑扎的。
返回值: 如果没有错误发生,connect()返回0。否则返回值SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
对于阻塞套接字来说,返回值表示连接试图是否成功。
对于非阻塞套接字来说,连接试图不一定马上完成。当connect()返回SOCKET_ERROR,并且WSAGetLastError()返回WSAEWOULDBLOCK时,应用程序可以:
1. 利用select()函数,通过检查套接字是否可写来判断连接请求是否完成。
2. 如果应用程序已使用WSAAsyncSelect()函数注册了对连接事件的兴趣,则当连接操作完成时应用程序将收到FD _CONNECT通知(无论成功与否)。
3. 如果应用程序已使用WSAEventSelect()函数注册了对连接事件的兴趣,则当连接操作完成时相应的事件对象将设置信号(无论成功与否)。
对于一个非阻塞套接字来说,在连接试图完成之前,任何对该套接字的connect()调用都将以错误码WSAEALREADY失败,在连接成功之后则返回错误码WSAEISCONN。由于Windows Sockets 1.1规范在定义当连接请求正在处理时调用connect()函数返回的错误值有二义性,其返回值对于不同的Windows Sockets实现其值不同,因此不推荐应用程序采用多次调用connect()函数的方式来检测连接是否完成。如果应用程序员一定要这么做,为了确保程序的可靠运行,他们在处理错误码WSAEALREADY的同时,还必须准备处理WSAEINVAL和WSAEWOULDBLOCK错误码。
如果返回值指出连接试图失败(例如WSAECONNREFUSED,WSAENETUNREACH,WSAETIMEDOUT等),则应用程序可对该套接字再次调用connect()函数
注释: 客户程序在流套接字上调用connect()函数来建立与服务器的连接,服务器必须有一个空闲的被动接口。否则,connect()函数将以错误码WSAECONNREFUSED失败。
对于流套接字(类型SOCK_STREAM),connect()函数用来初始化与参数name(套接字名字空间中的地址)指示的外部主机的活动连接。当connect()调用成功完成后,套接字就可以收发数据了。如果结构name的地址域全为0,则返回错误WSAEADDRNOTAVAIL。
流套接字只能调用connect()函数一次,多次调用将以错误码WSAEISCONN失败。数据报套接字(类型SOCK_DGRAM)可以重复调用connect()函数来变换连接,它设置与数据报套接字数据传送的默认目标,供以后的send()/WSASend()和recv()/WSARecv()函数使用。数据报套接字可以通过连接一个无效地址,例如空地址(即全部字段清零)来结束连接。
对于无连接的套接字来说,name可以是任何合法的地址,包括广播地址。然而,要连接到一个广播地址,套接字必须使用setsockopt()函数的SO_BROADCAST选项来允许广播,否则connect()函数将以错误码WSAEACCES失败。
对于处于阻塞模式的套接字s,connect()函数阻塞调用者,直到建立起连接或有错误被接收到。对于非阻塞套接字s,如果返回值是SOCKET_ERROR,并且错误码为WSAEWOULDBLOCK,那么应用程序可以使用select()函数来检查套接字s是否可写来判定连接请求是否完成;如果应用程序使用了基于消息的WSAAsyncSelect()/WSAEventSelect()函数来指示感兴趣的连接事件,那么当连接操作完成后,应用程序将收到FD_CONNECT消息。
5)accept( ) //服务端接收客户端请求
语法: SOCKET WSAAPI accept (
IN SOCKET s, //该套接字在用作accept()函数的参数前必须先调用过listen()函数,此时它正处于监听连接的状态。
OUT struct sockaddr FAR* addr, //一个可选的指向缓冲区的指针,用来接收连接实体的地址,在通讯层使用。addr的确切格式由套接字创建时建立的地址族决定
OUT int FAR* addrlen//一个可选的指向整数的指针,它调用时含有地址addr指向的空间的大小,返回时含有返回的地址的确切长度(字节数)。
);
作用:此函数用于从套接字上接收一个连接。它提取挂在套接字s 上的连接队列中的第一个连接,创建一个和s有相同属性(包括使用函数WSAAsyncSelect()或WSAEventSelect()注册的异步事件,但不包括监听套接字的套接字组ID)的新数据套接字,并返回一个指向新套接字的句柄。
如果连接队列上没有等待的连接,并且套接字没有标志为非阻塞,那么accept()阻塞调用直到出现一个连接。
如果套接字标志为非阻塞,并且队列上没有等待的连接,那么accept()返回错误WSAEWOULDBLOCK。新创建的数据套接字不能用来接收更多的连接,它只能用于数据传输;原来的套接字仍然打开,处于监听连接状态。
返回值: 如果没有错误发生,accept()返回一个SOCKET类型的值,表示接收到的套接字的描述符。否则返回值INVALID_SOCKET,错误码可通过调用WSAGetLastError()函数得到。
注释: 该调用只能和基于连接的套接字类型如SOCK_STREAM一起使用。如果参数addr 和/或addrlen等于NULL, 那么没有关于接收套接字的远程地址信息返回。
示例:SOCKADDR_IN clientaddr; //可选
int len; //可选
clientsoc = accept(serversoc, (SOCKADDR *)&clientaddr, &len)
recv(clientsoc, buf, BUFLEN, 0)
6) send( )
语法: int WSAAPI send (
IN SOCKET s, //已连接的套接字描述符
IN const char FAR * buf, //指向存有发送数据的缓冲区的指针
IN int len, //缓冲区buf中数据长度
IN int flags
);
flags : 定调用的方式,它可用来与套接字相关的选项一起影响函数的功能。就是说,send()函数的意义由套接字选项和flags参数共同决定。flags可取下述值:
MSG_DONTROUTE 指出数据不提交给路由选择。
MSG_OOB 发送带外数据。
作 用:此函数用于在参数s指定的已连接的数据报或流套接字上发送输出数据
返回值: 如果没有错误发生,send()返回总共发送的字节数(注意,这可能比len指示的长度小)。否则它返回SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
注 释: 对数据报套接字来说,一定要注意一次发送的数据不能超过下层子网的最大IP包大小,该值在WSAStartup()返回的WSAData结构的iMaxUdpDg中给出。如果数据太长,不能通过下面的协议,将返回错误WSAEMSGSIZE,并且没有数据传输。
注意:send()函数只是将数据传送到输出缓冲区,它执行成功并不意味着数据成功地发送出去了。如果传输系统中没有可用缓冲区用来保存传输数据,send()将阻塞,除非套接字被设置为非阻塞I/O模式。在非阻塞SOCK_STREAM套接字上,写的字节数可为从1到请求的长度,这由本地和外部主机上可用的缓冲区大小决定。select()、WSAAsyncSelect()或WSAEventSelect()函数可用来查看何时可能发送更多的数据。
示例:
if(send(soc, buf, strlen(buf)+1, 0)<=0)
{
printf("Error!\n");
}
7)sendto( )
语法: int WSAAPI sendto (
IN SOCKET s,
IN const char FAR * buf,
IN int len,
IN int flags,
IN const struct sockaddr FAR * to,
IN int tolen
);
作用:此函数用于在参数s指定的数据报或流套接字上向指定目的地发送输出数据。
参 数 |
描 述 |
s |
套接字描述符。 |
buf |
指向存有发送数据的缓冲区的指针。 |
len |
缓冲区buf中数据长度。 |
flags |
指定调用的方式,它可用来与套接字相关的选项一起影响函数的功能。就是说,sendto()函数的意义由套接字选项和flags参数共同决定。flags可取下述值: MSG_DONTROUTE 指出数据不提交给路由选择。 MSG_OOB 发送带外数据。 |
to |
指向目的套接字地址的任选指针。 |
tolen |
to参数所指地址的长度。 |
返回值: 如果没有错误发生,sendto()返回总共发送的字节数(注意,这可能比len指示的长度小)。否则它返回SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
注 释: sendto()函数通常用在SOCK_DGRAM套接字上,用于向参数to指定的对等方套接字发送数据报。
对于SOCK_STREAM套接字,to和tolen参数被忽略,在这种情况下,sendto()等同于send()。
为了发送广播消息(仅用于SOCK_DGRAM套接字),参数to中的地址需要用特殊的IP地址INADDR_BROADCAST(在winsock.h中定义)和一定的端口号构成。通常广播数据报不应超长,否则就会出现碎片,这意味着数据报的数据部分(不包括头)不应超过512字节。
8) recv( )
语法: int WSAAPI recv (
IN SOCKET s,
OUT char FAR* buf,
IN int len,
IN int flags
);
作用:此函数用于在参数s指定的已连接的数据报或流套接字上读取输入数据。
参 数 |
描 述 |
s |
已连接的套接字描述符。 |
buf |
指向接收输入数据缓冲区的指针。 |
len |
buf参数所指缓冲区的长度。 |
flags |
指定调用的方式,它可用来与套接字相关的选项一起影响函数的功能。就是说,recv()函数的意义由套接字选项和flags参数共同决定。flags可取下列值: MSG_OOB 读取套接字上的带外数据。 MSG_PEEK 查看输入数据,数据被拷入缓冲区中, 但不从输入队列中清除。 |
返回值: 如果没有错误发生,recv()返回收到的字节数。如果连接被关闭,返回0。否则它返回SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
注释: 此函数用在参数s指定的连接套接字或已绑扎的无连接套接字上,用来读取进来的数据。套接字的本地地址必须已知。对于服务器方应用程序来说,这常常通过显式地调用bind()函数完成,或调用accept()/WSAAccept()函数附带完成。客户方应用程序不鼓励对套接字使用显式的绑扎,
无论是连接还是无连接套接字,此函数都严格限制可接受的接收消息来源地址。此函数只返回从连接中指定的远程地址到来的消息,从其它地址到来的消息被丢弃。
对于流风格套接字(如SOCK_STREAM类型),最多可返回最大缓冲区长度的信息。如果套接字被设置用于在正常数据流中接收带外数据(套接字选项SO_OOBINLINE), 并且带外数据未读取, 那么只返回带外数据。应用程序可使用ioctlsocket()或WSAIoctl()函数的SIOCATMARK命令来查看是否还有带外数据未读出。
对于基于消息的套接字(如SOCK_DGRAM类型),数据从函数connect()指定的目的地址的第一个排队数据报中抽取出来,最多可有最大缓冲区长度的信息。如果数据报比提供的缓冲区大,则缓冲区填以数据报的第一部分,recv()返回错误码WSAEMSGSIZE。对于不可靠协议(如UDP),多余的数据就丢失了;对于可靠协议,数据被服务提供者保留,直到它们被使用了足够大的缓冲区的recv()调用读取。
如果套接字上没有输入数据,那么除非是非阻塞模式,否则recv()函数将一直等待数据的到来,此时将返回SOCKET_ERROR错误,错误码设为WSAEWOULDBLOCK。应用程序可通过调用select()、WSAAsyncSelect()或WSAEventSelect()函数来查看何时有数据到来。
如果套接字是面向连接的, 并且远程方已“雅致”地关闭了连接,所有数据也已经被接收,则recv()立即返回,接收0字节数据。如果连接被复位,recv()将失败,错误码为WSAECONNRESET。
示例:recv(clientsoc, buf, BUFLEN, 0) //服务端
9) recvfrom( )
语法: int WSAAPI recvfrom (
IN SOCKET s,
OUT char FAR* buf,
IN int len,
IN int flags,
OUT struct sockaddr FAR* from,
IN OUT int FAR* fromlen
);
作用:此函数用于在参数s指定的套接字(可能已连接)上读取输入数据,并捕获发送数据的地址,存入源地址缓冲区。
参 数 |
描 述 |
s |
套接字描述符。 |
buf |
指向接收输入数据缓冲区的指针。 |
len |
buf 参数指向的缓冲区的长度。 |
flags |
指定调用的方式,它可用来与套接字相关的选项一起影响函数的功能。就是说,recvfrom()函数的意义由套接字选项和flags参数共同决定。flags可取下列值: MSG_OOB 读取套接字上的带外数据。 MSG_PEEK 查看输入数据,数据被拷入缓冲区中, 但不从输入队列中清除。 |
from |
任选指针,它指示源地址缓冲区。 |
fromlen |
任选指针,它指示from长度。 |
返回值: 如果没有错误发生,recvfrom()返回收到的字节数。如果连接被关闭,返回0。否则它返回SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
注释: 此函数用在参数s指定的连接套接字或已绑扎的无连接套接字上,用来读取进来的数据。套接字的本地地址必须已知。对于服务器方应用程序来说,这常常通过显式地调用bind()函数完成,或调用accept()/WSAAccept()函数附带完成。客户方应用程序不鼓励对套接字使用显式的绑扎,它可以通过函数connect()、WSAConnect()、sendto()、WSASendTo()或WSAJoinLeaf()来将套接字附带变为绑扎的。
无论是连接还是无连接套接字,此函数都严格限制可接受的接收消息来源地址。此函数只返回从连接中指定的远程地址到来的消息,从其它地址到来的消息被丢弃。
流风格套接字(如SOCK_STREAM类型),最多可返回最大缓冲区长度的信息。如果套接字被设置用于在正常数据流中接收带外数据(套接字选项SO_OOBINLINE), 并且带外数据未读取, 那么只返回带外数据。应用程序可使用ioctlsocket()或WSAIoctl()函数的SIOCATMARK命令来查看是否还有带外数据未读出。对于SOCK_STREAM套接字,from和fromlen参数被忽略。
对于基于消息的套接字,数据从第一个排队的数据报中抽取出来,最多可有最大缓冲区长度的信息。如果数据报比提供的缓冲区大,则缓冲区填以数据报的第一部分,recvfrom()返回错误码WSAEMSGSIZE。对于不可靠协议(如UDP),多余的数据被丢失。
如果from非零,且套接字是SOCK_DGRAM类型,发送数据的对等方的网络地址被拷入对应的结构sockaddr中。fromlen指向的值被初始化为该结构的大小,返回时被改变为存入此处的地址的实际大小。
如果套接字上没有输入数据,那么除非是非阻塞模式,否则recvfrom()函数将一直等待数据的到来,此时将返回SOCKET_ERROR错误,错误码设为WSAEWOULDBLOCK。应用程序可通过调用select()、WSAAsyncSelect()或WSAEventSelect()函数来查看何时有数据到来。
如果套接字是面向连接的, 并且远程方已“雅致”地关闭了连接,recvfrom()将立即返回,接收0字节数据。如果连接被复位,recv()将失败,错误码为WSAECONNRESET。
10) closesocket()
语法: int WSAAPI closesocket (
IN SOCKET s //待关闭的套接字描述符
);
作用:此函数关闭套接字s,并释放分配给该套接字的资源,以后对s 的引用都将产生错误WSAENOTSOCK。如果s涉及一个打开的TCP连接,该连接被释放。
返回值: 如果没有错误发生,closesocket()返回0。否则返回值SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到
注释:closesocket()函数的语义受套接字选项SO_LINGER和SO_DONTLINGER的影响,具体见下表(默认情况下是允许SO_DONTLINGER):
选 项 |
间 隔 |
关闭类型 |
等待关闭? |
SO_DONTLINGER |
不用 |
Graceful |
No |
SO_LINGER |
零 |
Hard |
No |
SO_LINGER |
非零 |
Graceful |
Yes |
如果SO_LINGER设置(例如,linger结构的l_onoff域非零)并且超时间隔为零(l_linger为零),那么即使队列数据尚未发送或确认,closesocket()函数也不会阻塞。这称作强制(“hard”或“abortive”)关闭,因为套接字的虚电路立即复位,任何未发送的数据都将丢失,并且在虚电路远程方的任何recv()调用都将以WSAECONNRESET失败。在这种情况下,套接字不进入TCP状态机的三次握手流程,系统资源被立即释放。这对于服务器应用程序非正常退出后希望能立即启动很有用,当正常通信中不鼓励使用。
如果SO_LINGER设置超时间隔为非零,closesocket()函数将阻塞,直到剩余的数据都发送完毕或直到超时退出,这称作“雅致”(graceful)关闭。注意如果套接字设置为非阻塞并且SO_LINGER设置为非零超时,调用closesocket()将失败,错误码为WSAEWOULDBLOCK。
如果SO_DONTLINGER设置在流套接字上(例如,linger结构的l_onoff域为零), closesocket()调用将立即返回。然而,排队等待传送的任何数据如果可能的话都将在该套接字关闭前发送出去,这也称作“雅致”关闭。注意在这些情况下,Windows Sockets实现可能会在任意时间内不释放套接字和其他资源, 这可能影响希望使用全部可用套接字的应用程序。如果应用程序要确保连接上的所有数据都被发送或接收到,则应该在调用closesocket()函数之前调用shutdown()函数。
下面给出closesocket()函数的小结:
· 如果SO_DONTLINGER允许(默认设置),且不会出现错误WSAEWOULDBLOCK──连接在后台“雅致”关闭;
· 如果SO_LINGER允许并且超时间隔为0,则总是立即返回──连接被重置或终止;
· 如果SO_LINGER允许并且超时间隔非0:
──对于阻塞套接字,阻塞到所有数据发送完或超时间隔到时;
──对于非阻塞套接字,立即返回并且指示错误WSAEWOULDBLOCK。
11) shutdown( )
语法: int WSAAPI shutdown (
IN SOCKET s,
IN int how
);
作用:此函数用来切断一个双向连接的接收、发送部分或全部连接。
参 数 |
描 述 |
s |
套接字描述符。 |
how |
断路情况,有0,1,2三种不同情况:如果how为0(SD_RECEIVE),套接字上后续的接收将被禁止,这对低层的协议没有影响。对TCP套接字来说,由于数据不能递交到用户,如果有数据在套接字上排队等待接收,或有后续数据到达,则连接被重置。对UDP来说,进来的数据报被接收并排队。这些情况都没有ICMP错误包产生。如果how为1(SD_SEND),后续的发送被禁止。对TCP套接字来说,将发送一个FIN。设置how为2(SD_BOTH)将同时禁止发送和接收。 |
返回值:如果没有错误发生,shutdown()返回0。否则它返回值SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
注释: 不管套接字上SO_LINGER是否设置,shutdown()函数都不会阻塞。应用程序不应该在shutdown()一个套接字之后再去使用它。注意shutdown()并不关闭套接字,与套接字相关的资源并没有释放,除非执行一个closesocket()调用。
为了确保在一个连接的套接字上的所有数据在它关闭前能够被发送/接收,应用程序应该采用“雅致”的断连,如使用下面的步骤:
· 调用WSAAsyncSelect()函数注册FD_CLOSE事件通知;
· 使用用参数how=SD_SEND调用shutdown()函数;
· 当接收到FD_CLOSE事件时,调用recv()函数直到接收到0字节,或返回错误码SOCKET_ERROR;
· 调用closesocket()函数。