一、 操作系统提供的网络接口
为了能更好的排查网络通信问题 , 我们需要熟悉操作系统提供的以下网络接口函数 , 列表如下:
文章插图
注意:这里以bekeley提供的标准为例 , 不包括特定操作系统上特有的接口函数(如windows平台的WSASend , linux的accept4) , 也不包括实际与网络数据来往不相关的函数(如select、linux的epoll) , 这里只讨论与tcp相关的接口函数 , 像与udp相关的函数sendto/recvfrom等函数与此类似 。
下面讨论一下以上函数的一些使用注意事项:
1、以上函数如果调用出错后 , 返回值均为-1;但是返回值是-1 , 不一定代表出错 , 这还得根据对应的套接字模式(阻塞与非阻塞模式) 。
2、默认使用的socket函数创建的套接字是阻塞模式的 , 可以调用相关接口函数将其设置为非阻塞模式(Windows平台可以使用ioctlsocket函数 , linux平台可以使用fcntl函数 , 具体设置方法可以参考这里 。) 。阻塞模式和非阻塞模式的套接字 , 对服务器的连接服务器和网络数据的收发行为影响很大 。详情如下:
阻塞模式下 , connect函数如果不能立刻连上服务器 , 会导致执行流阻塞在那里一会儿 , 直到connect连接成功或失败或网络超时;而非阻塞模式下 , 无论是否连接成功connect将立即返回 , 此时如果未连接成功 , 返回值将是-1 , 错误码是EINPROGRESS , 表示连接操作仍然在进行中 。Linux平台后续可以通过使用select/poll等函数检测该socket是否可写来判断连接是否成功 。
阻塞套接字模式下 , send函数如果由于对端tcp窗口太小 , 不足以将全部数据发送出去 , 将阻塞执行流 , 直到出错或超时或者全部发送出去为止;同理recv函数如果当前协议栈系统缓冲区中无数据可读 , 也会阻塞执行流 , 直到出错或者超时或者读取到数据 。send和recv函数的超时时间可以参考下文关于常用socket选项的介绍 。
非阻塞套接字模式下 , 如果由于对端tcp窗口太小 , 不足以将数据发出去 , 它将立刻返回 , 不会阻塞执行流 , 此时返回值为-1 , 错误码是EAGAIN或EWOULDBLOCK , 表示当前数据发不出去 , 希望你下次再试 。但是返回值如果是-1 , 也可能是真正的出错了 , 也可能得到错误码EINTR , 表示被linux信号中断了 , 这点需要注意一下 。recv函数与send函数情形一样 。
3、send函数虽然名称叫“send” , 但是其并不是将数据发送到网络上去 , 只是将数据从应用层缓冲区中拷贝到协议栈内核缓冲区中 , 具体什么时候发送到网络上去 , 与协议栈本身行为有关系(socket选项nagle算法与这个有关系 , 下文介绍常见套接字选项时会介绍) , 这点需要特别注意 , 所以即使send函数返回一个大于0的值n , 也不能表明已经有n个字节发送到网络上去了 。同样的道理 , recv函数也不是从网络上收取数据 , 只是从协议栈内核缓冲区拷贝数据至应用层缓冲区 , 并不是真正地从网络上收数据 , 所以 , 调用recv时 , 操作系统的协议栈已经将数据从网络上收到自己的内核缓冲区中了 , recv仅仅是一次数据拷贝操作而已 。
4、由于套接字实现是收发全双工的 , 收和发通道相互独立 , 不会相互影响 , shutdown函数是用来选择关闭socket收发通道中某一路(当然 , 也可以两路都关闭) , 其how参数取值一般有三个:SHUT_RD/SHUT_WR/SHUT_RDWR , SHUT_RD表示关闭收消息链路 , 即该套接字不能再收取数据 , 同理SHUT_WR表示关闭套接字发消息链路 , 但是这里有个问题 , 有时候我们需要等待缓冲区中数据发送完后再关闭连接怎么办?这里就要用到套接字选项LINGER , 关于这个选项请参考下文常见的套接字选项介绍 。最后 , SHUT_RDWR同时关闭收消息链路和发消息链路 。通过上面的分析 , 我们得出结论 , shutdown函数并不会要求操作系统底层回收套接字等资源 , 真正会回收资源是close函数 , 这个函数会要求操作系统回收相关套接字资源 , 并释放对ip地址与端口号二元组的占用 , 但是由于tcp四次挥手最后一个阶段有个TIME_WAIT状态(关于这个状态下文介绍tcp三次握手和四次回收时会详细介绍) , 导致与该socket相关的端口号资源不会被立即释放 , 有时候为了达到释放端口用来复用 , 我们会设置套接字选项SOL_REUSEPORT(关于这个选项 , 下文会介绍) 。综合起来 , 我们关闭一个套接字 , 一般会先调用shutdown函数再调用close函数 , 这就是所谓的优雅关闭:
推荐阅读
- Linux常用的10个性能检测命令!
- MySQL服务器最近偶尔出现cpu居高不下的情况,如何排查?
- linux系统root密码忘记了怎么办
- Linux 防火墙入门教程
- app开发完成后为什么需要进行ios签名内测?
- 高并发服务器逻辑处理瓶颈,如何解决?
- 5分钟快速搭建FTP服务器的图文教程
- 苹果企业开发者账号卖多少钱
- 程序员和开发者,细品之下见差别
- 传统服务器与云服务器的对比