Winsock 部分函数解析
注:本文综合了其他人的博客。
函数附表:
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 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()函数得到。
2) bind( ) 将本地地址与套接字相连,建立半连接
语 法: int WSAAPI bind (
IN SOCKET s, //指示未连接的数据报或流套接字的描述符
IN conststruct sockaddr FAR* name, //本地名字
IN int namelen // 地址缓存长度 sizeof(socketaddr)
);
作 用:此函数用于未连接的数据报或流套接字,它将一本地地址与套接字连接,即建立半相关。当一套接字用socket()创建后,它存在于一名字空间(地址族), 但它没有赋予名字。bind()通过将一本地名字赋予一未命名的套接字, 建立起套接字的本地连接(主机地址/端口号)。
返回值: 如果没有错误发生,bind()返回0。否则返回值SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
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值还多,则超出部分被重置或丢弃。
4) connect() TCP客户端发起连接
语法: int WSAAPI connect (
INSOCKET s, //发出连接请求的套接字的描述符
IN conststruct sockaddr FAR* name, //对等方的套接字的地址
INint namelen //socket address结构的字节数
);
作用:此函数用来与对等方建立一个连接。如果套接字s没有绑扎,则系统赋予本地相关一个唯一值,并且套接字被表示为已绑扎的。
返回值:如果没有错误发生,connect()返回0。否则返回值SOCKET_ERROR,错误码可通过调用WSAGetLastError()函数得到。
对于阻塞套接字来说,返回值表示连接试图是否成功。
对于非阻塞套接字来说,连接试图不一定马上完成。当connect()返回SOCKET_ERROR,并且WSAGetLastError()返回WSAEWOULDBLOCK时,应用程序可以:
1. 利用select()函数,通过检查套接字是否可写来判断连接请求是否完成。
2. 如果应用程序已使用WSAAsyncSelect()函数注册了对连接事件的兴趣,则当连接操作完成时应用程序将收到FD _CONNECT通知(无论成功与否)。
3. 如果应用程序已使用WSAEventSelect()函数注册了对连接事件的兴趣,则当连接操作完成时相应的事件对象将设置信号(无论成功与否)。
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, 那么没有关于接收套接字的远程地址信息返回。
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()函数可用来查看何时可能发送更多的数据。
7) 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。
8) 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()函数。
9) 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()函数。
10) setsockopt( ) 获取或者设置与某个套接字关联的选项。
语法:int get/setsockopt(
int sock,
int level,
int optname,
const void *optval,
socklen_t optlen
);
参数描述:
sock:将要被设置或者获取选项的套接字。
level:选项所在的协议层。
1)SOL_SOCKET:通用套接字选项.
2)IPPROTO_IP:IP选项.
3)IPPROTO_TCP:TCP选项.
optname:需要访问的选项名。指定控制的方式(选项的名称,与level有关,见下表:
选项名称 说明 数据类型
======================================================================
SOL_SOCKET
-------------------------------------------------------------------------------------------------------------
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSERADDR 允许重用本地地址和端口 int 关于该特性,见该文章
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
======================================================================
IPPROTO_IP
---------------------------------------------------------------------------------------------------------------
IP_HDRINCL 在数据包中包含IP首部 int
IP_OPTINOS IP首部选项 int
IP_TOS 服务类型
IP_TTL 生存时间 int
======================================================================
IPPRO_TCP
-----------------------------------------------------------------------------------------------------------
TCP_MAXSEG TCP最大数据段的大小 int
TCP_NODELAY 不使用Nagle算法 int
======================================================================
optval:获得或者是设置套接字选项.根据选项名称的数据类型进行转换。
optlen:对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。
返回值:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EBADF:sock不是有效的文件描述词
EFAULT:optval指向的内存并非有效的进程空间
EINVAL:在调用setsockopt()时,optlen无效
ENOPROTOOPT:指定的协议层不能识别选项
ENOTSOCK:sock描述的不是套接字
注意:
当设置TCP套接口接收缓冲区的大小时,函数调用顺序是很重要的,因为TCP的窗口规模选项是在建立连接时用SYN与对方互换得到的。对于客户,O_RCVBUF选项必须在connect之前设置;对于服务器,SO_RCVBUF选项必须在listen前设置。
例:
//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(constchar*)&nSendBuf,sizeof(int));