3.网络I/O的工作机制
前言:数据从一台主机(服务端)发送到网络中的另一台主机(客户端)需要经过很多步骤:首先需要有相互沟通的意向。其次要有能够沟通的物理渠道(物理链路):是通过电话,还是直接面对面交流。再次,双方见面时语言要能够交流,而且双方说话步调要一致,明白什么时候该自己说话,什么时候该对方说话(通信协议)。所以下面要讲一下通信协议和如何完成数据传输。
什么是通信协议:通信协议其实就是一种规则和约定。就是两者想要交流必须要知道那种方式交流,什么时候去交流,交流什么内容,这就是通信协议。
什么是socket:socket就是网络中的一个端口,两个程序想要进行数据交互,至少要有一个socket(端口),http协议可以比作一个轿车,而socket可以比作是轿车的发动机,作用是比较重要的。
什么是SYN:是TCP和IP建立连接的一个握手信号。 当客户端和服务端建立正常的TCP网络连接时, 首先客户端会先向服务端发送一个SYN, 然后服务端会使用SYN+ACK接收这个消息, 然后客户端接收到服务器端的SYN+ACK最后客户端在使用ACK接收响应。
什么是ACK:ACK是一个确认字符,确认发过来的数据接收无误。 可以理解SYN是TCP和IP之间发送消息要使用的,可以理解ACK是TCP和IP之间接收响应要使用的。
什么是FIN: FIN可以说是用来关闭连接的。
什么是IP: IP就是你电脑的IP地址,当你和TCP通信是TCP需要知道通信的IP。
3.1.TCP状态转化 (TCP就是一个面向连接的,基于字节流的传输层通信协议)
先看一下如何建立和关闭一个TCP连接,TCP状态转换图(这个图太难画就不画了,这个图和书上的图意思一样。):
图解:
closed(CLOSED):是TCP状态转换的起始点,在连接超时或者连接关闭时会进入此状态。 为什么closed是起始点呢,因为一开始不可能你就处于连接的状态,在没连接的状态或超时状态就是进入closed,等你连接时就会走listen的状态。
listen(LISTEN):服务端在等待连接时的状态,当服务端调用socket,bind,listen函数,会进入此状态,此状态是被动打开的,也就是客户端连接时进入此状态。
syn-sent(SYN-SENT):这个状态是由closed主动打开的,当客户端发送syn给服务端,服务端不能正确连接,则直接进入closed状态。
syn-rcvd(SYN-RCVD):服务端接收客户端的syn请求,服务端由listen状态进入该状态。同时服务端要回应一个ack,发送一个syn给客户端;另一种状态是:客户端在发送syn的同时接收到了服务端的syn请求,客户端会由syn-sent转换到该状态。
established(ESTABLISHED):服务器端和客户端完成3次握手后进入该状态,说明已经可以传输数据了。
fin-wait_1(FIN-WAIT_1):主动关闭的一方,由established状态进入此状态。具体动作是发送fin(关闭连接)给对方。
fin-wait_2(FIN-WAIT_2):主动关闭的一方,接收到对方的fin ack(关闭连接和响应),进入此状态。由此不能再接收对方的数据,但是能够向对方发送数据。
close-wait(CLOSE-WAIT):接收到fin以后,被动关闭(被动关闭连接)的一方进入此状态。具体动作是在接收到fin的同时发生ack。
last-ack(LAST-ACK):被动关闭(被动关闭连接)的一方,发起关闭请求,由close-wait状态进入此状态。具体动作是发生fin给对方,同时在接收到ack时进入closed状态(就是通信结束以后进入无连接的状态。)。
closing(CLOSING):两边同时发起关闭请求时,会由fin-wait_1进入此状态,具体动作是接收到fin请求,同时响应一个ack。
time-wait(TIME-WAIT):这个状态比较复杂,但也是我们最常见的一个连接状态,有三个状态可以转化为此状态。
由fin-wait_2转换到time-wait,具体情况是:在双方不同时发起fin的情况下,主动关闭的一方在完成自身发起的关闭请求后,接收到被动关闭一方的fin后进入此状态。
由closing转换到time-wait,具体情况是:在双方同时发起关闭,都做了发起fin的请求,同时接收到了fin并做了ack的情况下,这时就由closing状态进入time-wait状态。
由fin-wait_1转换到time-wait,具体情况是:同时接收到fin(对方发起)和ack(本身发起的fin回应),它与closing转换到time-wait的区别在于本身发起的fin回应的ack先于对方的fin请求到达,而由closing转换到time-wait则是fin先到达。
3.2.影响网络传输的因素
将一份数据从一个地方正确的传输到另一个地方所需要的时间我们称之为响应时间,影响这个响应时间的因素有很多。
1.网络带宽:带宽和宽带不一样,但是却差不多,这个带宽的速度回影响到响应时间的速度,一般平均网络带宽只有1.7Mb/s左右。
2.传输距离:传输数据,也就是数据在光纤中要走的距离,光的传播速度很快,但也是有时间的,而且光纤不是直线走的,也有折射率,而且传输也有传输延迟这种问题。
3.TCP拥塞控制:TCP传输是一个“停-等-停-等”的协议,传输方和接受方的步调要一致,要达到步调一致就要通过拥塞控制来调节。TCP在传输时会设定一个窗口,这个窗口的大小是由带宽和RTT决定的。计算的公式是带宽xRTT。通过这个值可以得出理论上最优的TCP缓冲区的大小。
3.3.Java Socket的工作机制
socket这个概念没有对应到一个具体的实体,它描述计算机之间完成相互通信的一种抽象功能。打个比方,可以把socket比作两个城市之间的交通工具,有了它,就可以在城市之间来回穿梭了。交通工具有多种,每种交通工具也有相应的交通规则。socket也一样,也有多种。大部分情况下我们使用的都是基于TCP/IP的流套接字,它是一种稳定的通信协议。
socket通信示例图:
主机A的应用程序要能和主机B的应用程序通信,必须通过Socket建立连接,而建立Socket连接必须由底层TCP/IP来建立。建立TCP需要底层IP来寻址网络中的主机。网络层使用的IP可以帮助我们根据IP地质来找到目标主机,但是在一台主机上可能运行着多个程序,如何才能与指定的应用程序通信就要通过TCP或UPD的地址也就是端口号来指定。这样就可以通过一个socket实例来唯一代表一个主机上的应用程序的通信链路了。
3.4.建立通信链路
当客户端要与服务端通信时,客户端首先要创建一个socket实例,操作系统将为这个socket实例分配一个没有被使用的本地端口号,并创建一个包含本地地址、远程地址和端口号的套接字数据结构,这个数据结构将一直保存在系统中知道这个连接关闭。在创建socket实例的构造函数正确返回之前,将要进行TCP的三次握手协议,TCP握手协议完成后,socket实例对象将创建完成,否则将抛出IOException错误。
与之对应的服务端将创建一个serverSocket实例,创建serverSocket比较简单,只要指定的端口号没有被占用,一般实例创建都会成功。同时操作系统也会为serverSocket实例创建一个底层数据结构,在这个数据结构中包含指定监听的端口号和包含监听地址的通配符,通常情况下都是“*”一个星号,即监听所有地址。之后当调用accept()方法时,将进入堵塞状态,等待客户端的请求。当一个新的请求到来时,将为这个连接创建一个新的套接字数据结构,改套接字数据的信息包含的地址和端口信息正是请求源地址和端口。这个新创建的数据结构将会关联到serverSocket实例的一个未完成的连接数据结构列表中。注意,这时服务端的与之对应的socket实例并没有完成创建,而要等到与客户端的3此握手完成后,这个服务端的socket实例才会返回,并将这个socket实例对应的数据结构从未完成列表中移到已完成列表中。所以与serverSocket所关联的列表中每个数据结构都代表与一个客户端建立的TCP连接。
3.5数据传输
传输数据是我们建立连接的主要目的,下面说一下如何通过socket传输数据。
当连接已经建立成功时,服务端和客户端都会拥有一个socket实例,每个socket实例都有一个inputStream和OutPutStream,并通过这两个对象来交互数据,同时我们也知道网络I/O都是以字节流传输的,当创建SOcket对象时,操作系统将会为inputStream和outputStream分别分配一定大小的缓存区,数据的写入和读取都是通过这个缓存区完成的,写入端将数据写到outputStream对应的SendQ队列中,当队列填满时,数据将被转移到另一端InputStream的RecvQ队列中,如果这时RecvQ已经满了,nameOutputStream的write方法将会堵塞,直到RecvQ队列有足够的空间容纳SendQ发送的数据。特别值得注意的是,这个缓存区的大小及写入端的速度和读取端的速度非常影响这个链接的数据传输效率,由于可能会发送阻塞,所以网络I/O与磁盘I/O不同的是数据的写入和读取还要有一个协调的过程,如果在两边同时传送数据可能会产生死锁。
妈耶终于写完了,后面这些理论没做太多的深入,几天后再过一遍。
原文地址:https://www.cnblogs.com/java-263/p/10293847.html