当前位置: 首页 > Web前端 > HTML

原来这就是Socket!

时间:2023-03-29 12:11:24 HTML

对Socket的理解大致分为以下几个主题,什么是Socket,Socket是如何创建的,Socket是如何连接和收发数据的,Socket的删除等等。什么是Socket以及创建过程?数据包由应用程序产生,进入协议栈,对各种报头进行封装。然后操作系统调用网卡驱动程序指挥硬件,将数据发送给对端主机。整个过程的大致示意图如下。我们都知道,协议栈其实就是位于操作系统中的一些协议的栈,这些协议包括TCP、UDP、ARP、ICMP、IP等,通常一个协议就是为了解决某些问题而设计的。例如,TCP旨在安全可靠地传输数据。UDP专为小数据包和高传输效率而设计。ARP旨在通过IP地址查询物理(Mac)数据。地址,ICMP是为了给主机返回错误信息而设计的,IP是为了实现大型主机的互联互通而设计的。浏览器、电子邮件、文件传输服务器等应用程序产生的数据,都会通过传输层协议进行传输,应用程序不会直接与传输层建立联系,但在应用层与传输层之间的套件,这个套件就是Socket。上图中,应用程序包括Socket和解析器。解析器的作用是向DNS服务器发起查询,查询目标IP地址。应用程序下面是操作系统的内部,其中包括协议栈,是一系列协议的栈。操作系统下面是网卡驱动程序。网卡驱动负责控制网卡硬件,驱动驱动网卡硬件完成发送和接收工作。在操作系统内部,有一个存储空间用于存储控制信息,这个存储空间记录了控制通信的控制信息。其实这些控制信息就是Socket的实体,或者说存放控制信息的内存空间就是socket的实体。在这里你可能不知道为什么,所以我使用netstat命令来告诉你socket是什么。我们在Windows命令提示符下输入netstat-ano#netstat是用来显示socket内容的,-ano是可选选项#a不仅显示正在通信的socket,还显示所有还没有开始通信的socketSocket#n显示IP地址和端口号#o显示socket的程序PID我的电脑会显示如下结果。图中每一行相当于一个socket,每一列又称为一个元组,所以一个socket是一个五元组(协议、本地地址、外部地址、状态、PID)。有时也称为四联体,四联体不包括协议。比如图中的第一行,它的协议是TCP,本地地址和远程地址都是0.0.0.0。还不知道,此时的状态是LISTENING,LISTENING表示应用已经打开,正在等待与远程主机建立连接(各种状态之间的转换,可以看作者的这篇TCP文章哦,终于来了!!)最后一个元组是PID,进程标识,PID就像我们的身份证号,可以精确定位一个唯一的进程。现在你可能对Socket有了基本的了解,现在喝杯酒,休息一下,让我们继续探索Socket。现在有个问题,Socket是怎么创建的?套接字是用应用程序创建的。应用程序中有一个套接字组件。应用程序启动时会调用socket应用程序创建socket,协议栈会根据应用程序application创建socket:首先分配一个socket需要的内存空间,这一步相当于准备一个容器用于控制信息,只是容器没有实际作用,所以还需要把控制信息放到容器中;如果你不申请创建socket所需的内存空间,你创建的控制信息就不会被存储,所以分配内存空间和放入控制信息缺一不可。至此socket的创建已经完成。socket创建后,会返回一个socket描述符给应用程序,相当于一个号码牌,用来区分不同的socket。根据这个描述符,应用程序在委托协议栈收发数据时需要提供这个描述符。套接字连接套接字创建后,仍然用于数据的发送和接收。在数据发送和接收之前,需要一个connect的步骤,也就是建立连接的过程。这种连接并不是真正的连接:它是两台计算机之间插入的水管。它是应用程序通过网络介质通过TCP/IP协议标准从一台主机传输到另一台主机的过程。socket刚创建后,还没有数据,不知道通信对象。在这种状态下,即使你让客户端应用委托给协议栈发送数据,它也不知道往哪里发送。因此,浏览器需要根据URL查询服务器的IP地址。完成这项工作的协议是DNS。查询目标主机后,它会告诉协议栈目标主机的IP。至此,客户端就准备好了。在服务端,和客户端一样需要创建一个socket,但是它也不知道通信对象是谁,所以我们需要让客户端通知服务端客户端的必要信息:IP地址和端口号.现在通信双方建立连接的必要信息都已经有了,只欠一个股东。通信双方收到数据后,需要一个位置来存储数据。这个位置就是缓冲区,它是内存的一部分。有了缓冲区,就可以发送和接收数据。OK,现在客户端要向服务端发送一条数据,应该怎么办呢?首先,客户端应用程序需要调用Socket库中的connect方法,提供套接字描述符以及服务器IP地址和端口号。connect(<描述符>,<服务器IP地址和端口号>)这些信息会传递给协议栈中的TCP模块,TCP模块将请求报文进行封装,然后传递给IP模块进行IP包头封装,然后传递给物理层,帧头封装,然后通过网络介质传递给服务器,服务器会分析帧头,IP模块,TCP模块头找到对应的socket,socket字接收后请求,它会写入相应的信息,并将状态更改为正在连接。请求过程完成后,服务器的TCP模块将返回响应。这个过程和客户端是一样的(如果不清楚包头的封装过程,可以看作者的文章TCP/IP基础知识总结)在一个完整的请求和响应过程中,控制信息起着非常关键的作用(具体作用后面会讲到)。SYN是同步的缩写。客户端会先发送一个SYN包请求服务器建立连接。ACK对应的意思,是对发送SYN包的回应。FIN表示已终止,表示客户端/服务器要终止连接。由于网络环境复杂多变,数据包经常丢失。所以在通信的时候,双方需要确认对方的数据包是否已经到达,判断标准就是ACK的值。(通信双方建立连接都会经过三次握手的过程,关于三次握手的详细介绍可以看作者的文章TCP基础。)当建立连接的所有消息可以正常收发,socket已经进入可以收发了。这时候就可以认为两个socket通过一个管理连接起来了。当然,管子实际上并不存在。连接建立后,协议栈的连接操作就结束了,也就是说connect已经执行完毕,控制流还给应用程序。发送和接收数据当控制流从connect返回到应用程序时,会直接进入数据发送和接收阶段。数据发送和接收操作在应用程序调用write将要发送的数据交给协议栈时开始,协议栈接收到数据后执行。发送操作。协议栈并不关心应用程序传输的是什么数据,因为这些数据最终都会被转换成二进制序列。协议栈收到数据后不会立即发送数据,而是将数据放入发送缓冲区,然后等待应用程序发送下一条数据。为什么接收到的数据包不直接发送,而是放在缓冲区中?因为只要数据一收到就发送,就有可能发送大量的小数据包,导致网络效率下降。因此,协议栈需要将数据积累到一定数量后才发送出去。至于协议栈将多少数据放入缓冲区,不同版本和类型的操作系统有不同的看法。但是,所有的操作系??统和类型都会遵循以下标准:第一个判断因素是每个网络数据包所能容纳的数据长度是由MTU来判断的,它代表了一个网络数据包的最大长度。最大长度包括了header,所以如果只谈数据区,就会用MTU——header的长度,得到的最大数据长度称为MSS。另一个判断标准是时间。当应用程序产生的数据比较少,协议栈将数据放入缓冲区的效率不高时,如果每次都发送MSS,可能会因为等待时间过长而造成延迟。在这种情况下,即使数据长度没有达到MSS,也应该将数据发送出去。协议栈并没有告诉我们如何平衡这两个因素。如果优先考虑数据长度,那么效率可能会比较低;如果时间优先,会降低网络的效率。一段时间过去了。.....假设我们使用的是有限长度规则,那么缓冲区是满的,协议栈即将发送数据。协议栈只想发送数据,却发现自己一次无法(相对)发送这么大量的数据。我应该怎么办?在这种情况下,发送缓冲区中的数据会超过MSS的长度,发送缓冲区中的数据会根据MSS的大小被分割成一个包,每个分割后的数据会加上TCP、IP,以太网报头,然后将其放入单独的网络数据包中。至此,网络包已经准备好发送给服务器,但是数据发送操作还没有完成,因为服务器还没有确认是否收到了网络包。因此,客户端发送数据包后,需要服务端进行确认。TCP模块在拆分数据时,会计算网络包的偏移量。这个偏移量是从数据开始算起的字节数,把算出的字节数写到TCP头中。TCP模块还会生成网络数据包的序列号(SYN)。这个序列号是唯一的,这个序列号是服务器用来确认的。服务器将确认客户端发送的数据包。服务器端确认无误后,会生成一个序列号和一个确认号(ACK),一起发送给客户端。客户端确认后,会向服务器发送确认号。让我们来看看实际的工作过程。首先,客户端在连接时需要计算序号的初始值,并将这个值发送给服务器。接下来,服务端通过这个初始值计算出确认号,返回给客户端。初始值在通信过程中可能会被丢弃,所以当服务端收到初始值时,需要返回一个确认号进行确认。同时,服务端还需要计算从服务端到客户端的序列号的初始值,并将这个值发送给客户端。然后,客户端还需要根据服务器发送的初始值计算确认数,并发送给服务器。至此,连接建立,接下来就可以进入数据收发阶段了。在数据发送和接收阶段,通信双方可以同时发送请求和响应,也可以同时确认请求。请求-确认机制非常强大。通过这种机制,我们可以确认接收方是否收到了某个包,如果没有收到则重新发送。这样,我们可以立即发现网络中的任何错误并进行修复。网卡、集线器和路由器没有错误恢复机制。一旦检测到错误,数据包将被直接丢弃,应用程序没有这种机制。只有TCP/IP模块可以工作。由于网络环境复杂多变,数据包会丢失,所以发送序号和确认号有一定的规则。TCP将通过windows管理确认号。我们不会在本文中重复它们。可以看看作者TCP基础的这篇文章一探究竟。断开连接当通信双方不再需要发送和接收数据时,就需要断开连接。不同的应用程序在不同的时间断开连接。以Web为例,浏览器向Web服务器发送请求消息,Web服务器返回响应消息。这个时候所有的发送和接收数据就结束了。服务端可能先发起断开连接响应,当然客户端也可能先发起断开连接(Disconnection是应用程序做出的判断),与协议栈无关。无论哪一方发起断开连接请求,都会调用Socket库的close程序。我们以服务器断开连接为例。当服务器发起断开连接请求时,协议栈会生成断开连接的TCP头。其实就是设置FIN位,然后委托IP模块向客户端发送数据。同时,服务器的socket会记录连接断开的信息。客户端协议栈收到服务器的FIN请求后,会将socket标记为断开连接,然后客户端返回一个确认号给服务器,这是断开连接的第一步。之后,应用程序还调用read来读取数据。服务器发送数据后,协议栈会通知客户端应用数据已经收到。只要服务器端返回的所有数据都收到了,客户端就会调用close程序结束发送和接收操作。这时客户端会生成一个FIN发送给服务端。一段时间后,服务器会返回ACK号。至此,客户端与服务器端的通信就完成了。删除套接字通信完成后,用于通信的套接字将不再使用,此时我们可以删除这个套接字。但是,此时socket并不会立即删除,而是过一会再删除。等待这段时间是为了防止误操作。最常见的误操作是客户端返回的确认号丢失。至于要等多久,跟数据包重传的方式有关。