精华内容
下载资源
问答
  • 客户终端私用netty通信同时和web通信使用不同的协议tcp和http
  • C#使用ModbusTcp协议与西门子1200PLC通讯。包含modbus协议的全部八种功能码的读写程序。
  • 终于把TCP协议与UDP协议给整明白了

    万次阅读 多人点赞 2020-07-04 21:35:16
    网络编程有三个要素,分别是IP地址、端口号和通信协议,本文主要讲述的是TCP与UDP这两种通信协议,以及编程的实现。

    网络编程有三个要素,分别是IP地址、端口号和通信协议,那本文主要讲述的是TCP与UDP这两种通信协议,以及编程的实现。

    首先,我们需要了解一下IP地址、端口号、通信协议的相关知识。

    一、IP地址

    网络中的计算机使用IP地址来进行唯一标识,IP地址有IPv4和IPv6两种类型。IPv4采用十进制或二进制表示形式,十进制是一种比较常用的表示形式,如192.168.1.131,IPv6采用十六进制表示形式,一般不常用。

    如何查看IP地址相关信息:

    在Windows系统下,打开cmd,输入命令ipconfig,按回车即可查看。在Linux或Mac系统下,打开终端,使用ifconfig命令,按回车即可查看。

    二、端口号

    端口号是计算机中的应用程序的一个整数数字标号,用来区分不同的应用程序。

    0 ~ 1024 为被系统使用或保留的端口号,0 ~ 65535为有效的端口号,也就是说我们要对一些程序定义端口号的时候,要选择1024 ~ 65535范围内的整数数字。

    比如,以前学过的MySQL的端口号是3306,SQLServer的端口号是1433,查了一下Oracle的端口号是1521。

    一定要把这些数据库对应的端口号,藏在深深的脑海里,以后在连接数据库的时候,会使用到端口号。

    三、通信协议

    说的通俗一点,通信协议就是网络通信中的规则,分为TCP协议和UDP协议两种。

    第一种:TCP协议
    英文名:Transmission Control Protocol
    中文名:传输控制协议
    协议说明:TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。

    举例:打电话,需要双方都接通,才能进行对话

    特点:效率低,数据传输比较安全

    第二种:UDP协议
    英文名:User Datagram Protocol
    中文名:数据报协议
    协议说明:UDP是一种面向无连接的传输层通信协议。

    举例:发短信,不需要双方建立连接,But,数据报的大小应限制在64k以内

    特点:效率高,数据传输不安全,容易丢包

    四、三要素关系图与网络模型图

    1、网络编程三要素关系图
    在这里插入图片描述

    注:图中端口号、IP地址为演示,并非真实

    2、OSI参考模型与TCP/IP参考模型
    在这里插入图片描述

    五、TCP编程

    TCP是基于字节流的传输层通信协议,所以TCP编程是基于IO流编程。

    对于客户端,我们需要使用Socket类来创建对象。对于服务器端,我们需要使用ServerSocket来创建对象,通过对象调用accept()方法来进行监听是否有客户端访问。

    客户端与服务器端图解:
    在这里插入图片描述

    客户端与服务器端实现步骤:

    前提:创建一个项目,在项目中创建两个模块(model),一个模块用来放客户端相关代码,一个模块用来放服务器端相关代码。

    目录结构如下图

    在这里插入图片描述

    客户端:

    1、创建Socket对象,并指定服务器端应用程序的端口号和服务器端主机的IP地址。

    2、使用Socket的对象调用getOutputStream()方法来获取字节输出流对象。

    3、调用字节输出流的write(byte[] buf)或者write(int b)向服务器发送指定数据。

    4、记得关闭流。

    服务器端:

    1、创建ServerSocket对象,并指定该应用程序的端口号,端口号必须和客户端指定的端口号一样。

    2、使用ServerSocket对象的accept()方法来监听客户端发送过来的请求,返回值为Socket对象。

    3、调用Socket对象的getInputStream()方法获取字节输入流对象

    4、调用字节输入流对象的read(byte[] buf)read()方法获取数据。

    5、记得关闭流。

    实例:

    客户端向服务器端发送信息,并显示在服务器端。

    Client类(客户端)

    package cn.tkrnet.client;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.Socket;
    
    public class Client {
        public static void main(String[] args) throws IOException {
            
            //创建Socket对象,指定要发送到服务器端的IP地址,以及服务器端应用程序接收的端口号
            //localhost代表本机IP地址
            Socket client = new Socket("localhost",9000);
            
            //获取输出流,用于向服务器端发送数据
            OutputStream os = client.getOutputStream();
            
            os.write("Java is my friend !".getBytes());
            System.out.println("信息已发送");
    
            //关闭流
            os.close();
            client.close();
        }
    }
    

    Server类(服务器端)

    package cn.tkrnet.server;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class Server {
        public static void main(String[] args) throws IOException {
            System.out.println("--服务器端已开启--");
    
            //创建ServerSocket对象,这里的端口号必须与客户端的端口号相同
            ServerSocket server = new ServerSocket(9000);
    
            //调用方法accept(),用来监听客户端发来的请求
            Socket socket = server.accept();
    
            //获取输入流对象
            InputStream is = socket.getInputStream();
    
            //读取输入流中的数据
            int b = 0;
            while ((b =is.read()) != -1){
                System.out.print((char)b);
            }
            //关闭流
            is.close();
            socket.close();
            server.close();
        }
    }
    

    提示:在运行程序时,一定要先运行服务器端的程序代码,再运行客户端的程序代码。因为客户端要向服务器发送请求,前提是服务器端要处于开启状态。

    Server类(服务器端)运行结果:

    --服务器端已开启--
    
    

    Client类(客户端)运行结果:

    信息已发送
    

    Client类(客户端)运行后,Server类(服务器端)收到信息,运行结果:

    --服务器端已开启--
    Java is my friend !
    

    实例分析:

    服务器端启动后,服务器端的accept()方法一直处于监听状态,直到客户端连接了服务器,服务器端再从流中读取客户端发来的数据。

    恕我直言,这是一个超级无敌简单的单向通信实例。

    六、UDP编程

    UDP使用数据报进行数据传输,没有客户端与服务器端之分,只有发送方与接收方,两者哪个先启动都不会报错,但是会出现数据丢包现象。发送的内容有字数限制,大小必须限制在64k以内。

    发送方与接收方实现步骤:

    前提:创建一个项目,在项目中创建两个模块(model),一个模块用来放发送方相关代码,一个模块用来放接收方相关代码。

    目录结构如下图

    在这里插入图片描述

    发送方:

    1、创建DatagramSocket对象,可以指定应用程序的端口号,也可以不指定。

    2、准备需要发送的数据

    3、创建DatagramPacket对象,用来对发送的数据进行打包,需要指定发送内容、发送多少、发送到哪里和接收方的端口号四个参数。

    4、调用DatagramSocket对象的send()方法发送数据。

    5、记得关闭流。

    接收方:

    1、创建DatagramSocket对象,指定接收方的端口号,这个必须指定。

    2、创建一个byte类型数组,用来接收发送方发送过来的数据。

    3、创建DatagramPacket对象,准备接收数据。

    4、调用DatagramSocket对象的receive()方法用于接收数据。

    5、使用String类的构造方法将byte类型的数组中的数据转化成String类型并显示。

    6、记得关闭流。

    实例:

    发送方发送信息,接收方接收信息,并显示。

    Sender类(发送方)

    package cn.tkrnet.Sender;
    
    import java.io.IOException;
    import java.net.*;
    
    public class Sender {
        public static void main(String[] args) throws IOException {
    
            //创建接受或发送的数据报套接字,并指定发送方的端口号为7770
            DatagramSocket ds = new DatagramSocket(7770);   //端口号也可以不指定
            System.out.println("---发送方---");
    
            //创建数据报对象,用来发送数据
            byte[] b = "Java is my friend !".getBytes();
    
            //8800为接收方的端口号,netAddress.getByName("localhost")是获取主机的IP地址
            DatagramPacket dp = new DatagramPacket(b,b.length, InetAddress.getByName("localhost"),7788);
    
            ds.send(dp);    //发送数据报
            System.out.println("数据已发送");
            //关闭流
            ds.close();
        }
    }
    

    Receiver类(接收方)

    package cn.tkrnet.receiver;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    
    public class Receiver {
        public static void main(String[] args) throws IOException {
            System.out.println("---接收方---");
    
            //创建数据报套接字对象,指定的端口号要和发送方发送数据的端口号相同
            // (不是发送方的端口号7770,是发送方发送数据的端口号7788)
            DatagramSocket ds = new DatagramSocket(7788);
    
            //创建接收数据报的对象
            byte[] b = new byte[1024];
            DatagramPacket dp = new DatagramPacket(b,b.length);
    
            //接收数据
            ds.receive(dp);
            System.out.println(new String(b,0,dp.getLength()));
            //关闭流
            ds.close();
        }
    }
    

    提示:在运行程序时,先运行发送方程序,还是先运行接收方程序都不会报错,但是有可能会出现数据丢包,一般我们都先运行接收方的程序代码,再运行发送方的程序代码。

    Receiver类(接收方)运行结果:

    ---接收方---
    
    

    Sender类(发送方)运行结果:

    ---发送方---
    数据已发送
    

    Sender类(发送方)运行后,Receiver类(接收方)接收到信息,运行结果:

    ---接收方---
    Java is my friend !
    

    实例分析:

    只有接收方先启动运行,才会存在端口号为7788的程序,发送方才能发送数据到指定端口号7788,接收方才能接收数据。

    不瞒你说,这也是个超级无敌简单的单向通信实例。

    七、总结

    以上是我分享给大家的关于网络通信TCP协议与UDP协议的一些总结。如果觉得还不错的话,就送我一个赞吧!如果本文对你有用的话,也欢迎收藏哦!

    展开全文
  • TCP协议详解 (史上最全)

    千次阅读 多人点赞 2021-03-08 12:27:36
    传输层TCP协议提供了一种面向连接的、可靠的字节流服务,其数据帧格式,大致如下图所示: 图:传输层TCP协议的数据帧格式 一个传输层TCP协议的数据帧,大致包含以下字段: (一)源端口号 源端口号表示报文的发送...

    文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源:


    推荐2:史上最全 Java 面试题 21 个专题

    史上最全 Java 面试题 21 个专题阿里、京东、美团、头条… 随意挑、横着走!!!
    1: JVM面试题(史上最强、持续更新、吐血推荐)https://www.cnblogs.com/crazymakercircle/p/14365820.html
    2:Java基础面试题(史上最全、持续更新、吐血推荐)https://www.cnblogs.com/crazymakercircle/p/14366081.html
    4:设计模式面试题 (史上最全、持续更新、吐血推荐)https://www.cnblogs.com/crazymakercircle/p/14367101.html
    5:架构设计面试题 (史上最全、持续更新、吐血推荐)https://www.cnblogs.com/crazymakercircle/p/14367907.html
    还有 21篇必刷、必刷 的面试题更多 …, 请参见【 疯狂创客圈 高并发 总目录

    推荐3: 疯狂创客圈 高质量 博文

    springCloud 高质量 博文
    nacos 实战(史上最全) sentinel (史上最全+入门教程)
    springcloud + webflux 高并发实战 Webflux(史上最全)
    SpringCloud gateway (史上最全)TCP/IP图解 (史上最全)
    10分钟看懂, Java NIO 底层原理Feign原理 (图解)
    更多精彩博文 …请参见【 疯狂创客圈 高并发 总目录

    TCP/IP协议包含了一系列的协议,也叫TCP/IP协议族(TCP/IP Protocol Suite,或TCP/IP
    Protocols),简称TCP/IP。TCP/IP协议族提供了点对点的连结机制,并且将传输数据帧的封装、寻址、传输、路由以及接收方式,都予以标准化。

    TCP/IP协议的分层模型

    在展开介绍TCP/IP协议之前,首先介绍一下七层ISO模型。国际标准化组织ISO为了使网络应用更为普及,推出了OSI参考模型,即开放式系统互联(Open
    System Interconnect)模型,
    一般都叫OSI参考模型。OSI参考模型是ISO组织在1985年发布的网络互连模型,其含义就是为所有公司使用一个统一的规范来控制网络,这样所有公司遵循相同的通信规范,网络就能互联互通了。

    OSI模型的七层框架

    OSI模型定义了网络互连的七层框架(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),每一层实现各自的功能和协议,并完成与相邻层的接口通信。OSI模型各层的通信协议,大致举例如下表所示:

    表:OSI模型各层的通信协议举例

    应用层HTTP、SMTP、SNMP、FTP、Telnet、SIP、SSH、NFS、RTSP、XMPP、Whois、ENRP、等等
    表示层XDR、ASN.1、SMB、AFP、NCP、等等
    会话层ASAP、SSH、RPC、NetBIOS、ASP、Winsock、BSD Sockets、等等
    传输层TCP、UDP、TLS、RTP、SCTP、SPX、ATP、IL、等等
    网络层IP、ICMP、IGMP、IPX、BGP、OSPF、RIP、IGRP、EIGRP、ARP、RARP、X.25、等等
    数据链路层以太网、令牌环、HDLC、帧中继、ISDN、ATM、IEEE 802.11、FDDI、PPP、等等
    物理层例如铜缆、网线、光缆、无线电等等

    TCP/IP协议是Internet互联网最基本的协议,其在一定程度上参考了七层ISO模型。OSI模型共有七层,从下到上分别是物理层、数据链路层、网络层、运输层、会话层、表示层和应用层。但是这显然是有些复杂的,所以在TCP/IP协议中,七层被简化为了四个层次。TCP/IP模型中的各种协议,依其功能不同,被分别归属到这四层之中,常被视为是简化过后的七层OSI模型。

    TCP/IP协议与七层ISO模型的对应关系

    TCP/IP协议与七层ISO模型的对应关系,大致如下图所示:
    在这里插入图片描述

    图:TCP/IP协议与七层ISO模型的对应关系

    TCP/IP协议的应用层的主要协议有HTTP、Telnet、FTP、SMTP等,是用来读取来自传输层的数据或者将数据传输写入传输层;传输层的主要协议有UDP、TCP,实现端对端的数据传输;网络层的主要协议有ICMP、IP、IGMP,主要负责网络中数据包的传送等;链路层有时也称作数据链路层或网络接口层,主要协议有ARP、RARP,
    通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡,它们一起处理与传输媒介(如电缆或其他物理设备)的物理接口细节。

    (一)TCP/IP协议的应用层

    应用层包括所有和应用程序协同工作,并利用基础网络交换应用程序的业务数据的协议。一些特定的程序被认为运行在这个层上,该层协议所提供的服务能直接支持用户应用。应用层协议包括HTTP(万维网服务)、FTP(文件传输)、SMTP(电子邮件)、SSH(安全远程登陆)、DNS(域名解析)以及许多其他协议。

    (二)TCP/IP协议的传输层

    传输层的协议,解决了诸如端到端可靠性问题,能确保数据可靠的到达目的地,甚至能保证数据按照正确的顺序到达目的地。传输层的主要功能大致如下:

    (1)为端到端连接提供传输服务;

    (2)这种传输服务分为可靠和不可靠的,其中TCP是典型的可靠传输,而UDP则是不可靠传输;

    (3)为端到端连接提供流量控制、差错控制、QoS(Quality of
    Service)服务质量等管理服务。

    传输层主要有两个性质不同的协议:TCP传输控制协议和UDP用户数据报协议。

    TCP协议是一个面向连接的、可靠的传输协议,它提供一种可靠的字节流,能保证数据完整、无损并且按顺序到达。TCP尽量连续不断地测试网络的负载并且控制发送数据的速度以避免网络过载。另外,TCP试图将数据按照规定的顺序发送。

    UDP协议是一个无连接的数据报协议,是一个“尽力传递”和“不可靠”协议,不会对数据包是否已经到达目的地进行检查,并且不保证数据包按顺序到达。

    总体来说,TCP协议传输效率低,但可靠性强;UDP协议传输效率高,但可靠性略低,适用于传输可靠性要求不高、体量小的数据(比如QQ聊天数据)。

    (三)TCP/IP协议的网络层

    TCP/IP协议网络层的作用是在复杂的网络环境中为要发送的数据报找到一个合适的路径进行传输。简单来说,网络层负责将数据传输到目标地址,目标地址可以是多个网络通过路由器连接而成的某一个地址。另外,网络层负责寻找合适的路径到达对方计算机,并把数据帧传送给对方,网络层还可以实现拥塞控制、网际互连等功能。网络层协议的代表包括:ICMP、IP、IGMP等。

    (四)TCP/IP协议的链路层

    链路层有时也称作数据链路层或网络接口层,用来处理连接网络的硬件部分。该层既包括操作系统硬件的设备驱动、NIC(网卡)、光纤等物理可见部分,还包括连接器等一切传输媒介。在这一层,数据的传输单位为比特。其主要协议有ARP、RARP等。

    图解 物理层:使用MAC解决设备的身份证问题

    通信的原始时代

    很久很久之前,你不与任何其他电脑相连接,孤苦伶仃。

    image

    直到有一天,你希望与另一台电脑 B 建立通信,于是你们各开了一个网口,用一根网线连接了起来。

    image

    用一根网线连接起来怎么就能"通信"了呢?我可以给你讲 IO、讲中断、讲缓冲区,但这不是研究网络时该关心的问题。

    如果你纠结,要么去研究一下操作系统是如何处理网络 IO 的,要么去研究一下包是如何被网卡转换成电信号发送出去的,要么就仅仅把它当做电脑里有个小人在开枪吧~

    image.gif

    反正,你们就是连起来了,并且可以通信。

    有一天,一个新伙伴 C 加入了,但聪明的你们很快发现,可以每个人开两个网口,用一共三根网线,彼此相连。

    image

    随着越来越多的人加入,你发现身上开的网口实在太多了,而且网线密密麻麻,混乱不堪。(而实际上一台电脑根本开不了这么多网口,所以这种连线只在理论上可行,所以连不上的我就用红色虚线表示了,就是这么严谨哈哈~)

    image

    集线器的诞生

    于是你们发明了一个中间设备,你们将网线都插到这个设备上,由这个设备做转发,就可以彼此之间通信了,本质上和原来一样,只不过网口的数量和网线的数量减少了,不再那么混乱。

    image

    你给它取名叫集线器,它仅仅是无脑将电信号转发到所有出口(广播),不做任何处理,你觉得它是没有智商的,因此把人家定性在了物理层

    image.gif

    由于转发到了所有出口,那 BCDE 四台机器怎么知道数据包是不是发给自己的呢?

    首先,你要给所有的连接到交换机的设备,都起个名字。原来你们叫 ABCD,但现在需要一个更专业的,全局唯一的名字作为标识,你把这个更高端的名字称为 MAC 地址

    你的 MAC 地址是 aa-aa-aa-aa-aa-aa,你的伙伴 b 的 MAC 地址是 bb-bb-bb-bb-bb-bb,以此类推,不重复就好。

    这样,A 在发送数据包给 B 时,只要在头部拼接一个这样结构的数据,就可以了。

    image

    B 在收到数据包后,根据头部的目标 MAC 地址信息,判断这个数据包的确是发给自己的,于是便收下

    其他的 CDE 收到数据包后,根据头部的目标 MAC 地址信息,判断这个数据包并不是发给自己的,于是便丢弃

    image

    虽然集线器使整个布局干净不少,但原来我只要发给电脑 B 的消息,现在却要发给连接到集线器中的所有电脑,这样既不安全,又不节省网络资源。

    图解 数据链路:使用交换机解决MAC 地址映射问题

    集线器的问题

    如果把这个集线器弄得更智能一些,只发给目标 MAC 地址指向的那台电脑,就好了。

    image

    交换机的诞生

    虽然只比集线器多了这一点点区别,但看起来似乎有智能了,你把这东西叫做交换机。也正因为这一点点智能,你把它放在了另一个层级,数据链路层

    image

    如上图所示,你是这样设计的。

    交换机内部维护一张 MAC 地址表,记录着每一个 MAC 地址的设备,连接在其哪一个端口上。

    MAC 地址端口
    bb-bb-bb-bb-bb-bb1
    cc-cc-cc-cc-cc-cc3
    aa-aa-aa-aa-aa-aa4
    dd-dd-dd-dd-dd-dd5

    假如你仍然要发给 B 一个数据包,构造了如下的数据结构从网口出去。

    image

    到达交换机时,交换机内部通过自己维护的 MAC 地址表,发现目标机器 B 的 MAC 地址 bb-bb-bb-bb-bb-bb 映射到了端口 1 上,于是把数据从 1 号端口发给了 B,完事~

    你给这个通过这样传输方式而组成的小范围的网络,叫做以太网

    当然最开始的时候,MAC 地址表是空的,是怎么逐步建立起来的呢?

    假如在 MAC 地址表为空是,你给 B 发送了如下数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-quHDePVL-1615262768771)(https://upload-images.jianshu.io/upload_images/19816137-2195d8e3fa05f616.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

    由于这个包从端口 4 进入的交换机,所以此时交换机就可以在 MAC地址表记录第一条数据:

    MAC:aa-aa-aa-aa-aa-aa-aa
    端口:4

    交换机看目标 MAC 地址(bb-bb-bb-bb-bb-bb)在地址表中并没有映射关系,于是将此包发给了所有端口,也即发给了所有机器。

    之后,只有机器 B 收到了确实是发给自己的包,于是做出了响应,响应数据从端口 1 进入交换机,于是交换机此时在地址表中更新了第二条数据:

    MAC:bb-bb-bb-bb-bb-bb
    端口:1

    过程如下

    image

    经过该网络中的机器不断地通信,交换机最终将 MAC 地址表建立完毕~

    随着机器数量越多,交换机的端口也不够了,但聪明的你发现,只要将多个交换机连接起来,这个问题就轻而易举搞定~

    image

    你完全不需要设计额外的东西,只需要按照之前的设计和规矩来,按照上述的接线方式即可完成所有电脑的互联,所以交换机设计的这种规则,真的很巧妙。你想想看为什么(比如 A 要发数据给 F)。

    但是你要注意,上面那根红色的线,最终在 MAC 地址表中可不是一条记录呀,而是要把 EFGH 这四台机器与该端口(端口6)的映射全部记录在表中。

    MAC 地址和端口的映射记录

    最终,两个交换机将分别记录 A ~ H 所有机器的映射记录

    左边的交换机

    MAC 地址端口
    bb-bb-bb-bb-bb-bb1
    cc-cc-cc-cc-cc-cc3
    aa-aa-aa-aa-aa-aa4
    dd-dd-dd-dd-dd-dd5
    ee-ee-ee-ee-ee-ee6
    ff-ff-ff-ff-ff-ff6
    gg-gg-gg-gg-gg-gg6
    hh-hh-hh-hh-hh-hh6

    右边的交换机

    MAC 地址端口
    bb-bb-bb-bb-bb-bb1
    cc-cc-cc-cc-cc-cc1
    aa-aa-aa-aa-aa-aa1
    dd-dd-dd-dd-dd-dd1
    ee-ee-ee-ee-ee-ee2
    ff-ff-ff-ff-ff-ff3
    gg-gg-gg-gg-gg-gg4
    hh-hh-hh-hh-hh-hh6

    这在只有 8 台电脑的时候还好,甚至在只有几百台电脑的时候,都还好,所以这种交换机的设计方式,已经足足支撑一阵子了。

    但很遗憾,人是贪婪的动物,很快,电脑的数量就发展到几千、几万、几十万。

    图解 传输层:IP地址和路由器

    二层交换机的问题

    交换机已经无法记录如此庞大的映射关系了。

    此时你动了歪脑筋,你发现了问题的根本在于,连出去的那根红色的网线,后面不知道有多少个设备不断地连接进来,从而使得地址表越来越大。

    那我可不可以让那根红色的网线,接入一个新的设备,这个设备就跟电脑一样有自己独立的 MAC 地址,而且同时还能帮我把数据包做一次转发呢?

    这个设备就是路由器,它的功能就是,作为一台独立的拥有 MAC 地址的设备,并且可以帮我把数据包做一次转发你把它定在了网络层。

    image

    注意,路由器的每一个端口,都有独立的 MAC 地址

    好了,现在交换机的 MAC 地址表中,只需要多出一条 MAC 地址 ABAB 与其端口的映射关系,就可以成功把数据包转交给路由器了,这条搞定。

    那如何做到,把发送给 C 和 D,甚至是把发送给 DEFGH… 的数据包,统统先发送给路由器呢?

    不难想到这样一个点子,假如电脑 C 和 D 的 MAC 地址拥有共同的前缀,比如分别是

    C 的 MAC 地址:FFFF-FFFF-CCCC D 的 MAC 地址:FFFF-FFFF-DDDD

    那我们就可以说,将目标 MAC 地址为 FFFF-FFFF-?开头的,统统先发送给路由器。

    这样是否可行呢?答案是否定的。

    IP地址的诞生

    我们先从现实中 MAC 地址的结构入手,MAC地址也叫物理地址、硬件地址,长度为 48 位,一般这样来表示

    00-16-EA-AE-3C-40

    它是由网络设备制造商生产时烧录在网卡的EPROM(一种闪存芯片,通常可以通过程序擦写)。

    其中前 24 位(00-16-EA)代表网络硬件制造商的编号,后 24 位(AE-3C-40)是该厂家自己分配的,一般表示系列号。

    只要不更改自己的 MAC 地址,MAC 地址在世界是唯一的。形象地说,MAC地址就如同身份证上的身份证号码,具有唯一性。

    那如果你希望向上面那样表示将目标 MAC 地址为 FFFF-FFFF-?开头的,统一从路由器出去发给某一群设备(后面会提到这其实是子网的概念),那你就需要要求某一子网下统统买一个厂商制造的设备,要么你就需要要求厂商在生产网络设备烧录 MAC 地址时,提前按照你规划好的子网结构来定 MAC 地址,并且日后这个网络的结构都不能轻易改变。

    这显然是不现实的。

    于是你发明了一个新的地址,给每一台机器一个 32 位的编号,如:

    11000000101010000000000000000001

    你觉得有些不清晰,于是把它分成四个部分,中间用点相连。

    11000000.10101000.00000000.00000001

    你还觉得不清晰,于是把它转换成 10 进制。

    192.168.0.1

    最后你给了这个地址一个响亮的名字,IP 地址。现在每一台电脑,同时有自己的 MAC 地址,又有自己的 IP 地址,只不过 IP 地址是软件层面上的,可以随时修改,MAC 地址一般是无法修改的。

    这样一个可以随时修改的 IP 地址,就可以根据你规划的网络拓扑结构,来调整了。

    image

    如上图所示,假如我想要发送数据包给 ABCD 其中一台设备,不论哪一台,我都可以这样描述,“将 IP 地址为 192.168.0 开头的全部发送给到路由器,之后再怎么转发,交给它!”,巧妙吧。

    路由器的诞生

    路由器诞生了,专门负责IP地址的寻找。那报文交给路由器之后,路由器又是怎么把数据包准确转发给指定设备的呢?

    别急我们慢慢来。

    我们先给上面的组网方式中的每一台设备,加上自己的 IP 地址

    image

    image.gif

    现在两个设备之间传输,除了加上数据链路层的头部之外,还要再增加一个网络层的头部。

    假如 A 给 B 发送数据,由于它们直接连着交换机,所以 A 直接发出如下数据包即可,其实网络层没有体现出作用。

    image

    但假如 A 给 C 发送数据,A 就需要先转交给路由器,然后再由路由器转交给 C。由于最底层的传输仍然需要依赖以太网,所以数据包是分成两段的。

    A ~ 路由器这段的包如下:

    image

    路由器到 C 这段的包如下:

    image

    好了,上面说的两种情况(A->B,A->C),相信细心的读者应该会有不少疑问,下面我们一个个来展开。

    子网的由来

    A 给 C 发数据包,怎么知道是否要通过路由器转发呢?

    答案:子网

    如果源 IP 与目的 IP 处于一个子网,直接将包通过交换机发出去。

    如果源 IP 与目的 IP 不处于一个子网,就交给路由器去处理。

    好,那现在只需要解决,什么叫处于一个子网就好了。

    • 192.168.0.1 和 192.168.0.2 处于同一个子网

    • 192.168.0.1 和 192.168.1.1 处于不同子网

    这两个是我们人为规定的,即我们想表示,对于 192.168.0.1 来说:

    192.168.0.xxx 开头的,就算是在一个子网,否则就是在不同的子网。

    那对于计算机来说,怎么表达这个意思呢?于是人们发明了子网掩码的概念

    假如某台机器的子网掩码定为 255.255.255.0

    这表示,将源 IP 与目的 IP 分别同这个子网掩码进行与运算****,相等则是在一个子网,不相等就是在不同子网,就这么简单。

    比如

    • A电脑:192.168.0.1 & 255.255.255.0 = 192.168.0.0

    • B电脑:192.168.0.2 & 255.255.255.0 = 192.168.0.0

    • C电脑:192.168.1.1 & 255.255.255.0 = 192.168.1.0

    • D电脑:192.168.1.2 & 255.255.255.0 = 192.168.1.0

    那么 A 与 B 在同一个子网,C 与 D 在同一个子网,但是 A 与 C 就不在同一个子网,与 D 也不在同一个子网,以此类推。

    image

    所以如果 A 给 C 发消息,A 和 C 的 IP 地址分别 & A 机器配置的子网掩码,发现不相等,则 A 认为 C 和自己不在同一个子网,于是把包发给路由器,就不管了,之后怎么转发,A 不关心

    A 如何知道,哪个设备是路由器?

    答案:在 A 上要设置默认网关

    上一步 A 通过是否与 C 在同一个子网内,判断出自己应该把包发给路由器,那路由器的 IP 是多少呢?

    其实说发给路由器不准确,应该说 A 会把包发给默认网关

    对 A 来说,A 只能直接把包发给同处于一个子网下的某个 IP 上,所以发给路由器还是发给某个电脑,对 A 来说也不关心,只要这个设备有个 IP 地址就行。

    所以默认网关,就是 A 在自己电脑里配置的一个 IP 地址,以便在发给不同子网的机器时,发给这个 IP 地址。

    image

    仅此而已!

    路由表的由来(和Mac表的由来好像,都是逼出来的)

    路由器如何知道C在哪里?

    答案:路由表

    现在 A 要给 C 发数据包,已经可以成功发到路由器这里了,最后一个问题就是,路由器怎么知道,收到的这个数据包,该从自己的哪个端口出去,才能直接(或间接)地最终到达目的地 C 呢。

    路由器收到的数据包有目的 IP 也就是 C 的 IP 地址,需要转化成从自己的哪个端口出去,很容易想到,应该有个表,就像 MAC 地址表一样。

    这个表就叫路由表

    至于这个路由表是怎么出来的,有很多路由算法,本文不展开,因为我也不会哈哈~

    不同于 MAC 地址表的是,路由表并不是一对一这种明确关系,我们下面看一个路由表的结构。

    目的地址子网掩码下一跳端口
    192.168.0.0255.255.255.00
    192.168.0.254255.255.255.2550
    192.168.1.0255.255.255.01
    192.168.1.254255.255.255.2551

    我们学习一种新的表示方法,由于子网掩码其实就表示前多少位表示子网的网段,所以如 192.168.0.0(255.255.255.0) 也可以简写为 192.168.0.0/24

    目的地址下一跳端口
    192.168.0.0/240
    192.168.0.254/320
    192.168.1.0/241
    192.168.1.254/321

    这就很好理解了,路由表就表示,192.168.0.xxx 这个子网下的,都转发到 0 号端口,192.168.1.xxx 这个子网下的,都转发到 1 号端口。下一跳列还没有值,我们先不管

    配合着结构图来看(这里把子网掩码和默认网关都补齐了)

    image

    image.gif

    刚才说的都是 IP 层,但发送数据包的数据链路层需要知道 MAC 地址,可是我只知道 IP 地址该怎么办呢?

    答案:arp

    假如你(A)此时不知道你同伴 B 的 MAC 地址(现实中就是不知道的,刚刚我们只是假设已知),你只知道它的 IP 地址,你该怎么把数据包准确传给 B 呢?

    答案很简单,在网络层,我需要把 IP 地址对应的 MAC 地址找到,也就是通过某种方式,找到 192.168.0.2 对应的 MAC 地址 BBBB

    这种方式就是 arp 协议,同时电脑 A 和 B 里面也会有一张 arp 缓存表,表中记录着 IP 与 MAC 地址的对应关系。

    IP 地址MAC 地址
    192.168.0.2BBBB

    一开始的时候这个表是空的,电脑 A 为了知道电脑 B(192.168.0.2)的 MAC 地址,将会广播一条 arp 请求,B 收到请求后,带上自己的 MAC 地址给 A 一个响应。此时 A 便更新了自己的 arp 表。

    这样通过大家不断广播 arp 请求,最终所有电脑里面都将 arp 缓存表更新完整。

    图解:整个传输过程

    从各个节点的视角来看

    电脑视角

    • 首先我要知道我的 IP 以及对方的 IP

    • 通过子网掩码判断我们是否在同一个子网

    • 在同一个子网就通过 arp 获取对方 mac 地址直接扔出去

    • 不在同一个子网就通过 arp 获取默认网关的 mac 地址直接扔出去

    交换机视角:

    • 我收到的数据包必须有目标 MAC 地址

    • 通过 MAC 地址表查映射关系

    • 查到了就按照映射关系从我的指定端口发出去

    • 查不到就所有端口都发出去

    路由器视角:

    • 我收到的数据包必须有目标 IP 地址

    • 通过路由表查映射关系

    • 查到了就按照映射关系从我的指定端口发出去(不在任何一个子网范围,走其路由器的默认网关也是查到了)

    • 查不到则返回一个路由不可达的数据包

    如果你嗅觉足够敏锐,你应该可以感受到下面这句话:

    网络层(IP协议)本身没有传输包的功能,包的实际传输是委托给数据链路层(以太网中的交换机)来实现的。

    涉及到的三张表分别是

    • 交换机中有 MAC 地址表用于映射 MAC 地址和它的端口

    • 路由器中有路由表用于映射 IP 地址(段)和它的端口

    • 电脑和路由器中都有** arp 缓存表**用于缓存 IP 和 MAC 地址的映射关系

    这三张表是怎么来的

    • MAC 地址表是通过以太网内各节点之间不断通过交换机通信,不断完善起来的。

    • 路由表是各种路由算法 + 人工配置逐步完善起来的。

    • arp 缓存表是不断通过 arp 协议的请求逐步完善起来的。

    知道了以上这些,目前网络上两个节点是如何发送数据包的这个过程,就完全可以解释通了!

    参考的网络拓扑图

    那接下来我们就放上参考的 最后一个网络拓扑图吧,请做好 战斗 准备!

    image

    这时路由器 1 连接了路由器 2,所以其路由表有了下一条地址这一个概念,所以它的路由表就变成了这个样子。如果匹配到了有下一跳地址的一项,则需要再次匹配,找到其端口,并找到下一跳 IP 的 MAC 地址。

    也就是说找来找去,最终必须能映射到一个端口号,然后从这个端口号把数据包发出去。

    目的地址下一跳端口
    192.168.0.0/240
    192.168.0.254/320
    192.168.1.0/241
    192.168.1.254/321
    192.168.2.0/24192.168.100.5
    192.168.100.0/242
    192.168.100.4/322

    这时如果 A 给 F 发送一个数据包,能不能通呢?如果通的话整个过程是怎样的呢?

    image

    思考一分钟…

    详细过程动画描述:

    image

    详细过程文字描述:

    1. 首先 A(192.168.0.1)通过子网掩码(255.255.255.0)计算出自己与 F(192.168.2.2)并不在同一个子网内,于是决定发送给默认网关(192.168.0.254)

    2. A 通过 ARP 找到 默认网关 192.168.0.254 的 MAC 地址。

    3. A 将源 MAC 地址(AAAA)与网关 MAC 地址(ABAB)封装在数据链路层头部,又将源 IP 地址(192.168.0.1)和目的 IP 地址(192.168.2.2)(注意这里千万不要以为填写的是默认网关的 IP 地址,从始至终这个数据包的两个 IP 地址都是不变的,只有 MAC 地址在不断变化)封装在网络层头部,然后发包

    image

    4. 交换机 1 收到数据包后,发现目标 MAC 地址是 ABAB,转发给路由器1

    5. 数据包来到了路由器 1,发现其目标 IP 地址是 192.168.2.2,查看其路由表,发现了下一跳的地址是 192.168.100.5*

    6. 所以此时路由器 1 需要做两件事,第一件是再次匹配路由表,发现匹配到了端口为 2,于是将其封装到数据链路层,最后把包从 2 号口发出去。

    7. 此时路由器 2 收到了数据包,看到其目的地址是 192.168.2.2,查询其路由表,匹配到端口号为 1,准备从 1 号口把数据包送出去。

    8. 但此时路由器 2 需要知道 192.168.2.2 的 MAC 地址了,于是查看其 arp 缓存,找到其 MAC 地址为 FFFF,将其封装在数据链路层头部,并从 1 号端口把包发出去。

    9. 交换机 3 收到了数据包,发现目的 MAC 地址为 FFFF,查询其 MAC 地址表,发现应该从其 6 号端口出去,于是从 6 号端口把数据包发出去。

    **10.**F 最终收到了数据包!**并且发现目的 MAC 地址就是自己,于是收下了这个包

    HTTP报文传输原理

    利用TCP/IP进行网络通信时,数据包会按照分层顺序与对方进行通信。发送端从应用层往下走,接收端从链路层往上走。从客户端到服务器的数据,每一帧数据的传输的顺序都为:应用层->运输层->网络层->链路层->链路层->网络层->运输层->应用层。

    HTTP报文传输过程

    以一个HTTP请求的传输为例,请求从HTTP客户端(如浏览器)和HTTP服务端应用的传输过程,大致如下图所示:
    在这里插入图片描述

    图:HTTP请求报文的分层传输过程

    数据封装和分用

    接下来,为大家介绍一下数据封装和分用。

    数据通过互联网传输的时候不可能是光秃秃的不加标识,如果这样数据就会乱。所以数据在发送的时候,需要加上特定标识,加上特定标识的过程叫做数据的封装,在数据使用的时候再去掉特定标识,去掉特定标识的过程就叫做分用。TCP/IP协议的数据封装和分用过程,大致如下图所示:
    在这里插入图片描述

    图:TCP/IP协议的数据封装和分用过程

    在数据封装时,数据经过每个层都会打上该层特定标识,添加上头部。

    在传输层封装时,添加的报文首部时要存入一个应用程序的标识符,无论TCP和UDP都用一个16位的端口号来表示不同的应用程序,并且都会将源端口和目的端口存入报文首部中。

    在网络层封装时,IP首部会标识处理数据的协议类型,或者说标识出网络层数据帧所携带的上层数据类型,如TCP、UDP、ICMP、IP、IGMP等等。
    具体来说,会在IP首部中存入一个长度为8位的数值,称作协议域:
    1表示为ICMP协议、2表示为IGMP协议、6表示为TCP协议、17表示为UDP协议、等等。IP首部还会标识发送方地址(源IP)和接收方地址(目标IP)。

    在链路层封装时,网络接口分别要发送和接收IP、ARP和RARP等多种不同协议的报文,因此也必须在以太网的帧首部中加入某种形式的标识,以指明所处理的协议类型,为此,以太网的报文帧的首部也有一个16位的类型域,标识出以太网数据帧所携带的上层数据类型,如IPv4、ARP、IPV6、PPPoE等等。

    数据封装和分用的过程大致为:发送端每通过一层会增加该层的首部,接收端每通过一层则删除该层的首部。

    总体来说,TCP/IP分层管理、数据封装和分用的好处:分层之后若需改变相关设计,只需替换变动的层。各层之间的接口部分规划好之后,每个层次内部的设计就可以自由改动。层次化之后,设计也变得相对简单:各个层只需考虑分派给自己的传输任务。

    TCP/IP与OSI的区别主要有哪些呢?除了TCP/IP与OSI在分层模块上稍有区别,更重要的区别为:OSI参考模型注重“通信协议必要的功能是什么”,而TCP/IP则更强调“在计算机上实现协议应该开发哪种程序”。

    实际上,在传输过程中,数据报文会在不同的物理网络之间传递,还是以一个HTTP请求的传输为例,请求在不同物理网络之间的传输过程,大致如下图所示:
    在这里插入图片描述

    图:HTTP请求在不同物理网络之间的传输过程

    数据包在不同物理网络之间的传输过程中,网络层会通过路由器去对不同的网络之间的数据包进行存储、分组转发处理。构造互连网最简单的方法是把两个或多个网络通过路由器进行连接。路由器可以简单理解为一种特殊的用于网络互连的硬件盒,其作用是为不同类型的物理网络提供连接:以太网、令牌环网、点对点的链接和FDDI(光纤分布式数据接口)等等。

    物理网络之间通过路由器进行互连,随着增加不同类型的物理网络,可能会有很多个路由器,但是对于应用层来说仍然是一样的,TCP协议栈为大家屏蔽了物理层的复杂性。总之,物理细节和差异性的隐藏,使得互联网TCP/IP传输的功能变得非常强大。

    接下来,开始为大家介绍与传输性能有密切关系的内容:TCP传输层的三次握手建立连接,四次挥手释放连接。不过在此之前,还得先介绍一下TCP报文协议。

    TCP协议的报文格式

    在TCP/IP协议栈中,IP协议层只关心如何使数据能够跨越本地网络边界的问题,而不关心数据如何传输。整体TCP/IP协议栈,共同配合一起解决数据如何通过许许多多个点对点通路,顺利传输到达目的地。一个点对点通路被称为一“跳”(hop),通过TCP/IP协议栈,网络成员能够在许多“跳”的基础上建立相互的数据通路。

    传输层TCP协议提供了一种面向连接的、可靠的字节流服务,其数据帧格式,大致如下图所示:
    在这里插入图片描述

    图:传输层TCP协议的数据帧格式

    一个传输层TCP协议的数据帧,大致包含以下字段:

    (一)源端口号

    源端口号表示报文的发送端口,占16位。源端口和源IP地址组合起来,可以标识报文的发送地址。

    (二)目的端口号

    目的端口号表示报文的接收端口,占16位。目的端口和目的IP地址相结合,可以标识报文的接收地址。

    TCP协议是基于IP协议的基础上传输的,TCP报文中的源端口号+源IP,与TCP报文中的目的端口号+目的IP一起,组合起来唯一性的确定一条TCP连接。

    (三)序号(Sequence Number)

    TCP传输过程中,在发送端出的字节流中,传输报文中的数据部分的每一个字节都有它的编号。序号(Sequence
    Number)占32位,发起方发送数据时,都需要标记序号。

    序号(Sequence Number)的语义与SYN控制标志(Control
    Bits)的值有关。根据控制标志(Control Bits)中的SYN是否为1,序号(Sequence
    Number)表达不同的含义:

    (1)当SYN = 1时,当前为连接建立阶段,此时的序号为初始序号ISN((Initial Sequence
    Number),通过算法来随机生成序号;

    (2)当SYN = 0时在数据传输正式开始时,第一个报文的序号为 ISN +
    1,后面的报文的序号,为前一个报文的SN值+TCP报文的净荷字节数(不包含TCP头)。比如,如果发送端发送的一个TCP帧的净荷为12byte,序号为5,则发送端接着发送的下一个数据包的时候,序号的值应该设置为5+12=17。

    在数据传输过程中,TCP协议通过序号(Sequence
    Number)对上层提供有序的数据流。发送端可以用序号来跟踪发送的数据量;接收端可以用序号识别出重复接收到的TCP包,从而丢弃重复包;对于乱序的数据包,接收端也可以依靠序号对其进行排序。

    (四)确认序号(Acknowledgment Number)

    确认序号(Acknowledgment
    Number)标识了报文接收端期望接收的字节序列。如果设置了ACK控制位,确认序号的值表示一个准备接收的包的序列码,注意,它所指向的是准备接收的包,也就是下一个期望接收的包的序列码。

    举个例子,假设发送端(如Client)发送3个净荷为1000byte、起始SN序号为1的数据包给Server服务端,Server每收到一个包之后,需要回复一个ACK响应确认数据包给Client。ACK响应数据包的ACK
    Number值,为每个Client包的为SN+包净荷,既表示Server已经确认收到的字节数,还表示期望接收到的下一个Client发送包的SN序号,具体的ACK值如下图左边的正常传输部分所示。
    在这里插入图片描述

    图:传输过程的确认序号(Acknowledgment Number)值示例图

    在上图的左边部分,Server第1个ACK包的ACK
    Number值为1001,是通过Client第1个包的SN+包净荷=1+1000计算得到,表示期望第2个Client包的SN序号为1001;Server第2个ACK包的ACK
    Number值为2001,为Client第2个包的SN+包净荷=2001,表示期望第3个Server包的SN为2001,以此类推。

    如果发生错误,假设Server在处理Client的第二个发送包异常,Server仍然回复一个ACK
    Number值为1001的确认包,则Client的第二个数据包需要重复发送,具体的ACK值如上图右边的正常传输部分所示。

    只有控制标志的ACK标志为1时,数据帧中的确认序号ACK
    Number才有效。TCP协议规定,连接建立后,所有发送的报文的ACK必须为1,也就是建立连接后,所有报文的确认序号有效。如果是SYN类型的报文,其ACK标志为0,故没有确认序号。

    (五)头部长度

    该字段占用4位,用来表示TCP报文首部的长度,单位是4bit位。其值所表示的并不是字节数,而是头部的所含有的32bit的数目(或者倍数),或者4个字节的倍数,所以TCP头部最多可以有60字节(4*15=60)。没有任何选项字段的TCP头部长度为20字节,所以其头部长度为5,可以通过20/4=5计算得到。

    (六)预留6位

    头部长度后面预留的字段长度为6位,作为保留字段,暂时没有什么用处。

    (七)控制标志

    控制标志(Control
    Bits)共6个bit位,具体的标志位为:URG、ACK、PSH、RST、SYN、FIN。6个标志位的说明,如下表所示。

    表:TCP报文控制标志(Control Bits)说明

    标志位说明
    URG占1位,表示紧急指针字段有效。URG位指示报文段里的上层实体(数据)标记为“紧急”数据。当URG=1时,其后的紧急指针指示紧急数据在当前数据段中的位置(相对于当前序列号的字节偏移量),TCP接收方必须通知上层实体。
    ACK占1位,置位ACK=1表示确认号字段有效;TCP协议规定,接建立后所有发送的报文的ACK必须为1;当ACK=0时,表示该数据段不包含确认信息。当ACK=1时,表示该报文段包括一个对已被成功接收报文段的确认序号Acknowledgment Number,该序号同时也是下一个报文的预期序号。
    PSH占1位,表示当前报文需要请求推(push)操作;当PSH=1时,接收方在收到数据后立即将数据交给上层,而不是直到整个缓冲区满。
    RST占1位,置位RST=1表示复位TCP连接;用于重置一个已经混乱的连接,也可用于拒绝一个无效的数据段或者拒绝一个连接请求。如果数据段被设置了RST位,说明报文发送方有问题发生。
    SYN占1位,在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1。 综合一下,SYN置1就表示这是一个连接请求或连接接受报文。
    FIN占1位,用于在释放TCP连接时,标识发送方比特流结束,用来释放一个连接。当 FIN = 1时,表明此报文的发送方的数据已经发送完毕,并要求释放连接。

    在连接建立的三次握手过程中,若只是单个SYN置位,表示的只是建立连接请求。如果SYN和ACK同时置位为1,表示的建立连接之后的响应。

    (八)窗口大小:

    长度为16位,共2个字节。此字段用来进行流量控制。流量控制的单位为字节数,这个值是本端期望一次接收的字节数。

    (九)校验和:

    长度为16位,共2个字节。对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,接收端用于对收到的数据包进行验证。

    (十)紧急指针:

    长度为16位,2个字节。它是一个偏移量,和SN序号值相加表示紧急数据最后一个字节的序号。

    以上十项内容是TCP报文首部必须的字段,也称固有字段,长度为20个字节。接下来是TCP报文的可选项和填充部分。

    (十一)可选项和填充部分

    可选项和填充部分的长度为4n字节(n是整数),该部分是根据需要而增加的选项。如果不足4n字节,要加填充位,使得选项长度为32位(4字节)的整数倍,具体的做法是在这个字段中加入额外的零,以确保TCP头是32位(4字节)的整数倍。

    最常见的选项字段是MSS(Maximum Segment
    Size最长报文大小),每个连接方通常都在通信的第一个报文段(SYN标志为1的那个段)中指明这个选项字段,表示当前连接方所能接受的最大报文段的长度。

    由于可选项和填充部分不是必须的,所以TCP报文首部最小长度为20个字节。

    至此,TCP报文首部的字段,就全部介绍完了。TCP报文首部的后面,接着的是数据部分,不过数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有TCP首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据,比如在处理超时的过程中,也会发送不带任何数据的报文段。

    总体来说,TCP协议的可靠性,主要通过以下几点来保障:

    (1)应用数据分割成TCP认为最适合发送的数据块。这部分是通过MSS(最大数据包长度)选项来控制的,通常这种机制也被称为一种协商机制,MSS规定了TCP传往另一端的最大数据块的长度。值得注意的是,MSS只能出现在SYN报文段中,若一方不接收来自另一方的MSS值,则MSS就定为536字节。一般来讲,MSS值还是越大越好,这样可以提高网络的利用率。

    (2)重传机制。设置定时器,等待确认包,如果定时器超时还没有收到确认包,则报文重传。

    (3)对首部和数据进行校验。

    (4)接收端对收到的数据进行排序,然后交给应用层。

    (5)接收端丢弃重复的数据。

    (6)TCP还提供流量控制,主要是通过滑动窗口来实现流量控制。

    至此TCP协议的数据帧格式介绍完了。接下来开始为大家重点介绍:TCP传输层的三次握手建立连接,四次挥手释放连接。

    TCP的三次握手

    TCP连接的建立时,双方需要经过三次握手,而断开连接时,双方需要经过四次分手,那么,其三次握手和四次分手分别做了什么呢?又是如何进行的呢?

    通常情况下,建立连接的双方,由一端打开一个监听套接字(ServerSocket)来监听来自请求方的TCP(Socket)连接,当服务器端监听开始时,必须做好准备接受外来的连接,在Java中该操作通过创建一个ServerSocket服务监听套接字实例来完成,此操作会调用底层操作系统(如Linux)的C代码中三个函数socket()、bind()、listen()
    来完成。开始监听之后,服务器端就做好接受外来连接的准备,如果监听到建立新连接的请求,会开启一个传输套接字,称之为被动打开(Passive
    Open)。

    一段简单的服务端监听新连接请求,并且被动打开(Passive
    Open)传输套接字的Java示例代码,具体如下:

    public class SocketServer {
    
    public static void main(String[] args) {
    
    try {
    
    // 创建服务端socket
    
    ServerSocket serverSocket = new ServerSocket(8080);
    
    //循环监听等待客户端的连接
    
    while(true){
    
    //监听到客户端连接,传输套接字被动开启
    
    Socket socket = serverSocket.accept();
    
    //开启线程进行连接的IO处理
    
    ServerThread thread = new ServerThread(socket);
    
    thread.start();
    
    ......
    
    }
    
    } catch (Exception e) {
    
    // 处理异常
    
    e.printStackTrace();
    
    }
    
    }
    
    }
    

    客户端在发起连接建立时,Java代码通过创建Socket实例,调用底层的connect(…)方法,主动打开(Active
    Open)Socket连接。套接字监听方在收到请求之后,监听方和发起方(客户端)之间就会建立一条的连接通道,该通道由双方IP和双方端口所唯一确定。

    一段简单的客户端连接主动打开(Active Open)的Java示例代码,具体如下:

    public class SocketClient {
    
    public static void main(String[] args) throws InterruptedException {
    
    try {
    
    // 和服务器创建连接
    
    Socket socket = new Socket("localhost",8080);
    
    // 写入给监听方的输出流
    
    OutputStream os = socket.getOutputStream();
    
    …..
    
    // 读取监听方的输入流
    
    InputStream is = socket.getInputStream();
    
    …..
    
    } catch (Exception e) {
    
    e.printStackTrace();
    
    }
    
    }
    
    }
    

    三次握手过程

    TCP连接的建立时,双方需要经过三次握手,具体过程如下:

    (1)第一次握手:Client进入SYN_SENT状态,发送一个SYN帧来主动打开传输通道,该帧的SYN标志位被设置为1,同时会带上Client分配好的SN序列号,该SN是根据时间产生的一个随机值,通常情况下每间隔4ms会加1。除此之外,SYN帧还会带一个MSS(最大报文段长度)可选项的值,表示客户端发送出去的最大数据块的长度。

    (2)第二次握手:Server端在收到SYN帧之后,会进入SYN_RCVD状态,同时返回SYN+ACK帧给Client,主要目的在于通知Client,Server端已经收到SYN消息,现在需要进行确认。Server端发出的SYN+ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment
    Number)值被设置为Client的SN+1;SYN+ACK帧的SYN标志位被设置为1,SN值为Server端生成的SN序号;SYN+ACK帧的MSS(最大报文段长度)表示的是Server端的最大数据块长度。

    (3)第三次握手:Client在收到Server的第二次握手SYN+ACK确认帧之后,首先将自己的状态会从SYN_SENT变成ESTABLISHED,表示自己方向的连接通道已经建立成功,Client可以发送数据给Server端了。然后,Client发ACK帧给Server端,该ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment
    Number)值被设置为Server端的SN序列号+1。还有一种情况,Client可能会将ACK帧和第一帧要发送的数据,合并到一起发送给Server端。

    (4)Server端在收到Client的ACK帧之后,会从SYN_RCVD状态会进入ESTABLISHED状态,至此,Server方向的通道连接建立成功,Server可以发送数据给Client,TCP的全双工连接建立完成。

    三次握手的图解

    三次握手的交互过程,具体如下图所示:
    在这里插入图片描述

    图:TCP建立的连接时三次握手示意图

    Client和Server完成了三次握手后,双方就进入了数据传输的阶段。数据传输完成后,连接将断开,连接断开的过程需要经历四次挥手。

    TCP的四次挥手

    业务数据通信完成之后,TCP连接开始断开(或者拆接)的过程,在这个过程中连接的每个端的都能独立地、主动的发起,断开的过程TCP协议使用了四路挥手操作。

    四次挥手具体过程

    四次挥手具体过程,具体如下:

    (1)第一次挥手:主动断开方(可以是客户端,也可以是服务器端),向对方发送一个FIN结束请求报文,此报文的FIN位被设置为1,并且正确设置Sequence
    Number(序列号)和Acknowledgment
    Number(确认号)。发送完成后,主动断开方进入FIN_WAIT_1状态,这表示主动断开方没有业务数据要发送给对方,准备关闭SOCKET连接了。

    (2)第二次挥手:正常情况下,在收到了主动断开方发送的FIN断开请求报文后,被动断开方会发送一个ACK响应报文,报文的Acknowledgment
    Number(确认号)值为断开请求报文的Sequence Number
    (序列号)加1,该ACK确认报文的含义是:“我同意你的连接断开请求”。之后,被动断开方就进入了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知高层的应用进程,对方向本地方向的连接已经关闭,对方已经没有数据要发送了,若本地还要发送数据给对方,对方依然会接受。被动断开方的CLOSE-WAIT(关闭等待)还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。

    主动断开方在收到了ACK报文后,由FIN_WAIT_1转换成FIN_WAIT_2状态。

    (3)第三次挥手:在发送完成ACK报文后,被动断开方还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截止后,被动断开方会向主动断开方发送一个FIN+ACK结束响应报文,表示被动断开方的数据都发送完了,然后,被动断开方进入LAST_ACK状态。

    (4)第四次挥手:主动断开方收在到FIN+ACK断开响应报文后,还需要进行最后的确认,向被动断开方发送一个ACK确认报文,然后,自己就进入TIME_WAIT状态,等待超时后最终关闭连接。处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,如果期间没有收到其他报文,则证明对方已正常关闭,主动断开方的连接最终关闭。

    被动断开方在收到主动断开方的最后的ACK报文以后,最终关闭了连接,自己啥也不管了。

    四次挥手图解

    四次挥手的全部交互过程,具体如下图所示:
    在这里插入图片描述

    图:TCP建立的连接时四次挥手的示意图

    处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,才真正关闭连接通道,其等待的时间为什么是2MSL呢?

    2MSL翻译过来就是两倍的MSL。MSL全称为Maximum Segment
    Lifetime,指的是一个TCP报文片段在网络中最大的存活时间,具体来说,2MSL对应于一次消息的来回(一个发送和一个回复)所需的最大时间。如果直到2MSL,主动断开方都没有再一次收到对方的报文(如FIN报文),则可以推断ACK已经被对方成功接收,此时,主动断开方将最终结束自己的TCP连接。所以,TCP的TIME_WAIT状态也称为2MSL等待状态。

    有关MSL的具体的时间长度,在RFC1122协议中推荐为2分钟。在SICS(瑞典计算机科学院)开发的一个小型开源的TCP/IP协议栈——LwIP开源协议栈中MSL默认为1分钟。在源自Berkeley的TCP协议栈实现中MSL默认长度为30秒。总体来说,TIME_WAIT(2MSL)等待状态的时间长度,一般维持在1-4分钟之间。

    通过三次握手建立连接和四次挥手拆除连接,一次TCP的连接建立及拆除,至少进行7次通信,可见其成本是很高的。

    三次握手、四次挥手的常见面试题

    有关TCP的连接建立的三次握手及拆除过程的四次挥手的面试问题,是技术面试过程中的出现频率很高的重点和难点问题,常见问题大致如下:

    问题(1):为什么关闭连接的需要四次挥手,而建立连接却只要三次握手呢?

    关闭连接时,被动断开方在收到对方的FIN结束请求报文时,很可能业务数据没有发送完成,并不能立即关闭连接,被动方只能先回复一个ACK响应报文,告诉主动断开方:“你发的FIN报文我收到了,只有等到我所有的业务报文都发送完了,我才能真正的结束,在结束之前,我会发你FIN+ACK报文的,你先等着”。所以,被动断开方的确认报文,需要拆开成为两步,故总体就需要四步挥手。

    而在建立连接场景中,Server端的应答可以稍微简单一些。当Server端收到Client端的SYN连接请求报文后,其中ACK报文表示对请求报文的应答,SYN报文用来表示服务端的连接也已经同步开启了,而ACK报文和SYN报文之间,不会有其他报文需要发送,故而可以合二为一,可以直接发送一个SYN+ACK报文。所以,在建立连接时,只需要三次握手即可。

    问题(2):为什么连接建立的时候是三次握手,可以改成两次握手吗?

    三次握手完成两个重要的功能:一是双方都做好发送数据的准备工作,而且双方都知道对方已准备好;二是双方完成初始SN序列号的协商,双方的SN序列号在握手过程中被发送和确认。

    如果把三次握手改成两次握手,可能发生死锁。两次握手的话,缺失了Client的二次确认ACK帧,假想的TCP建立的连接时二次挥手,可以如下图所示:
    在这里插入图片描述

    图:假想的TCP建立的连接时二次握手的示意图

    在假想的TCP建立的连接时二次握手过程中,Client发送Server发送一个SYN请求帧,Server收到后发送了确认应答SYN+ACK帧。按照两次握手的协定,Server认为连接已经成功地建立了,可以开始发送数据帧。这个过程中,如果确认应答SYN+ACK帧在传输中被丢失,Client没有收到,Client将不知道Server是否已准备好,也不知道Server的SN序列号,Client认为连接还未建立成功,将忽略Server发来的任何数据分组,会一直等待Server的SYN+ACK确认应答帧。而Server在发出的数据帧后,一直没有收到对应的ACK确认后就会产生超时,重复发送同样的数据帧。这样就形成了死锁。

    问题(3):为什么主动断开方在TIME-WAIT状态必须等待2MSL的时间?

    原因之一:主动断开方等待2MSL的时间,是为了确保两端都能最终关闭。假设网络是不可靠的,被动断开方发送FIN+ACK报文后,其主动方的ACK响应报文有可能丢失,这时候的被动断开方处于LAST-ACK状态的,由于收不到ACK确认被动方一直不能正常的进入CLOSED状态。在这种场景下,被动断开方会超时重传FIN+ACK断开响应报文,如果主动断开方在2MSL时间内,收到这个重传的FIN+ACK报文,会重传一次ACK报文,后再一次重新启动2MSL计时等待,这样,就能确保被动断开方能收到ACK报文,从而能确保被动方顺利进入到CLOSED状态。只有这样,双方都能够确保关闭。反过来说,如果主动断开方在发送完ACK响应报文后,不是进入TIME_WAIT状态去等待2MSL时间,而是立即释放连接,则将无法收到被动方重传的FIN+ACK报文,所以不会再发送一次ACK确认报文,此时处于LAST-ACK状态的被动断开方,无法正常进入到CLOSED状态。

    原因之二:防止“旧连接的已失效的数据报文”出现在新连接中。主动断开方在发送完最后一个ACK报文后,再经过2MSL,才能最终关闭和释放端口,这就意味着,相同端口的新TCP新连接,需要在2MSL的时间之后,才能够正常的建立。2MSL这段时间内,旧连接所产生的所有数据报文,都已经从网络中消失了,从而,确保了下一个新的连接中不会出现这种旧连接请求报文。

    问题(4):如果已经建立了连接,但是Client端突然出现故障了怎么办?

    TCP还设有一个保活计时器,Client端如果出现故障,Server端不能一直等下去,这样会浪费系统资源。每收到一次Client客户端的数据帧后,Server端都的保活计时器会复位。计时器的超时时间通常是设置为2小时,若2小时还没有收到Client端的任何数据帧,Server端就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,Server端就认为Client端出了故障,接着就关闭连接。如果觉得保活计时器的两个多小时的间隔太长,可以自行调整TCP连接的保活参数。

    展开全文
  • STM32+W5500 Modbus-TCP协议功能实现

    热门讨论 2014-04-04 23:11:42
    经过这几天的学习与调试,终于在STM32F103VCT6+W5500(SPI1)+Freemodbus 平台上,实现Modbus-TCP协议的功能。其实很简单,只要熟悉Modbus-RTU通讯,明白Modbus帧的结构等,Modbus-TCP只是在原来的帧结构上加个头,去...
  • UDP协议和TCP协议

    千次阅读 2020-07-03 18:26:45
    UDP协议和TCP协议都是传输层的协议,TCP协议提供可靠的通信传输,而UDP则是常常被用于让广播和细节控制的交给应用的通信传输 UDP协议:不保证安全,但是性能比较好 UDP的传输过程类似于寄信,仅仅在乎的是信件有...

    UDP协议和TCP协议都是传输层的协议,TCP协议提供可靠的通信传输,而UDP则是常常被用于让广播和细节控制的交给应用的通信传输

    UDP协议:不保证安全,但是性能比较好

    在这里插入图片描述

    UDP的传输过程类似于寄信,仅仅在乎的是信件有没有发送,不在乎是否接收
    特点:

    • 无连接:仅仅知道接收一方的IP地址和和端口号就可以进行发送
    • 不可靠:没有确认的机制,没有重传的机制
    • 面向数据报:不能够灵活的控制读写数据的次数和数量,应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并,倘若我们需要传输的数据超过64K,我们就需要在应用层手动的分包,多次发送,并且在接收端手动的拼装;
    • 有接收缓冲区,没有发送缓冲区:这个接收缓冲区不能保证接收到的UDP的数据报的顺序和发送UDP数据报的顺序是一样的,当缓冲区满的时候,再接收到的数据就会被直接丢弃

    TCP协议:安全和性能的一个平衡

    TCP全称为 “传输控制协议(Transmission Control Protocol”)
    在这里插入图片描述

    • 4位TCP报头长度: 表示该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 = 60
    • 6位标志位:
      URG: 紧急指针是否有效
      ACK: 确认号是否有效
      PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
      RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
      SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段
      FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段

    特点:

    • 有连接:双方建立好连接,才可以进行通信
    • 可靠:
    • 面向字节流:
    • 具有接收和发送缓冲区

    1、确认应答机制

    基于序列号和确认序列号实现,TCP将每个字节的数据进行编号,每一个ACK都带有一个对应的确认序号,告诉发送的一方,下次发送从哪个地方开始发送
    在这里插入图片描述
    2、超时重传机制

    倘若发送端发送的数据,因为网络等问题,无法到达接收端;如果发送端在一个特定的时间啊i内没能接收到接收端发送的确认应答,这时发送端会重新发送刚才的数据。

    但是也有一种情况,接收端发送的ACK丢掉,这时发送端误以为接收端没能收到,这时会继续发送数据,接收端会收到很多重复的数据,这时我们就可以利用前面说的序列号进行去重
    在这里插入图片描述
    系统基于TCP协议实现,动态的计算报文的最大生存时间(MSL),超时时间设置为(2MSL)
    超过超时时间,表示丢包(发送的数据报,接收的确认数据报),此时需要重新发送数据报(系统中发送缓冲区中保存有数据可以重发)
    3、连接管理机制

    • 建立连接(三次握手)
      在这里插入图片描述
    • 关闭连接(四次挥手)

    在这里插入图片描述
    CLOSE-WAIT状态:此时客户端已经发出释放连接的请求,已经没有数据传输过来,但是服务端还有数据没有发送完,这时就会有这个状态
    客户端需要等待2MSL(TIME_WAIT状态)
    因为第四次的响应报文ACK可能会丢失,会导致服务端无法关闭连接,需要服务端重新发送FIN请求,所以客户端必须等待最大的超过时间(2MSL),2MSL是服务端重新发送FIN报文和客户端发出ACK确认报文所能保持有效的一个最大时长、

    为什么要三次握手和四次挥手
    在建立连接时,服务端接收到SYN请求之后,服务端并不需要做什么准备,所以就可以一次性返回SYN+ACK报文
    在关闭连接的时候,服务端收到客户端的FIN报文,并不会立即关闭,只是回复ACK,告诉给客户端我收到了你的关闭请求,服务端可能有数据还没有数据还没有传输完,只能等数据传输完之后,才会发送FIN请求,这两个地方要分开发送。

    4、滑动窗口:处于发送端,提高性能
    这种机制被称为“高速重发控制”(“快重传”)
    滑动窗口是为了改善性能,在确认应答中,每每发送一个数据报,都要进行确认应答,这样性能会比较差,所以就一次发送多个数据报。
    窗口的大小就是不需要等待应答就可以继续发送数据报的最大值,也就是说将滑动窗口里的数据报看成一个组,倘若里面有四个段,每当收到一个应答,滑动窗口就向后移一个,依次类推。
    操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉;

    数据报丢失

    • 应答数据报丢失:这种情况可以根据后面的ACK进行确认
    • 发送的数据报丢失
      在这里插入图片描述
      当1001-1000报文段丢失之后, 发送端会一直收到 1001 这样的ACK, 此时如果发送端主机连续三次收到了同样一个 “1001” 这样的应答, 就会将对应的数据 1001 - 2000 重新发送;这个时候接收端收到了 1001 之后, 再次返回的ACK就是7001了(因为2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区

    5、流量控制
    接收端通过TCP协议头中的“窗口大小”字段,高速发送端,发送数据的大小
    这样做的目的就是为了防止接收缓冲区被打满,再次接收数据就直接丢弃,,使用窗口大小可以告诉发送端发送数据的大小
    6、拥塞控制
    网络上存在很多的计算机,可能当前的网络状态比较拥堵,在不清楚目前网络的情况下,贸然发送大量的数据,无疑是雪上加霜
    对于这个问题,TCP引入了慢启动的机制,先发送少量的数据,摸清楚当前的网络状态,再决定按照多大的速度传输数据
    拥塞窗口的初始值为1,以慢启动指数增长的方式,达到一定的阈值就转变为线性增长的方式
    7、延迟应答机制
    当接收到多个数据报的时候,不针对每条数据报进行响应,而是延迟一定的时间,这样缓冲区的数据可以很快被处理掉,这样返回的窗口的大小的字段可以设置更大,传输效率会变高
    8、粘包问题
    避免粘包问题就是要明确包的边界
    UDP不会出现粘包问题,站在应用层的站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收. 不会出现"半个"的情况.

    TCP/UDP对比

    TCP是可靠连接, TCP和UDP之间的优点和缺点, 不能简单, 绝对的进行比较,根据不同的需求进行判断哪种协议是最优的
    TCP用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景;
    UDP用于对高速传输和实时性要求较高的通信领域, 例如, 早期的QQ, 视频传输等. 另外UDP可以用于广播;

    展开全文
  • TCP协议与UDP协议

    万次阅读 2019-02-19 19:30:54
    首先咱们弄清楚,TCP协议和UCP协议与TCP/IP协议的联系,很多人犯糊涂了,一直都是说TCP/IP协议与UDP协议的区别,我觉得这是没有从本质上弄清楚网络通信! TCP/IP协议是一个协议簇。里面包括很多协议的。UDP只是其中...

    概念理解

    首先咱们弄清楚,TCP协议和UCP协议与TCP/IP协议的联系,很多人犯糊涂了,一直都是说TCP/IP协议与UDP协议的区别,我觉得这是没有从本质上弄清楚网络通信!
    TCP/IP协议是一个协议簇。里面包括很多协议的。UDP只是其中的一个。之所以命名为TCP/IP协议,因为TCP,IP协议是两个很重要的协议,就用他两命名了。
    TCP/IP协议集包括应用层,传输层,网络层,网络访问层。
    其中应用层包括:
    超文本传输协议(HTTP):万维网的基本协议.
    文件传输(TFTP简单文件传输协议):
    远程登录(Telnet),提供远程访问其它主机功能,它允许用户登录
    internet主机,并在这台主机上执行命令.
    网络管理(SNMP简单网络管理协议),该协议提供了监控网络设备的方法,以及配置管理,统计信息收集,性能管理及安全管理等.
    域名系统(DNS),该系统用于在internet中将域名及其公共广播的网络节点转换成IP地址.
    其次网络层包括:
    Internet协议(IP)
    Internet控制信息协议(ICMP)
    地址解析协议(ARP)
    反向地址解析协议(RARP)
    最后说网络访问层:网络访问层又称作主机到网络层(host-to-network).网络访问层的功能包括IP地址与物理地址硬件的映射,以及将IP封装成帧.基于不同硬件类型的网络接口,网络访问层定义了和物理介质的连接.
    当然我这里说得不够完善,TCP/IP协议本来就是一门学问,每一个分支都是一个很复杂的流程,但我相信每位学习软件开发的同学都有必要去仔细了解一番。
    下面我着重讲解一下TCP协议和UDP协议的区别。

    在这里插入图片描述

    常用的熟知端口号

    应用程序FTPTFTPTELNETSMTPDNSHTTPSSHMYSQL
    熟知端口21,206923255380223306
    传输层协议TCPUDPTCPTCPUDPTCP

    TCP(Transmission Control Protocol,传输控制协议)

    TCP的概述
    TCP把连接作为最基本的对象,每一条TCP连接都有两个端点,这种断点我们叫作套接字(socket),它的定义为端口号拼接到IP地址即构成了套接字,例如,若IP地址为192.3.4.16 而端口号为80,那么得到的套接字为192.3.4.16:80。

    TCP报文首部

    1. 源端口和目的端口,各占2个字节,分别写入源端口和目的端口;
    2. 序号,占4个字节,TCP连接中传送的字节流中的每个字节都按顺序编号。例如,一段报文的序号字段值是 301 ,而携带的数据共有100字段,显然下一个报文段(如果还有的话)的数据序号应该从401开始;
    3. 确认号,占4个字节,是期望收到对方下一个报文的第一个数据字节的序号。例如,B收到了A发送过来的报文,其序列号字段是501,而数据长度是200字节,这表明B正确的收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701;
    4. 数据偏移,占4位,它指出TCP报文的数据距离TCP报文段的起始处有多远;
    5. 保留,占6位,保留今后使用,但目前应都位0;
    6. 紧急URG,当URG=1,表明紧急指针字段有效。告诉系统此报文段中有紧急数据;
    7. 确认ACK,仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1;
    8. 推送PSH,当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1;
    9. 复位RST,当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接;
    10. 同步SYN,在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1;
    11. 终止FIN,用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放;
    12. 窗口,占2字节,指的是通知接收方,发送本报文你需要有多大的空间来接受;
      13 .检验和,占2字节,校验首部和数据这两部分;
    13. 紧急指针,占2字节,指出本报文段中的紧急数据的字节数;
    14. 选项,长度可变,定义一些其他的可选的参数。

    是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来,其中的过程非常复杂,只简单的描述下这三次对话的简单过程:主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。

    TCP连接的建立(三次握手)

    在这里插入图片描述

    最开始的时候客户端和服务器都是处于CLOSED状态。主动打开连接的为客户端,被动打开连接的是服务器。
    
    1. TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;
    2. TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时, TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
    3. TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。
    4. TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号
    5. 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。
      在这里插入图片描述

    为什么TCP客户端最后还要发送一次确认呢?

    一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。
    如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

    如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

    TCP连接的释放(四次挥手)

    在这里插入图片描述

    数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。
    
    1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
    2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
    3. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
    4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
    5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
    6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
      在这里插入图片描述

    为什么客户端最后还要等待2MSL?

    MSL(Maximum Segment Lifetime),TCP允许不同的实现可以设置不同的MSL值。
    
    第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,
         我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,
         于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
    
    第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,
    	在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。
    

    为什么建立连接是三次握手,关闭连接确是四次挥手呢?

    建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。 
    而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,
    也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。
    

    UDP(User Data Protocol,用户数据报协议)

    (1) UDP是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。
    (2) 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。
    (3) UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。
    (4) 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。
    (5)UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态表(这里面有许多参数)。
    (6)UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。
    我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。

    TCP与UDP的区别:

    1.基于连接与无连接;
    2.对系统资源的要求(TCP较多,UDP少);
    3.UDP程序结构较简单;
    4.流模式与数据报模式 ;
    5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。

    其他链接
    TCP三次握手四次挥手详解
    TCP协议与UDP协议的区别

    展开全文
  • Modbus TCP协议解析

    千次阅读 2019-07-07 17:13:54
    在之前的文章中,我们介绍了Modbus RTU协议的内容和...文章目录一、Modbus TCP协议解析Modbus TCP和Modbus RTU的区别Modbus TCP协议的构成Modbus TCP的报文头Modbus TCP的功能码Modbus TCP的数据码二、持续更新:Mo...
  • LwIP中TCP协议的实现

    千次阅读 2019-09-16 23:58:25
    与其他协议一样,为了描述TCP协议,LwIP定义了一个名字叫tcp_pcb的结构体,可以称之为TCP控制块,其内定义了大量的成员变量,基本定义了整个TCP协议运作过程的所有需要的东西,如发送窗口、接收窗口、数据缓冲区。...
  • 五分钟读懂TCP 协议——TCP协议简介

    万次阅读 多人点赞 2017-06-11 23:48:03
    TCP 是互联网核心协议之一,本文介绍它的基础知识。一、TCP 协议的作用互联网由一整套协议构成。TCP 只是其中的一层,有着自己的分工。(图片说明:TCP 是以太网协议和 IP 协议的上层协议,也是应用层协议的下层协议...
  • ModbusTcp协议详解

    千次阅读 2020-05-19 13:08:12
    1996年施耐德公司推出基于以太网TCP/IP的Modbus协议:ModbusTCP。 Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。 标准的Modbus协议物理层接口有RS232、RS422、RS485和以太网接口,采用...
  • TCP协议大全

    千次阅读 2018-08-21 16:22:53
    TCP协议笔记 上一篇文章已经讲过啦TCP/IP协议族了,大概多TCP协议有了大概的了解。 在网页中HTTP请求获取内容之前是需要先建立TCP连接的,而TCP建立连接需要三次握手,该篇文章将讲解TCP的工作原理。 TCP特性 ...
  • 实验九 TCP 协议分析实验

    千次阅读 2020-12-30 15:58:21
    实验九 TCP 协议分析实验 1.TCP 协议介绍** TCP 是传输控制协议 (Transmission Control Protocal)的缩写,提供面向连接的可靠的传输服务。在TCP/IP 体系中,HTTP、FTP、SMTP 等协议都是使用TCP 传输方式的。 (1...
  • TCP协议和UDP协议的区别及其应用

    万次阅读 多人点赞 2018-07-02 19:34:45
    下面是TCP和UDP的简单程序: 简单的TCP网络程序 https://blog.csdn.net/qq_37941471/article/details/80738319 ...接着我们通过这两个协议来更直接的理解一下TCP协议和UDP协议: 这两个协议...
  • 2,Tcp协议对应于传输层 3,http协议是在Tcp协议之上建立的,http在发起请求时通过tcp协议建立起连接服务器的通道,请求结束后,立即断开tcp连接 说明:从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单...
  • DNS用的是TCP协议还是UDP协议【转】

    千次阅读 2019-06-28 16:35:05
    DNS占用53号端口,同时...DNS在区域传输的时候使用TCP协议,其他时候使用UDP协议。 DNS区域传输的时候使用TCP协议: 1.辅域名服务器会定时(一般3小时)向主域名服务器进行查询以便了解数据是否有变动。如有变...
  • 什么是TCP协议

    万次阅读 多人点赞 2018-04-07 12:19:30
    TCP(Transmission Control Protocol 传输控制协议)是一种面向连接(连接导向)的、可靠的、 基于IP的传输层协议TCP在IP报文的协议号是6。TCP是一个超级麻烦的协议,而它又是互联网的基础,也是每个程序员必备的基本...
  • TCP协议为什么需要三次握手?

    万次阅读 多人点赞 2021-09-02 16:39:14
    TCP实现原理和为什么需要三次握手?两次握手不可以?四次握手不可以?读者可以带着疑问,看一遍本篇博客的详细讲解。首先解释原因之前还是要先复习一下TCP的基本知识和三次握手协议
  • TCP协议--TCP头部

    千次阅读 2017-11-25 17:19:10
    TCP协议的概念  TCP和UDP是传输层的两个主要协议。TCP相对于UDP来说,是面向连接、字节流和可靠传输。  (1) 面向连接   使用TCP通信的双方必须先建立起连接,然后才能开始数据的读写。建立连接后双方的系统...
  • TCP协议详解

    万次阅读 多人点赞 2018-08-29 17:22:32
    续接上篇:IP协议详解 5、TCP服务 TCP将用户数据打包构成报文段,它发送数据时启动一个定时器,另一端收到数据进行确认,对失序的数据重新排序,丢弃重复的数据。TCP提供一种面向连接的可靠的字节流服务,面向连接...
  • Wireshark抓包——TCP协议分析

    万次阅读 多人点赞 2019-06-11 00:34:17
    通过本次实验,掌握使用Wireshark抓取TCP/IP协议数据包的技能,能够深入分析TCP帧格式及“TCP三次握手”。通过抓包和分析数据包来理解TCP/IP协议,进一步提高理论联系实践的能力。 二. 实验内容 1.本次实验重点:...
  • UDP协议转换TCP协议

    千次阅读 2021-01-12 09:29:37
    UDP协议转换TCP协议 项目背景:项目类似中间件 作用是数据搬运 ,可以搬运文件 ,视频,音频 。从互联网把数据搬运到内网中。有的时候拉取视频流数据的时候 使用UDP拉取会存在丢包的情况 通过TCP拉拉取视频流数据 再...
  • TCP状态转换图详解 tcp协议讲解

    万次阅读 2018-09-27 15:38:28
    在前面,已经介绍了TCP协议的三路握手和四次挥手。如下图所示,TCP通信过程包括三个步骤:建立TCP连接通道(三次握手)、数据传输、断开TCP连接通道(四次挥手)。     &...
  • 若读者对TCP通讯不够熟悉,应首先学习相关内容,可以下载示例 TCPTest。 本示例使用VS2012编写">此示例用于演示与OMRON PLC设备进行以太网 FINS 通讯。 示例中只演示了对线圈值(布尔值)的读写操作。 详细的帧...
  • DNS用的是TCP协议还是UDP协议

    千次阅读 2020-05-12 10:04:01
    DNS用的是TCP协议还是UDP协议 DNS 查询以各种不同的方式进行解析。客户机有时也可通过使用从以前查询获得的缓存信息就地应答查询。DNS 服务器可使用其自身的资源记录信息缓存来应答查询,也可代表请求客户机来查询...
  • TCP协议实现原理

    千次阅读 2019-04-04 16:57:08
    TCP协议实现原理 TCP协议是端到端的传输控制协议,之所以是“端到端”的协议,是因为”路由“是由IP协议负责的,TCP协议负责为两个通信端点提供可靠性保证,这个可靠性不是指一个端点发送的数据,另一个端点肯定能...
  • HTTP协议、TCP协议、IP协议和TCP/IP协议区别

    万次阅读 多人点赞 2017-05-02 17:31:20
    TCP/IP协议看上去好像是TCP协议和IP协议,但其实并不一样 TCP/IP协议叫做网络通信协议,它包括上百个协议,而HTTP协议、TCP协议、IP协议只是TCP/IP协议中的一部分. TCP/IP协议是用于计算机通信的一组协议,我们通常称...
  • modbusTCP协议java实现(带注释)

    热门讨论 2013-05-02 12:19:49
    modbusTCP协议java实现(带注释)
  • TCP协议特点

    千次阅读 2020-09-14 15:05:01
    TCP是面向连接的运输层协议 每一条TCP连接只能有两个端点(endpoint),点对点 TCP提供可靠交付的服务,无差错、不丢失、不重复,并且按序到达 TCP提供全双工通信(双向通信) 面向字节流。‘面向字节流’的含义是:...
  • 基于VC++的Modbus TCP协议实现

    热门讨论 2013-07-30 09:06:53
    Modbus TCP协议的VC++实现,用VC++实现Modbus

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 732,137
精华内容 292,854
关键字:

tcp协议