对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,也应该将数据发送出去。协议栈并没有告诉我们如何平衡这两个因素。如果优先考虑数据长度,那么效率可能会比较低;如果时间优先,会降低网络的效率。一段时间过去了。.....
