网络宏观结构
网络协议
基于windows的socket
加深理解
int PASCAL FAR recv (
_In_ SOCKET s, //socket
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf, //接手数据的缓冲buff
_In_ int len, //想要接收的长度
_In_ int flags); //标志位,一般NULL(接收数据后会从socket移除);MSG_PEEK(不移除socket的数据)
返回值:
- 大于0的值,表示recv成功,返回的是实际接受的字节数。
- 等于0的值,这个一般表示连接被关闭了。
- 小于0的值,这个一般就是SOCKET_ERROR了,值=-1;表示出错了。出错的话,可以用WSAGetLastError来获取详细的错误代码。
有几种常见的错误代码:
- 10038:在一个非套接字上尝试了一个操作。这个错误在多线程的时候容易出现。做socket的时候,前后之间最好加入错误的判断。
- 10057:这个错误,表示套接字没有连接。有时候connect之后没有判断,直接调用recv就有可能出现这种错误。
- 10014:这个表示指针非法了。使用之前把缓冲区分配好就可以了,现在一般很少出现内存不足的情况。还有就是要注意len的大小,如果len太大的话,会导致缓冲区溢出。要是你用这个在你的电脑上开一个端口,轻者导致程序崩溃,重者被别人拿到和程序一样高的权限。
细节描述:
当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数 据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到 协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以 在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0
int PASCAL FAR select (
_In_ int nfds, //一个文件描述符集合保存在fd_set变量中,可读,可写,异常这三个描述符集合需要使用三个变量来保存,分别是 readfds,writefds,exceptfds
_Inout_opt_ fd_set FAR *readfds, //监视文件描述符的一个集合,我们监视其中的文件描述符是不是可读,或者更准确的说,读取是不是不阻塞了
_Inout_opt_ fd_set FAR *writefds, //监视文件描述符的一个集合,我们监视其中的文件描述符是不是可写,或者更准确的说,写入是不是不阻塞了
_Inout_opt_ fd_set FAR *exceptfds, //用来监视发生错误异常文件
_In_opt_ const struct timeval FAR *timeout); //timeout表示select返回之前的时间上限(即超时)
原理:
理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
执行fd_set set;FD_ZERO(&set);则set用位表示是0000,0000。
若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
若再加入fd=2,fd=1,则set变为0001,0011
执行select(6,&set,0,0,0)阻塞等待
若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。
若fd=1上都发生可读事件,则select返回,此时set变为0000,0001。注意:没有事件发生的fd=1,fd=5被清空。
返回值:
成功时:返回描述符集合中”准备好了“的文件描述符数量。
超时:返回0
错误:返回-1,并设置 errno