Linux socket

socket

https://www.bilibili.com/video/BV1VWtyeVEsV

https://blog.valiantcheng.com/misc/socket-intro.html

如果发送数据包要从HTTP到数据链路封包全部构建一遍,这样的操作成本无疑是非常高的。操作系统的构建者为我们提供了很好的支持,他们屏蔽了底层的细节,抽象出一个统一的网络操作接口。 来便于上层用户使用网络。

这个统一的接口就是socket。socket直译是插槽,我们只需关心如何插入,而不需要关心内部的实现。socket是一套抽象接口体系,可以理解为一套使用协议或者是一套API。因为是操作系统聪明封装提供的机制,因此具体使用需要以系统调用的形式来实现。socket接口提供了非常多种类的socket,没种都有其使用场景。

这是所有socket操作的第一步,create socket。

getsocket和setsocket相对于查看获取和设置socket选项。很多和socket交互的细节都通过这个方法来进行控制。

接下来是bind,绑定操作,相当于把电器插头插入插槽。也就是将机器上的某个IP地址和端口组合进行绑定。其他操作者无法使用这个组合,时一种声明网络所有权的操作类型。

listen传输层语义的监听操作,被动等待某个其他的网络实体向自身(bind过的IP和端口组和)发起通讯(通常是connect操作)

accept,传输层的确认操作:确认某个尝试建立连接的网络实体和自身之间的关系。一旦accept正式的传输就已经建立,可以开始后续的数据交互

connect:传输层语义的连接操作,连接某个传输层实体(也就是协议+IP+端口号)协议可能是TCP、UDP或其他四层协议(对于TCP就是经典的三次握手过程)

shutdown、close:传输层语义的关闭操作

send、sendto:传输层语义的发送操作

recvfrom:传输层语义的接收操作

上述系统调用相当多都是传输层的,这说明socket很大一部分功能是提供四层接口、也就是传输层网络之上的接口。

但是并非所有操作都是四层的。

这些是常见的socket类型。

socket逐渐从网络通信层面发展演变成了一个数据通信操作的标准接口体系。

对大部分开发者来说有许多我们常用TCP的选项,如SOL_SOCKET,SO_REUSEADDR

其他类型的socket也可以设置很多针对性的选项,比如广播和多播的可设置选项就非常多

TCP常见场景

机器正常下线

哪怕是kill -9操作系统也会自动释放tcp连接。

如果此时已经建立连接,正在写入数据,会收到EOF相关字样的报错

如果还未成功收到连接会收到connection refused类报错

连接被拒绝了。正常情况出现在对端机器正常,但对应的服务已经挂掉了,这种情况下收到了发向不可用服务短口的数据,这是宿主机就会为不可用的服务进程对应socket链接发送一个RST(reset)数据包通知对端本地服务不存在或终止了。

机器异常下线

如果机器网线被拉断了。由于对端服务所在的机器对我们来说已经不可用了。所以此时不会有任何响应发回。此时发送端会按照socket选项的默认配置,也就是keepAlive以及timeout相关的socket选项,进行发送的等待和重试。如果超过一定的发送次数和时间,会报错并中断连接。

在 不设置socket timeout选项的情况下,发送后等待的默认重传次数一般是15次,时间大概是900秒左右。每次重试的中间间隔会依次按照指数退避算法进行而不是固定的重试频率。如果在日常遇到了这样的时间间隔,要足够敏感。如果想要尽快失败,可以调整内核参数tcp_retries2来修改这里的行为。

这种情况下报错分为俩种情况,如果路由器没有替代应答本地会直接报timeout错误,

如果路由器替代应答,会发回一个ICMP包,告知destination is unreachable,那么我们最终收到的是unreachable类的错误。

如果在这个过程中机器又恢复了,恢复的机器后已经丢失了原先的信息(可能是被强制断电然后重启)因此会直接发送reset包。告知我们对端的机器拒绝接收当前发起或正在进行的连接。类似进程挂掉的情况。

UDP对端不可用

一般来讲默认的UDP通信在对端服务故障、不存在或丢包时是会无限阻塞住的。所以一般底层库都是会提供默认的超时值设置的。比如DNS解析是2S,如果没有,在编程时要注意自己控制超时。UDP发送的数据包目的端口被对端判断为无效(目标机器上没有这个端口对应的服务)或是其他不可达场景,对端通常会回复ICMP错误消息(不回复RST是因为UDP协议没有RST报文)。类似我们在上一部分提到的路由器代答错误。UDP本身可以不调用connect方法直接发送数据的,这一点和TCP有本质上的差异。所以机器上不会有连接信息。UDP sento判定标准是只要数据从缓冲区发送出去了,这个操作就是成功的,它不会管数据是否返回或者有报错信息。因此有ICMP返回的化,原来的UDP是接受不到的(这被称为一步ICMP错误问题)。因此要解决这类问题通常需要通过一个守护进程来代为接受ICMP包通知,或者是UDP发送数据前都确保了connect再进行后续其他操作。


通过socket发送的数据被机器内部或外部设置的防火墙规则拦截了,如果本地网络有网络过滤处理,比如ipvs,iptable等可能会收到EPERM或Operation not permitted类报错。遇到这一类报错要注意很可能是本地协议栈中的的netfilter框架drop掉了我们的数据包。

如果是外部防火墙,有俩种可能一种是直接丢包,这时我们感觉就像是对方机器挂了一样。

还有一种是发送reset包拒绝我们的连接发起。

Last modification:October 30, 2025
如果觉得我的文章对你有用,请随意赞赏