jjzjj

这次 moon 要把 socket 玩的明明白白

moon聊技术 2023-03-28 原文

微信公众号:「moon聊技术」
关注选择“ 「星标」 ”, 重磅干货,第一 时间送达!
[如果你觉得文章对你有帮助,欢迎「关注,在看,点赞,转发」]

  • 前言
  • Socket 是什么?
  • Socket 运行流程
    • 基于 TCP
    • 基于 UDP
  • Socket TCP 是如何建立连接的
    • 三次握手发生在 socket 的哪几个函数中
  • Socket TCP 是如何断开连接的
    • 第四次挥手后为何要等待 2MSL

前言

一说到网络,大家必然会想到 TCP、UDP、Http、三握四挥等,但是一说 Socket,大家可能会有点模糊了,只知道网络中会用到,但是 Socket 究竟是什么? 套接字又是啥? 为啥网络离不开 Socket?

Socket 是什么?

Socket 其实就是套接字,大部分人对于 Socket 的理解就是它可以实现一个简单的网络通信,但是它「具体解决了哪些问题?有什么实际的作用?为什么会有一个 Socket 出现?」

image.png

Socket 其实是在「应用层与传输层之间的一个产物」,它把传输层的很多复杂操作封装成一些简单的接口,来让应用层调用以此来实现进程在网络中的通信,Socket 是对端口通信开发的工具,它要更底层一些。

Socket 其实类似于一台洗碗机,它的功能就是洗碗(网络通信),如果没有它,你可能需要自己手动去洗碗(手动调用传输层、应用层之间的各个 api),但是有了它你只需要点击开关、调整时长就行了(封装了 api),你可以不需要它,但是如果没有它,洗碗(应用层与传输层之间的交互)将变得非常繁琐。

image.png

一次完整的网络通信必不可少的会经过物理传输层的网线和网卡,网络传输层的 IP 协议可以知道要将数据传送给哪台机器,但是在计算机系统中会运行不同进程,那要如何把「网卡中的网络数据识别出来是给哪个进程的」,这其实就是 Socket 设计的想解决的一点了。

Socket 是「对 TCP/IP 或者 UDP/IP 协议的封装」,Socket 本身其实就是一个调用接口。通过这个接口我们在开发网络应用程序的时候,就可以不用关心底层是怎么实现的,减轻开发的难度。

Socket 运行流程

基于 TCP

image.png

Server

  • socket():表示创建一个 socket,底层会生成一个文件描述符,用来表示该 socket
  • bind():用来绑定服务的端口,地址,这里一般都是以固定的为主,因为在客户端连接的时候需要指定
  • listen():当绑定完成之后,listen 就会监听这个端口的数据包
  • accept():相当于一个开关,表示我准备好了,可以接受请求了,但是这里会一直阻塞,直到客户端连接成功
  • read():读取客户端发送过来的内容
  • write():客户端写入要返回的数据
  • close():断开连接,「四次挥手」

Client

  • socket():表示创建一个 socket,底层会生成一个文件描述符,用来表示该 socket
  • connet():表示与指定地址进行连接,在此之前,会随机创建自己的端口,tcp 的「三次握手就是从这里开始」
  • write():客户端写入要发送的数据
  • read():客户端读取服务端返回的数据
  • close():断开连接,「四次挥手」,给客户端发送断开连接的信息

基于 UDP

image.png

这里我就不细写了,其实大同小异,从流程图上就可以看到

因为 UDP 是无状态的,所以对于服务端来说没有连接,并且其会在调用 Recvfrom() 方法后就收客户端的请求,并一直阻塞,直到收到信息

Socket TCP 是如何建立连接的

在 Socket 绑定完服务器的地址后,就开始和服务器建立连接了,TCP 建立连接的方式其实就是大名鼎鼎三次握手了

image.png
  • 第一次握手:A 的 TCP 进程创建一个 传输控制块 TCB ,然后向 B 发出连接请求报文段。之后将同步位 SYN 设置为 1,同时选择一个初始序列号 seq=x,这时客户端 A 进入到 SYN-SENT(同步已发送)状态。
  • 第二次握手:B 收到连接请求报文段,如果同意建立连接,则向 A 发送确认。在确认报文段中 同步位 SYN=1、确认位 ACK=1、确认号 ack=x+1,同时也为自己选择一个初始序列号 seq=y,这时服务器 B 进入 SYN-RCVID 状态。
  • 第三次握手:A 收到 B 的确认以后,再向 B 发出确认。确认报文 ACK=1、确认号ack=y+1。这时A进入到 ESTAB-LISHED 状态。当B接收到A的确认后,也进入 ESTAB-LISHED 状态。连接建立完成

三次握手发生在 socket 的哪几个函数中

image.png
  • 当客户端调用 connect 时,触发了连接请求,向服务器发送了SYN 信号,这时 connect 进入阻塞状态;
  • 服务器监听到连接请求,即收到 SYN,调用 accept 函数接收,进入阻塞状态,在此之前会尽力 socket、bind、listen 函数;然后返回相关的 syn 以及 ack 信号
  • 客户端接受到服务端的信息,此时 connect 完成,解除阻塞状态,并且向服务端发送 ack 信号
  • 服务端收到 ack, accept 阻塞解除,完成连接

在建立连接之后,connect() 就已经执行完毕了,服务端就可以向客户端发送数据了。

Socket TCP 是如何断开连接的

image.png
  • 第一次挥手:A 先发送连接释放报文段,段首部的终止控制位 FIN=1,序号seq=u(等于A前面发送数据的最后一个序号加1);然后 A 进入 FIN-WAIT-1(终止等待1)状态,等待 B 的确认。

  • 第二次挥手:B 收到 A 的连接释放报文段后,立刻发出确认报文段,确认号 ack=u+1,序号 seq=v(等于 B 前面发送数据的最后一个序号加1);然后 B 进入 CLOSE-WAIT(关闭等待)状态。

  • 第三次挥手:A 收到 B 的确认报文段后进入到 FIN-WAIT-2(终止等待2)状态,继续等待 B 发出连接释放报文段;

    • 若 B 已经没有数据要发送,B 就会向 A 发送连接释放报文段,段首部的终止控制位 FIN=1,序号 seq=w(半关闭状态可能又发送了一些数据),确认号 ack=u+1,这时B进入 LAST-ACK(最后确认)状态,等待A的确认。
  • 第四次挥手:A收到B的连接释放报文段并发出确认,确认段中 确认位 ACK=1,确认号 ack=w+1,序号 seq=u+1;然后 A 进入到TIME-WAIT(时间等待)状态。当 B 再接收到该确认段后,B 就进入 CLOSED状态。

第四次挥手后为何要等待 2MSL

首先 2MSL 的时间是从客户端(A)接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端(A)的 ACK 没有传输到服务端(B),客户端(A)又接收到了服务端(B)重发的 FIN 报文,那么 2MSL 时间会被重置。等待 2MSL 原因如下

  • 1.得原来连接的数据包消失

    • 如果B没有收到自己的ACK,会超时重传FiN那么A再次接到重传的FIN,会再次发送ACK
    • 如果B收到自己的ACK,也不会再发任何消息

在最后一次挥手后 A 并不知道 B 是否接到自己的信息, 包括 ACK 是以上哪两种情况,A 都需要等待,要取这「两种情况等待时间的最大值,以应对最坏的情况发生」,这个最坏情况是:去向ACK消息最大存活时间(MSL) + 来向FIN消息的最大存活时间(MSL)。这刚好是2MSL,这个时间,足以使得原来连接的数据包在网络中消失。

  • 2.保证 ACK 能被服务端接收到从而正确关闭链接

因为这个 ACK 是有可能丢失的,会导致服务器收不到对 FIN-ACK 确认报文。假设客户端不等待 2MSL ,而是在发送完 ACK 之后直接释放关闭,一但这个 ACK 丢失的话,服务器就无法正常的进入关闭连接状态。

有关这次 moon 要把 socket 玩的明明白白的更多相关文章

  1. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  2. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  3. 已解决socket.timeout : The read operation timed out - 2

    已解决(pip安装模块超时,利用四种国内镜像源完美解决)WARENTING:Retrying(Retry(total=4,connect=None,read=None,redirect=None,status=None))afterconnectionbrokenby‘ConnectTimeoutError(pip._vendor.urllib3.connection.HTTPSConnectionobjectatOx00001D6OE4F4A940>,‘Connectiontopypi.orgtimedout.(connecttimeout=15)’)’':/simple/pip/socke

  4. ruby - Cucumber 测试无法启动,错误为 "Display socket is taken but lock file is missing.." - 2

    运行cucumber后bundleexeccucumberfeatures/emails.feature:20我遇到了错误Displaysocketistakenbutlockfileismissing-checktheHeadlesstroubleshootingguide(Headless::Exception)/Users/me/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/headless-2.2.0/lib/headless.rb:195:inensure_xvfb_is_running'/Users/me/.rbenv/ver

  5. ruby - Interface Builder 看不到 MacRuby 的 socket - 2

    我正在尝试使用XCode和InterfaceBuilder构建一个基本的helloworld应用程序。但是,在InterfaceBuilder中,我看不到我的socket可以连接起来。我转到对象检查器Pane的连接选项卡,它显示“NewReferencingOutlet”。我想知道我的代码是否有误。在这里classHelloWorldControllerattr_accessor:hello_label,:hello_button,:hellodefawakeFromNib@hello=trueenddefchangeLabel(sender)if@hello@hello_label.

  6. ruby - yarn 未初始化常量 Socket::SOL_TCP - 2

    我在这里尝试使用yarn,遇到了一个可能与ruby​​相关的问题。在执行任何yarn命令,我收到错误.../.rvm/gems/ruby-2.3.0/gems/yarn-0.1.1/lib/yarn/server.rb:14:in':uninitializedconstantSocket::SOL_TCP(NameError)错误堆栈:$yarn.../.rvm/gems/ruby-2.3.0/gems/yarn-0.1.1/lib/yarn/server.rb:14:in':uninitializedconstantSocket::SOL_TCP(NameError)Didyoume

  7. ruby-on-rails - 有必要把Unicorn放在Nginx(或Apache)后面吗 - 2

    我对这个架构有点困惑。在我正在进行的一个项目中,Unicorn被选为Rails服务器。它放在Nginx网络服务器后面。据我了解,Unicorn是功能齐全的Web服务器,我们不打算在同一服务器实例上托管任何其他Rails应用程序。所以我的问题是:在链中添加附加层有什么好处:client->nginx->unicorn->unicornworker 最佳答案 Unicorn不是为处理“慢客户端”而设计的。您可以在PHILOSOPHY中阅读更多相关信息帮助文件:Mostbenchmarkswe’veseendon’ttellyouthis

  8. 多线程问题:为什么不应该使用多线程读写同一个socket连接? - 2

    问题的产生经典的单reactor多线程模式采用的是用主线程处理连接事件以及socket读写事件,业务逻辑的处理则是让线程池里的线程各自竞争处理。既然多线程这么方便,为什么不让线程池里的线程也参与到read和send这个过程中呢?在发送数据的过程中,即使TCP的发送缓存满了,我们也可以记录下当前成功发送了多少字节,然后再次注册一个EPOLLOUT事件,只需等待下次可写事件,继续让子线程发送数据即可,岂不是美哉?解释陈硕大佬的解释对于TCP,通常多线程读写同一个socket是错误的设计,因为有shortwrite的可能。假如你加锁,而又发生shortwrite,你是不是要一直等到整条消息发送完才解

  9. ruby-on-rails - postgresql 数据库错误 : Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"? - 2

    当我运行rakedb:migrate或运行railss命令时,我得到同样的错误:Error:couldnotconnecttoserver:NosuchfileordirectoryIstheserverrunninglocallyandacceptingconnectionsonUnixdomainsocket"/var/run/postgresql/.s.PGSQL.5432"?当我尝试railss时,浏览器出现错误。这是我的database.ymldefault:&defaultadapter:postgresqlencoding:unicodepool:5development

  10. ruby-on-rails - 为什么我会出现间歇性的 Excon::Error::Socket: getaddrinfo: No address associated with hostname (SocketError)? - 2

    Rails4-Ruby2.2.2-亚马逊AWSS3-蜻蜓1.0.12-dragonfly-s3_data_store1.2-fog-aws0.10.0大约99%的时间我们没有问题。这个问题通常只发生在使用率很高的时候,但我注意到它也发生在几乎没有用户的时候。抛出错误的行:#excon/lib/excon/socket.rb#line100insidetheconnectionmethod.addrinfo=::Socket.getaddrinfo(*args)该错误在应用程序中无处不在。有时在没有远程连接时会出现错误。-我无法再验证这一点。我使用Rails记录器来捕获传入的参数,通过和

随机推荐