Socket编程疑难

246 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情

网络编程疑难

工具使用

  • telnet ip port 可以检测是服务器开发还是服务器的问题。先ping一下说明网络是没问题的。再telent一下

    • 连的上的服务端问题
    • 连不上是客户端问题

Socket 函数

  • bind 端口号不指定, 操作系统自己选择一个,

    应用场景:唤醒服务

    网络库不能依赖很多特性,因此此时操作系统自己选择一个端口号很方便,实现linux下的唤醒机制。

    客户端也是可以用在客户端,将某个sockfd 绑定到指定的地址和端口号来连接服务端,

Socket 函数的非阻塞行为

1. 读取:read,recv, readv

对于非阻塞套接字,在非阻塞下的行为,根据返回值 n 可以有几种行为:

  • n >0:成功读取数据,n读取到的字节数
  • n =0: 表示对端关闭

为什么不可能接受到0字节的数据 ?

 接收到0字节的数据,意味着对端要发送个0字节的数据的。但是一个能在数据链路上发送的最小IP数据包大小是46字节:IP头部一般是20字节,TCP头部一般是20字节,如果应用层数据是0字节,整个IP数据包大小40字节小于46字节,根本无法发送出去,因此接收端不可能接收到大小为0字节的数据包。

  • n < 0:在非阻塞模式下要根据错误码判断是不是真的发生错误。

    • EAGAIN/EWOULDBLOCK:当前缓冲中没有数据可读,不是真的错误,需要再次调用这个函数进行重试
    • EINTR:被信号中断,不是真的错误,需要再次调用这个函数进行重试
    • 其他:是真的错误,需要关闭连接
2. 写操作: write,send, writev

在非阻塞套模式下,行为大致和 read 操作返回值含义一致。如果其发送缓冲区中已经没有空间,输出调用将立即返回错误:EWOULDBLOCK。如果发送缓冲区中还有一些空间,返回值就是复制到内核的字节数。

write 不存在返回0,因为如果返回0,就是表示发送缓冲区已满,不可再将数据复制到发送缓冲区,此时就是返回-1 且 erron = EAGAIN/EWOULDBLOCK

3. 接受外来连接:accept

对于一个非阻塞套接字使用 accept,也是返回 EWOULDBLOCK。不过这个操作一般是阻塞的,因为已经有 epoll/select存在代替这个函数阻塞。

4. 发起连接:connect

TCP连接建立涉及三次握手,客户端要一直等接收到服务对于自己的SYN请求的ACK应答,connect才是会返回,即第二次握手成功,connect才返回,因此每个TCP的 connect 调用都至少阻塞一个 RTT的时间。

对于一个非阻塞套接字调用connect,并且连接不能立即建立,那么连接照样能发起,不过会返回一个 EINPROGRESS 错误。 稍后可以使用 select 是否可读可写来判断是否建立成功:

  • 在 Windows 系统上,一个 socket 没有建立连接之前,我们使用 select 函数检测其是否可写,能得到正确的结果(不可写),连接成功后检测,会变为可写。所以,上述介绍的异步 connect 写法流程在 Windows 系统上是没有问题的。
  • 在 Linux 系统上一个 socket 没有建立连接之前,用 select 函数检测其是否可写,你也会得到可写的结果,所以上述流程并不适用于 Linux 系统。正确的做法是,connect 之后,不仅要用 select 检测可写,还要检测此时 socket 是否出错,通过错误码来检测确定是否连接上,错误码为 0 表示连接上,反之为未连接上。完整代码如下

域名解析

使用域名的好处在于可以避免服务前当前使用的 ip:port崩溃了,可以使用切换到其他 ip:port,而不至于服务停止。对于客户端而言没有影响,这个可以使用 ifconfig 来判断。

  • getaddrinfo:域名与ip的转换

设计网络库,需要考虑的功能

1. 使用哪种io复用函数

select 函数

  • select常常用户客户端的使用

epoll 函数

  • epoll_create 的参数任意正数

  • 读写+LT/ET 共四种工作方式

    • et模式的读和写两个方面考虑,读可能会造成数据积压

2. 事件触发

  • EPOLLIN

    • 内核中Socket接受缓冲区为空 --> 低电平 --> 不可读
    • 内核中Socket接受缓冲区不空 --> 高电平 --> 可读
  • EPOLLOUT

    • 内核中Socket发送缓冲区为满 --> 低电平 --> 不可写
    • 内核中Socket发送缓冲区不满 --> 高电平 --> 可写