网络篇-传输控制协议TCP

TCP协议

传输控制协议(TCP,Transmission Control Protocol)用一句话概括的话,它是一种面向连接的可靠的基于字节流传输层通信协议。

TCP(传输层)是位于网络层之上,应用层之下的中间层,不同的主机的应用层之间进程需要可靠的,像管道一样的连接,但是IP层不提供这种流机制,而提供的是不可靠的包交换。

记录一个写的非常详细的OSI七层模型:OSI七层模型详解_小鹏_加油的博客-CSDN博客_osi七层模型

 OSI每一层的作用

TCP协议特点

1、面向连接:通信之前建立连接,通信结束断开连接

2、每一条TCP连接只能是点对点的(一对一)

3、提供了可靠的交付服务,通过TCP连接传输的数据,无差错,不丢失,不重复

4、提供全双工通信

5、面向字节流,程序虽然和TCP交互是一次一个数据段,但是接收到的数据看成是连成一串无结构的字节流

6、TCP首部20字节

TCP编程

ServerSocket面向TCP的通信,主要是用来接收accept客户端的连接的socket

Socket:面向TCP的通信,主要是客户端来连接服务端(Connection)

服务端:

package TCP;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Description :
 * Created by Resumebb
 * Date :2022/3/1
 */
public class Server {
    public static void main(String[] args) {
        ServerSocket socket = null;
        BufferedReader reader = null;
        try {
            /*
            1.创建ServerSocket对象,初始化端口号
            2.监听该端口号,等待客户端连接,如果有连接,通过TCP的三次握手建立连接
            3.获取连接对象的输入流
            4.读取客户端的输入信息(输入exit结束进程)
            5.关闭资源
             */
            socket = new ServerSocket(1234);
            System.out.println("服务器已启动,正在监听端口1234...");
            Socket socket1 = socket.accept();
            System.out.println("连接已建立");
            reader = new BufferedReader(new InputStreamReader(socket1.getInputStream()));
            while(true){
                String msg = reader.readLine();
                System.out.println("来自客户端:"+socket1.getRemoteSocketAddress()+"的数据,内容为:"+msg);
                if("exit".equals(msg)){
                    break;
                }
            }
            socket.close();
            socket1.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端

package TCP;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * Description :
 * Created by Resumebb
 * Date :2022/3/1
 */
public class Client {
    public static void main(String[] args) {
        /*
        1.创建Socket实例
        2.连接服务器,TCP中服务器发起请求连接
        3.获取控制台输入
        4.获取连接服务器的输出流
         */
        Socket socket = null;
        try {
//            socket.connect(new InetSocketAddress("127.0.0.1", 1234));
            socket = new Socket("127.0.0.1", 1234);
            OutputStream os = socket.getOutputStream();
            BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
            while(true){
                System.out.println("输入:");
                String msg = buffer.readLine();
                if(msg == null || "".equals(msg.trim()))
                    continue;
                os.write((msg+"\n").getBytes());
                os.flush();
                if("exit".equals(msg)){
                    break;
                }
            }
            socket.close();
            os.close();
            buffer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

TCP报文格式

TCP首部的报文格式以及各个字段的意思

 报文结构

16位的源端口:

端口号是用来表示特定主机上的唯一进程,源端口就是发送主机上应用占用的端口。

IP地址是在传输层用来标识网络中不同主机端口则标识主机上的唯一应用,socket套接字就是IP地址加端口号共同组成

16位的目的端口:

端口号是用来表示特定主机上的唯一进程,目的端口就是接收主机上应用占用的端口,目的端口占用16位,共2^16个

32位的序号

表示这个报文段中第一个数据字节序号,如果将字节流看做是两个应用程序间的单向流动,则TCP用序号对每个字节进行计数,用来保证到达数据顺序的编号,所以这个字段需要比较大的存储

32位的确认号

确认序号是下一个期望接收的TCP的分段号,相当于是对对方所发送的并且已经被本方所正确接收的分段的确认,井道ACK标志=1时有效,确认号表示期望下一个字节的序号

4位数据偏移(报文首部)

以32位(4个字节)字长为单位,在不存在可选字段时报头长度是20字节,可选字段的长度可变的,以4字节为一个字长,2^4=15即最大是60个字节,

6位保留位

6位,必须为0

标志位

标志位占有6个比特位,有6个标志位,每个标志位值是0或1两种情况,1表示该标志位有效,依次为:URG、ACK、PSH、RST、SYN、FIN

六个标志位的含义:

URG:该位为1说明表示TCP包的紧急指针域有效,用来保证TCP连接不被中断,并督促上层应用赶快处理这些数据

ACK:该位置为1表示当前包是确认包,确认号有效,一般发送方发送数据给接收方,接收方要告诉发送方已接收到数据,此时接收方就会给定一个确认,即将ACK置为1,且填充确认号表明下一个要接受的数据序号

PSH:接收方应尽快将这个报文交给应用层,即Push,push操作就是在数据包到达接收端以后,立即创送给应用层,而不是在缓冲区排队

RST:连接复位,复位因主机奔溃或者其他原因而出现了连接出错,也可以用拒绝非法的分段或者拒绝连接请求

SYN:是一个同步的序号,通常和ACK合用来建立连接,也就是常说的三次握手

FIN:在连接结束后需要断开连接,这个字段表示发送方已经到达数据末尾,及即双方数据传输完成,将标志位置为1,连接将被断开,将开始四次挥手断开连接过程

16位窗口

TC的流量控制由连接的每一端通过声明窗口的大小来提供,窗口的大小为字节数,起始与确认序号字段确认的值,这个值是接收端期望接收的字节,是一个16个bit字段,窗口的大小最大65565字节

16位校验和

用于对分段的首部和数据进行校验,正常情况下一般为0,用于传输层差错校验

16位紧急指针:

只有当标志位URG置为1时有效,紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急指针的最后一个字节的序号,TCP的紧急方式是发送端向另一端发送紧急数据的一种方式

三次握手

TCP是面向连接的协议,面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接。这一过程与打电话很相似,先拨号振铃,等待对方摘机说 “喂”,然后才说明是谁。因此每个TCP连接都有3个阶段: 连接建立,数据传输和连接释放。
数据传输之前的连接建立过程就是三次握手状态转换

第一次握手

客户端发送连接请求报文到服务器端,客户端进入SYN-SENT状态,等待客户端的确认

第一次数据包内容(SYN=1,seq=x)

第二次握手

服务端接收到连接请求报文,如果同意连接,向客户端发回确认报文段,同时自己也发送一个SYN包(SYN=1),服务端的状态由LISTEN(监听)状态进入SYN-RECV(确认收到同步帧)状态,同时确认帧ACK置为1

第二次数据报文首部(SYN=1,ACK=1,ack=x+1,seq=y)

第三次握手

客户端接收到服务器的确认报文后,在向服务端给出确认报文,此时客户端和服务端在收到第三次消息后都进入到ESTAB-LISHED(连接建立)状态,该状态表示是全双工通信,就可以正常通信。

第三次数据报文首部(ACK=1,ack=y+1,seq=x+1)

四次挥手

在传输数据结束后需要进行断开连接,断开连接需要进行4次数据网络传输,即4次挥手

注意:MSL:最大生存时间 ,任何一个报文段在网络中都具有生存时间,在发送方给定MSL,在网络中每进入一个路由MSL-1,当msl=0是还没有到达目的地就会丢弃

第一次挥手:

客户端主动发起断开连接操作,用来关闭客户端到服务端对额数据传输

客户端发起第一次连接数据报文段后,状态由ESTAB-LISTHED状态进入FIN-WAIT-1状态,

第一次数据包首部信息(FIN=1,seq=u)

第二次挥手:

服务端响应客户端的请求确认消息,就进行第二次数据包发送

服务端发起第二次数据报文段后,状态由ESTAB-LISTHED状态CLOASE-WAIT状态,

第二次数据包首部信息(ACK=1,seq=v,ack=u+1)

客户端接收到消息后状态就由FIN-WAIT-1状态进入FIN-WAIT-2状态,此时客户端无法往服务端发送消息,但服务端可以继续给客户端单向发送数据,处于单双工状态

第三次挥手:

服务端关闭客户端的连接,发送一个FIN给客户端

服务端发起第三次数据报文段后,服务端状态由CLOASE-WAIT状态进入到LAST-ACK状态

第三次数据报文段首部信息(FIN=1,ACK=1,ack=u+1,seq=w)

第四次挥手:

客户端发回ACK报文确认

在客户端发起第四次数据报文段后,客户端的状态FIN-WAIT-2状态进入TIME-WAIT状态,注意;并没有直接进入close状态,而是在等待2MSL时间后自动进入close状态

服务端接收到第四次数报文后状态,由LAST-ACK状态直接进入close状态

第四次数据报文段首部信息为(ACK=1,seq=u+1,ack=w+1)

TCP扩展问题

TCP能改成两次握手吗?

不能,防止已经失效的连接请求有传送到服务端,因而产生错误,三次握手的最主要的目的是保证连接是双工的。

为什么需要TIME-WAIT状态?

TIME-WAIT是需要等待2MSL

1、为实现TCP这种全双工的连接的可靠的释放

这样可以让TCP再次发送的最后的ACK包防止这个ACK丢失,这种2msl等待的另一个结果是这个TCO连接在2msl等待期间,连接的端口是不能再被使用,只能在2msl结束后才能使用

2、为使旧的数据包在网络因过期而消失

每个TCP报文段都有最大生存时间:MSL,任何报文段被丢弃前在网络中的最长的时间

TCP如何发送数据

消息是由发送方产生,发送方会首先将数据放入到发送缓冲区,然后发送的时候会从缓冲区中取,接着消息从发送方的用户空间传入内核空间借助于网络传输介质完成传输,消息在发送到接收方的内核空间,接收方如果想要读取是从内容空间读取到用户空间,接收方会将接收到的数据放入接收缓冲区,让后应用程序使用的时候到缓冲区去取

TCP发送数据:

因为TCP本身传输的数据包大小就有限制,所以应用发出的消息包过大,TCP会把应用消息包拆分为多个TCP数据包发送出去。Negal算法的优化,当应用发送数据包太小,TCP为了减少网络请求次数的开销,它会等待多个消息包一起,打成一个TCP数据包一次发送出去。

TCP接收数据:

因为TCP缓冲区里的数据都是字符流的形式,没有明确的边界,因为数据没边界,所以应用从TCP缓冲区中读取数据时就没办法指定一个或几个消息一起读,而只能选择一次读取多大的数据流,而这个数据流中就可能包含着某个消息包的一部分数据。

TCP半包、粘包产生的原因

TCP是面向连接的传输协议,TCP传输的数据是以流形式传输,流数据是没有明确的开始结尾边界,所以TCP也没办法判断那一段流属于哪一个消息,所以TCP的粘包、半包都是在应用层解决

滑动窗口协议

滑动窗口协议(Sliding window protocol):属于TCP协议的一种应用,用于网络数据传输时流量控制以避免拥塞发生,协议允许发送方在停止并等待确认前发送的多个数据分组,由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据传输,提高网络吞吐量。

滑动窗口本质是描述接收方的TCP数据包缓冲区大小的数据,发送方根据这个数据来计算自己最多能发送多长数据,如果发送方收到的接收方的窗口大小为0的TCP报文段,那么发送方将停止发送数据,等到接收方发送窗口大小不为0的数据包的到来

具体说明:假如A与B建立TCP连接,滑动窗口的作用

1、B端来不及处理所有的数据(控制不同速率主机间的同步),这时,A通过B的通知的接收窗口而减弱数据的发送

2、B端来得及处理接收数据,但是在A与B之间某处C,使得AB之间的整体带宽性能较差,此时,A端根据拥塞控制处理策略(慢启动等)来更新窗口,以决定数据的发送

TCP建立连接初期,B告诉A自己接收窗口的大小,比如为‘20’,

根据B给出的窗口,A构造出自己的窗口

A发送11字节后,发送窗口的位置不变,B接收到的数据乱序的

A发了11个字节数据,只有当A成功发送了数据,即发送的数据得到了B的确认之后,才会移动滑动窗口离开已发送的数据;同时B则确认连续的数据分组,对于乱序的分组则先接收下来,避免网络重复传递:

A收到新的确认号,窗口向前滑动

发送窗口内的序号都属于已发送但未被确认

流量控制,主要是接收方传送数据给发送方,告诉其发送数据不要太快或者加速,是一种端到端的控制,

主要的方式就是返回的ACK中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送:

这里面涉及到一种情况,如果B已经告诉A自己的缓冲区已满,于是A停止发送数据;等待一段时间后,B的缓冲区出现了富余,于是给A发送报文告诉A我的rwnd大小为400,但是这个报文不幸丢失了,于是就出现A等待B的通知||B等待A发送数据的死锁状态。为了处理这种问题,TCP引入了持续计时器(Persistence timer),当A收到对方的零窗口通知时,就启用该计时器,时间到则发送一个1字节的探测报文,对方会在此时回应自身的接收窗口大小,如果结果仍未0,则重设持续计时器,继续等待。

TCP滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。

每个TCP/IP主机支持全双工数据传输,因此TCP有两个滑动窗口:一个用于接收数据,另一个用于发送数据

TCP使用ACK确认技术,其确认号指的是下一个所期待的字节。

假定发送方设备以每一次三个数据包的方式发送数据,也就是说,窗口大小为3。发送方发送序列号为1、2、3的三个数据包,接收方设备成功接收数据包,用序列号4确认。发送方设备收到确认,继续以窗口大小3发送数据。当接收方设备要求降低或者增大网络流量时,可以对窗口大小进行减小或者增加,本例降低窗口大小为2,每一次发送两个数据包。当接收方设备要求窗口大小为0,表明接收方已经接收了全部数据,或者接收方应用程序没有时间读取数据,要求暂停发送。发送方接收到携带窗口号为0的确认,停止这一方向的数据传输。

拥塞控制

发送方维护了一个叫做拥塞控制(cwnd)的状态变量,大小取决于网络的拥塞情况,动态的变化这,发送方让自己的发送窗口等于拥塞窗口,如果考虑接收方的接收能力,即发送方发送窗口可能小于拥塞窗口。

发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去,但只要网络出现拥塞,就把拥塞窗口减小一些,以减少注入到网络中的分组数。

拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载

常用的方法就是:

1. 慢开始、拥塞避免

2. 快重传、快恢复

慢开始、拥塞避免

发送方发送数据是从慢开始执行的

-1. 发送方维持一个叫做“拥塞窗口”的变量,该变量和接收窗口共同决定了发送者的发送窗口;

-2. 当主机开始发送数据时,避免一下子将大量字节注入到网络,造成或者增加拥塞,选择发送一个1字节的试探报文

-3. 当收到第一个字节的数据的确认后,就发送2个字节的报文;

-4. 若再次收到2个字节的确认,则发送4个字节,依次递增2的指数级;

-5. 最后会达到一个提前预设的“慢开始门限”ssthresh,比如24,即一次发送了24个分组,此时遵循下面的条件判定:

*1. cwnd < ssthresh, 继续使用慢开始算法;

*2. cwnd > ssthresh,停止使用慢开始算法,改用拥塞避免算法

*3. cwnd = ssthresh,既可以使用慢开始算法,也可以使用拥塞避免算法;

-6. 所谓拥塞避免算法就是:每经过一个往返时间RTT就把发送方的拥塞窗口+1,即让拥塞窗口缓慢地增大,按照线性规律增长;

-7. 当出现网络拥塞,比如丢包时,将慢开始门限设为原先的一半,然后将cwnd设为1,执行慢开始算法(较低的起点,指数级增长);

上述方法的目的是在拥塞发生时循序减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够的时间把队列中积压的分组处理完毕。慢开始和拥塞控制算法常常作为一个整体使用,

而快重传和快恢复则是为了减少因为拥塞导致的数据包丢失带来的重传时间,从而避免传递无用的数据到网络。

快重传的机制是:

-1. 接收方建立这样的机制,如果一个包丢失,则对后续的包继续发送针对该包的重传请求;

-2. 一旦发送方接收到三个一样的确认,就知道该包之后出现了错误,立刻重传该包;

-3. 此时发送方开始执行“快恢复”算法:

*1. 慢开始门限减半;

*2. cwnd设为慢开始门限减半后的数值;

*3. 执行拥塞避免算法(高起点,线性增长);

确认机制(ACK)

ack表示期望下次接收到的序号。

那么ack是如何算出来的呢,就是通过收到的序号,和数据长度相加得来。

假设A收到B过来的数据(seq = 5,len = 15)。len表示数据长度。

那么A就会回复B,“刚才的数据我已经收到了,你接下来就发序号为20的包给我吧”。这样就保证了数据不会乱序。

综上,确认号就是下一次将要收到包的序号。同时也等于发送方的序号+数据长度(确认号在ACK标志位有效时才有用。)

TCP协议如何保证数据的可靠性

1、校验和

TCP检验和的计算与UDP一样,在计算时要加上12byte的伪首部,检验范围包括TCP首部及数据部分,但是UDP的检验和字段为可选的,而TCP中是必须有的。计算方法为:在发送方将整个报文段分为多个16位的段,然后将所有段进行反码相加,将结果存放在检验和字段中,接收方用相同的方法进行计算,如最终结果为检验字段所有位是全1则正确(UDP中为0是正确),否则存在错误。

2、序列号

TCP将每个字节的数据都进行了编号,这就是序列号。 序列号的作用:

a) 保证可靠性(当接收到的数据总少了某个序号的数据时,能马上知道)

b) 保证数据的按序到达

c) 提高效率,可实现多次发送,一次确认

d) 去除重复数据

数据传输过程中的确认应答处理、重发控制以及重复控制等功能都可以通过序列号来实现

3、确认应答机制(ACK)

TCP通过确认应答机制实现可靠的数据传输。在TCP的首部中有一个标志位——ACK,此标志位表示确认号是否有效。接收方对于按序到达的数据会进行确认,当标志位ACK=1时确认首部的确认字段有效。进行确认时,确认字段值表示这个值之前的数据都已经按序到达了。而发送方如果收到了已发送的数据的确认报文,则继续传输下一部分数据;而如果等待了一定时间还没有收到确认报文就会启动重传机制。

4、超时重传机制/快重传

当报文发出后在一定的时间内未收到接收方的确认,发送方就会进行重传(通常是在发出报文段后设定一个闹钟,到点了还没有收到应答则进行重传)

当然,未收到确认不一定就是发送的数据包丢了,还可能是确认的ACK丢了:

当主机B返回应答,因为网络拥堵等原因在传送途中丢失,没有到达主机A。主机A会等待一段时间,若在等待的时间间隔内始终未能收到这个确认应答,主机B将第二次发送已接收数据的确认应答,由于主机B其实已经收到1~100的数据,当在有相同的数据到达时它会放弃。

5、连接管理机制连接可靠性

连接管理机制即TCP建立连接时的三次握手和断开连接时的四次挥手。

a) 拥塞控制

拥塞避免、慢开始等算法实现,上文以对拥塞控制这部分做出详细讲解,此处不赘述。

b) 流量控制

通过滑动窗口进行,上文以对滑动窗口做出详细讲解

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值