网络编程万字详解

网络编程

网络互联

随着时代的发展,越来越需要计算机之间互相通信,共享软件和数据,即以多个计算机协同工作来完成业务,就有了网络互连。
网络互连:将多台计算机连接在一起,完成数据共享。
数据共享本质是网络数据传输,即计算机之间通过网络来传输数据,也称为网络通信。根据网络互连的规模不同,可以划分为局域网和广域网。

局域网LAN

局域网,即Local Area Network,简称LAN。
Local即标识了局域网是本地,局部组建的一种私有网络。
局域网内的主机之间能方便的进行网络通信,又称为内网;局域网和局域网之间在没有连接的情况下,是无法通信的。

image-20220411101115782

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZHfxIeTa-1650593480277)(E:/MarkDown/note.1/image/image-20220411101138518.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rqdEa0do-1650593480278)(E:/MarkDown/note.1/image/image-20220411101211764.png)]

所以其实第三种通过路由器来进行连接的局域网是比较常见的

路由器跟交换机的区别有一些:

1 路由器上的口分成LAN 和 WAN 口   lan口是提供给主机来进行连接   WAN口相当于又去插入另一个路由器的LAN,可以理解为另一个路由器的主机设备

2 交换机的口都是一致的,都是通过连接主机设备进行构造局域网的

3 在后面的网络编程中学到 交换机包含了物理层和数据链路层  而路由器比交换机多包含一层网络层
上述讨论的区别,局限于"传统”的交换机和路由器~
实际上,真实的交换机和路由器,之间的界限,已经越来越模糊了.路由器的很多功能,交换机也有.
交换机的很多功能,路由器也有~~
通过路由器/交换机,组建起来的这些都叫做局域网~~
广域网其实和局域网之间,没有明确界限.
认为比较大的局域网,就可以称为"广域网"
全世界最大的广域网,叫做Internet(因特网)

广域网WAN

广域网,即 Wide Area Network,简称WAN。
通过路由器,将多个局域网连接起来,在物理上组成很大范围的网络,就形成了广域网。广域网内部的
局域网都属于其子网

网络通信基础

网络互连的目的是进行网络通信,也即是网络数据传输,更具体一点,是网络主机中的不同进程间,基
于网络传输数据。
那么,在组建的网络中,如何判断到底是从哪台主机,将数据传输到那台主机呢?这就需要使用IP地址
来标识

IP地址

概念

IP地址主要用于标识网络主机、其他网络设备(如路由器)的网络地址。简单说,IP地址用于定位主机的网络地址。
就像我们发送快递一样,需要知道对方的收货地址,快递员才能将包裹送到目的地。
格式

IP地址是一个32位的二进制数,通常被分割为4个8位二进制数”(也就是4个字节),如:01100100.00000100.00000101.00000110。

通常用“点分十进制”的方式来表示,即 a.b.c.d 的形式(a,b,c,d都是0~255之间的十进制整数)。如:
100.4.5.6

特殊IP

127.*的IP地址用于本机环回(loop back)测试,通常是127.0.0.1
本机环回主要用于本机到本机的网络通信(系统内部为了性能,不会走网络的方式传输),对于开发网络通信的程序(即网络编程)而言,常见的开发方式都是本机到本机的网络通信。
IP地址解决了网络通信时,定位网络主机的问题,但是还存在一个问题,传输到目的主机后,由哪个进程来接收这个数据呢?这就需要端口号来标识。

端口号

描述的某台主机上某个程序的标志

端口号本质上是一个2个字节(16位)的无符号整数.0-65535
3306 mysql默认的端口号.
服务器程序在启动的时候,就需要绑定上一个端口号.以便客户端程序来访问~

概念
在网络通信中,IP地址用于标识主机网络地址,端口号可以标识主机中发送数据、接收数据的进程。简单说:端口号用于定位主机中的进程。
类似发送快递时,不光需要指定收货地址(IP地址),还需要指定收货人(端口号)。格式
端口号是0~65535范围的数字,在网络通信中,进程可以通过绑定一个端口号,来发送及接收网络数据。

注意事项
两个不同的进程,不能绑定同一个端口号,但一个进程可以绑定多个端口号。
    为什么一个进程可以绑定多个端口号?
    答:因为一个进程可以打开多个文件描述符,而每个文件描述符都对应一个端口号,所以一个进程可以绑定多个端口号
     

了解:
一个进程启动后,系统会随机分配一个端口(启动端口)
程序代码中,进行网络编程时,需要绑定端口号(收发数据的端口)来发送、接收数据。
进程绑定一个端口号后,fork一个子进程,可以实现多个进程绑定一个端口号,但不同的进程不能绑定同一个端口号。

问题:
有了IP地址和端口号,可以定位到网络中唯一的一个进程,但还存在一个问题,网络通信是基于二进制
0/1数据来传输,如何告诉对方发送的数据是什么样的呢?

网络通信传输的数据类型可能有多种:图片,视频,文本等。同一个类型的数据,格式可能也不同,如
发送一个文本字符串“你好!”:如何标识发送的数据是文本类型,及文本的编码格式呢?
基于网络数据传输,需要使用协议来规定双方的数据格式  

认识协议

概念:
协议,网络协议的简称,网络协议是网络通信(即网络数据传输)经过的所有网络设备都必须共同遵从
的一组约定、规则。如怎么样建立连接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互
通信交流。通常由三要素组成:

1.语法:即数据与控制信息的结构或格式;
类似打电话时,双方要使用同样的语言:普通话

2.语义︰即需要发出何种控制信息,完成何种动作以及做出何种响应;
语义主要用来说明通信双方应当怎么做。用于协调与差错处理的控制信息。
类似打电话时,说话的内容。一方道:你瞅啥?另一方就得有对应的响应:瞅你咋的

3.时序,即事件实现顺序的详细说明。
时序定义了何时进行通信,先讲什么,后讲什么,讲话的速度等。比如是采用同步传输还是异步传输。
女生和男生的通话,总是由男生主动发起通话,而总是在男生恋恋不舍的时候,由女生要求结束通话。

协议(protocol)最终体现为在网络上传输的数据包的格式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tmGGqfMz-1650593480278)(E:/MarkDown/note.1/image/image-20220411103712692.png)]

学习网络原理实际上就是在学习各种协议

关于协议分层~~
网络通信这个过程,其实很复杂,里面有很多很多的细节.~
如果就只通过一个协议,来约定所有的细节,这个协议就会非常庞大,复杂~~
更好的办法,就是把一个大的复杂的协议,拆成多个小的,更简单的协议,每个协议,负责一部分工作~~
(就和写代码一样.写一个复杂的程序,不能指望说。一个文件把所有的代码都装进去.把这个代码拆分成多个更小的,更简单的文件,每个文件负责一部分工作)

计算机之间的传输媒介是光信号和电信号。通过"频率"和"强弱"来表示0和1这样的信息。
要想传递
各种不同的信息,就需要约定好双方的数据格式。
·计算机生产厂商有很多;
·计算机操作系统,也有很多;
·计算机网络硬件设备,还是有很多;
如何让这些不同厂商之间生产的计算机能够相互顺畅的通信?就需要有人站出来,约定一个共同的标准,大家都来遵守,这就是网络协议;
知名协议的默认端口
系统端口号范围为0~65535,其中:0~1023为知名端口号,这些端口预留给服务端程序绑定广泛使用的应用层协议
 这些知名端口号其实就是后面服务器端指定端口号的范围,剩余的端口号则由操作系统进行分配给客户端,如:
 
 22端口:预留给SSH服务器绑定SSH协议。
 21端口:预留给FTP服务器绑定FTP协议。 
 23端口:预留给Telnet服务器绑定Telnet协议. 
 80端口:预留给HTTP服务器绑定HTTP协议. 
 443端口:预留给HTTPS服务器绑定HTTPS协议
 
需要补充的是:
以上只是说明0~1023范围的知名端口号用于绑定知名协议,但某个服务器也可以使用其他1024~65535范围内的端口来绑定知名协议。
餐厅的VIP包房是给会员使用,但会员也可以不坐包房,坐其他普通座位。

协议分层

协议分层类似于打电话时,定义不同的层次的协议 :

你与对方在通话是规定语言是英语还是汉语 是电话机还是无线电,当你们规定好了才能进行通信

image-20220411104013632

OSI七层模型

OSI:即Open System Interconnection,开放系统互连
  1 OSI 七层网络模型是一个逻辑上的定义和规范:把网络从逻辑上分为了7层。
  2 OSI 七层模型是一种框架性的设计方法,其最主要的功能使就是帮助不同类型的主机实现数据传输;
 3 它的最大优点是将服务、接口和协议这三个概念明确地区分开来,概念清楚,理论也比较完整。通过七个层次化的结构模型使不同的系统不    同的网络之间实现可靠的通讯
 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GSI95QsI-1650593480279)(E:/MarkDown/note.1/image/image-20220411105819637.png)]

上面这种是存在于教科书,实际上在现实中OST简化版本 TCP/IP五层模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4riew57a-1650593480279)(E:/MarkDown/note.1/image/image-20220411105950564.png)]

其实又可以简化为四层(站在程序员的角度) 最后一层物理层与程序员关系不大

物理层:网络通信中的硬件设备~~
网线/网卡... 针对硬件设备的约定,就是物理层协议所负责的范畴~~需要保证所有的主机和网络设备之间,都是相互匹配的~~
数据链路层:
完成两个相邻设备之间的通信
相邻指的是:同一根网线连接的两台设备
像这样的主机1和主机2不能进行通信 但是他们两个都可以和路由器进行连接
image-20220411110337208
网络层:
负责点到点之间的通信.
网络中的任意节点,到任意节点之间的通信~~(不一定是相邻了,更多的是指不相邻的)网络层就负责在这两个点之间,规划出一条合适的路线~~
实际的网络环境结构非常复杂.两个点之间的路线不只一条~~就需要规划处最合适的一条~~[高德地图为你导航]

类似于这种,从主机1到主机3,网络层需要帮你规定路线
image-20220411110523136
传输层:
负责端到端之间的通信.
起点和终点
只是关注结果(数据到没到),不关注过程(不关注数据是走哪条路转发的)
我就需要填写自己的收件人地址和收件人姓名.商家就要根据这个地址把快递发给我~~我和商家,都是只关注结果,不关注过程~~
快递公司,要关注中间的过程~
应用层:
和应用程序密切相关的.你传输的这个数据,是干啥用的~~~不同的应用程序就有不同的用途~
有一天我在网上买一个床刷子~~

商家,站在传输层~考虑这个东西是能不能发到我手上.快递公司,站在网络层规划路线.
他们都是只在考虑包裹如何传输

快递小哥,站在数据链路层,骑着电动车把货拉到集散中心
不考虑这个包裹里面是啥,更不关心包裹里

电动车/集装箱卡车/公路站在物理层,提供传输的基础~

但是我,作为买床刷子的人,就是抱着一定的用途/目的,来买的~~
这个是程序猿最最需要打交道的事情~~
image-20220411110944833

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QtxmJlwZ-1650593480279)(E:/MarkDown/note.1/image/image-20220411111008285.png)]

封装和分用

网络分层中的重要概念,不同协议之间是如何进行配合的.

假如,通过qq给一个同学发消息

用户A在键盘上输入"hello" 信息发给用户B

此时的应用层就是qq程序,qq代码里面设计的应用层协议来构造出一个应用层的数据报文

此时根据用户输入的内容,就将hello这个数据造出一个应用层的数据报文

应用层(应用程序)

这里面的协议是一种约定,而这个报文则遵守了这个约定

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YckAsTFG-1650593480280)(E:/MarkDown/note.1/image/image-20220411111802814.png)]

(其他的传输层,网络层…的协议都是现成,操作系统/硬件/驱动已经实现好的)应用层的协议大概率是程序猿自己设定的~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ccVtROKq-1650593480280)(E:/MarkDown/note.1/image/image-20220411111854803.png)]

应用层协议就调用操作系统提供的API(称为socket API),把应用层的数据,交给传输层(就已经进入操作系统内核了)

传输层(操作系统内核)

这个时候我们进入传输层,也就是操作系统内核

根据刚才传过来的数据,基于当前使用的传输层协议,来构造出一个传输层的协议报文
传输层最典型的协议,UDP,TCP以TCP为例~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OxT0kvhL-1650593480281)(E:/MarkDown/note.1/image/image-20220411112104872.png)]

这里的源端口和目的端口 实际上就是我们后面代码块中服务器和客户端的端口号

接下来就会把这个传输层的数据报,交给网络层

网络层(操作系统内核)

拿到了完整的传输层数据报,就会再根据当前使用的网络层协议(例如IP),再次进行封装~~把TCP数据报构造成IP数据报.还是添加上一个协议报头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o0uNTLDo-1650593480281)(E:/MarkDown/note.1/image/image-20220411112538126.png)]

紧接着,当前的网络层协议,就会把这个IP数据报,交给数据链路层~~

数据链路层(驱动程序)

在刚才的IP数据报基础上,根据当前使用的数据链路层的协议,给构造成一个数据链路层的数据报.典型的数据链路层的协议,叫做"以太网”,就会构造成一个"以太网数据帧"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QrzK5ysh-1650593480281)(E:/MarkDown/note.1/image/image-20220411112701910.png)]

这里又加上了帧头,帧头实际上就是你在传输设备的过程,当前设备和下一个目的地的地址.

假如你这数据已经传出去了,此时你要经过很多台路由器和交换机才能到底目的地,那么这个时候你的帧头就在不断变化

物理硬件设备

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NscakcbR-1650593480282)(E:/MarkDown/note.1/image/image-20220411113500374.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BN92Rors-1650593480282)(E:/MarkDown/note.1/image/image-20220411113519014.png)]


假设我们的目的地是服务器,此时服务器就要对接受的数据进行解析,也就是分用.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lYj0cIt7-1650593480282)(E:/MarkDown/note.1/image/image-20220411113753630.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VvJ6hheF-1650593480283)(E:/MarkDown/note.1/image/image-20220411113810136.png)]

分用.
分用就是封装的逆过程
封装是从上往下,数据依次被加上了协议报头(包快递)
分用是从下往上,数据一次被去掉了协议报头(拆快递)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-daVndqym-1650593480283)(E:/MarkDown/note.1/image/image-20220411113919138.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qd9oXfKb-1650593480284)(E:/MarkDown/note.1/image/image-20220411113937731.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vqPb0Nc4-1650593480284)(E:/MarkDown/note.1/image/image-20220411113957856.png)]

无论网络多么复杂,这里整体的传输过程都是类似的.只是在不停的重复封装和分用的过程~~


网络编程套接字

什么是网络编程 ???
网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输 )

请求和响应

一般来说,获取一个网络资源,涉及到两次网络数据传输

  • 第一次:请求数据的发送
  • 第二次:响应数据的发送

客户端和服务端

服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务

客户端:获取服务的一方进程,称为客户端

好比在银行办事:
银行提供存款服务:用户(客户端)保存资源(现金)在银行(服务端)
银行提供取款服务:用户(客户端)获取服务端资源(银行替用户保管的现金)

常见的客户端服务端模型
最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:
1.客户端先发送请求到服务端
2.服务端根据请求数据,执行相应的业务处理3.服务端返回响应:发送业务处理结果
4.客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)

Socket套接字

网络编程套接字,是操作系统给应用程序提供的一组API(叫做socket API)

概念:Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基
于Socket套接字的网络程序开发就是网络编程

TCP UDP
有连接 无连接
可靠传输 不可靠传输
面向字节流 面向数据报
全双工 全双工

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sX6UdIU9-1650593480284)(E:/MarkDown/note.1/image/image-20220411114833240.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fKvlSjUH-1650593480285)(E:/MarkDown/note.1/image/image-20220411115132007.png)]


UDP socket,主要涉及两个类

DatagramSocket 和DatagramPacket

其中DatagramSocket中 Datagram是指数据报 而Socket是指这一个DatagramSocket对象,就对应到操作系统中的一个socket文件~~

什么是socket文件呢???
操作系统中的“文件"是一个广义的概念.平时说的文件,只是指普通文件(硬盘上的数据)
实际上,操作系统中的文件还可能表示了一些硬件设备/软件资源~~
socket 文件,就对应这“网卡"这种硬件设备.
从socket 文件读数据,本质上就是读网卡.
操作系统中常见的情况.
往socket 文件写数据,本质上就是写网卡
你可以想象: socket 文件,就是一个遥控器,通过遥控器来操作网卡~~
这种行为非常常见,甚至早在三国时期,就有了~~
董卓,曹操~~挟天子,以令诸侯~~天子就是这个天下的遥控器~~

你仔细想一下:我们在传输数据从一台主机到另一台主机先是封装完 到了同一台主机的物理层,然后去读取网卡

UDP中Socket的一些api
receive:接收数据
send:发送数据
close:释放资源

DatagramPacket
表示了一个UDP 数据报.
每次发送/接收数据,都是在传输一个DatagramPacket对象

接下来我们写一个最简单的回显服务~ EchoSever(请求啥内容回复啥内容)

UPD编程

先上代码:

UDP服务器

private DatagramSocket socket = null;

    public UdpEchoSever(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    public  void start() throws IOException {
        System.out.println("启动服务器");
        while(true){
            //由于不需要建立连接  直接接收数据即可
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(requestPacket);
            //接收完数据
            //此时应该是进行解析成字符串
            String request = new String(requestPacket.getData(),0,requestPacket.getLength(),"UTF-8");
            //解析完成之后,需要去进行响应计算
            String response = process(request);
            //此时将响应之后的数据进行包装成数据报发送到客户端
            DatagramPacket reponsePcket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            //此时包装完成,需要进行发送回去
            socket.send(reponsePcket);
            System.out.printf("[%s:%d] req: %s, resp:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),
                    request,response);

        }
    }

    private String  process(String response) {
        return response;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoSever udpEchoSever = new UdpEchoSever(9090);
        udpEchoSever.start();
    }

UDP客户端

public class UdpEchoClient {
    private DatagramSocket socket;
    private String SeverIp;
    private int SeverPort;

    public UdpEchoClient(String severIp, int severPort) throws SocketException {
        socket = new DatagramSocket();
        SeverIp = severIp;
        SeverPort = severPort;
    }
    public void start() throws IOException {
        Scanner sc = new Scanner(System.in);
        System.out.println("->");
        while(true){
            //这里需要去输入请求
            String request = sc.next();
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(SeverIp),SeverPort);
            //此时包装完成,开始发送
            socket.send(requestPacket);
            //开始接收从服务器那边响应过来的数据包
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength(),
                    "UTF-8");
            System.out.printf("res: %s,resp:%s\n",request,response);
        }

    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
        udpEchoClient.start();
    }
}

UDP翻译服务器,继承关系

public class UdpDictServer extends  UdpEchoSever{

    private  Map map = new HashMap<>();

    public UdpDictServer(int port) throws SocketException {
        super(port);
        map.put("cat","小猫");
        map.put("pig","小猪");
        map.put("dog","小狗");
        map.put("snake","蛇");
        map.put("row","牛");
    }

    @Override
    public String process(String request){
        return map.getOrDefault(request,"输入的单词有误");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer udpDictServer = new UdpDictServer(9090);
        udpDictServer.start();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bu9vhejI-1650593480285)(E:/MarkDown/note.1/image/image-20220412153359910.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KZNgbiUn-1650593480285)(E:/MarkDown/note.1/image/image-20220412153848757.png)]

需要明确是:服务器的端口是需要自己指定的,而客户端口号一般是系统自动分配的,在服务器代码中的构造方法中需要指定端口号,

在客户端代码中需要指定发送的地址Ip 和端口号 
而在服务器中发送回显的响应需要去获取之前发送过来的请求的数据报的ip和端口号,根据这个来进行返回

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lVRY1tH8-1650593480285)(E:/MarkDown/note.1/image/image-20220412154002563.png)]

由于UDP是不需要进行连接的,所以直接等待到客户端发来的请求,receive这个方法就是他来接收请求,所以在启动服务器之后,客户端发出请求之前,receive是保持阻塞状态

接收请求的是一个数据报,所以你需要解析成一个字符串,将这个字符串传给process来计算响应,计算完成之后包装成数据报发送回去
至于一些格式我就不细说,可以以这个为模板

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MH8tOw87-1650593480286)(E:/MarkDown/note.1/image/image-20220412154513959.png)]


对于服务器来说,必须要手动指定~~、后续客户端要根据这个端口来访问到服务器~~
如果让系统随机分配,客户端就不知道服务器的端口是啥,不能访问~~
对于客户端来说,如果手动指定,也行,但是系统随机分配更好~~
一个机器上的两个进程,不能绑定同一个端口~~
客户端就是普通用户的电脑,天知道用户电脑上都装了什么程序,天知道用户的电脑上已经被占用了哪些端口.如果你手动指定一个端口,万一这个端口被别的程序占用,咱们的程序不就不能正常工作了嘛??
而且由于客户端是主动发起请求的一方,客户端需要在发送请求之前,先知道服务器的地址+端口.但是反过来在请求发出去之前,服务器是不需要事先知道客户端的地址+端口~~
image-20220412155044594

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DrEjkrLo-1650593480286)(E:/MarkDown/note.1/image/image-20220412155232529.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OTK8Y4Hr-1650593480286)(E:/MarkDown/note.1/image/image-20220412155258699.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mura5pMq-1650593480287)(E:/MarkDown/note.1/image/image-20220412155323373.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gdhqDtd8-1650593480287)(E:/MarkDown/note.1/image/image-20220412155343338.png)]

在UPD编程中如何请求多个客户端呢???

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9cM75cp1-1650593480287)(E:/MarkDown/note.1/image/image-20220412155446638.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NQvhUmJG-1650593480287)(E:/MarkDown/note.1/image/image-20220412155515069.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nG5VFpN2-1650593480288)(E:/MarkDown/note.1/image/image-20220412155536027.png)]

通常情况下,一个服务器,是要同时给多个客户端提供服务的~~
但是也有情况,就是一个服务器只给一个客户端提供服务~~(典型就是在分布式系统中,两个节点之间的交互


TCP编程

上面介绍了UDP编程,也介绍了UDP和TCP的区别

TCP是需要连接的 并且是可靠传输 ,面向字节流

实际上跟Udp的代码差别很大

先上代码

TCP服务端

这个版本是最终的版本:加入线程池和或者线程来处理多个客户端的请求

注意事项:
1 首先由于TCP协议是建立连接的,所以你需要去用accept接收客户端的请求,你是在while(true)循环中,然后你的同一个客户端不断的去发送请求,所以服务器在读取请求的时候也是在一个while(true)中,那么当你同一个客户端不断发出请求导致第二个while(true)循环一直不断开,所以pocessConnection(clientSocket)函数一直不退出,第一个while(true)也就一直不结束,那么也就无法第二次调用accept来接收第二个客户端的请求.
要想解决上述问题,就得让processConnection的执行,和前面的accept 的执行互相不干扰.不能让 processConnection里面的循环导致accept无法及时调用~~
所以你需要假如线程或者线程池,每一次去调用accept都会创建一个新的线程,然后把这个函数放进去这个线程里去执行,这样可以理解为有许多的线程分支,这些线程分支并发执行任何一个客户端的请求

2 一个TCP服务器不能让一个UDP客户端连上,因为你无法保证"五元组",协议类型不匹配,无法完成通信

3 在UDP中没有关闭资源的操作,在TCP中需要关闭资源,首先UDP是数据报传输,而TCP是字节流传输
首先你需要明白在TCP服务器中clientSocket表示的是建立连接后接收了客户端的请求数据,这里你可以进行关闭,因为你接收完数据并且返回响应知直到这个客户端断开连接后你就可以关闭.
而在UDP中是不需要建立连接,根据代码可知,socket是一直保持存在.所以不能关闭

4 理解TCP字节流传输,inputStream outputstream那里的代码,并且理解printWriter 和 他调用的flush方法含义

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u1ARJasH-1650593480288)(E:/MarkDown/note.1/image/image-20220412170115545.png)]

public class TcpThreadPoolServer {
    private ServerSocket serverSocket = null;

    public TcpThreadPoolServer(int serverPort) throws IOException {
        serverSocket = new ServerSocket(serverPort);
    }
    public void start() throws IOException {
        System.out.println("服务器启动!");
        //这个线程池是自动扩容的
        ExecutorService pool = Executors.newCachedThreadPool();
        //因为是TCP协议,所以这里应该是需要建立连接的
        while(true){
            Socket clientSocket = serverSocket.accept();

            //线程池这边是自动扩容,你只需要去安排sumbit即可
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        pocessConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    private void pocessConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d]客户端建立连接!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
        //建立连接后,此时TCP由于是字节流进行传输的,所以这里用到文件操作哪里的一些api
        try (InputStream inputStream = clientSocket.getInputStream()) {
            try (OutputStream outputStream = clientSocket.getOutputStream()) {
                Scanner scanner = new Scanner(inputStream);
                while (true) {
                    //
                    if (!scanner.hasNext()) {
                        System.out.printf("[%s:%d] 客户端断开连接\n", clientSocket.getInetAddress(), clientSocket.getPort());
                        break;
                    }
                    //因为上面的Scanner是从inputStream中读取的,所以这里request就是从客户端发来的请求
                    String request = scanner.next();
                    //此时我们就去把这个请求进行计算响应
                    String response = process(request);
                    //然后把这个响应发送给客户端
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(response);
                    //刷新缓冲区,如果没有这个刷新,客户端就不能第一时间看到响应结果
                    //其实这里有点不太懂,但你可以想一下:
                        /*首先你用这个PrintWriter是通过字符读写的,一开始你把这个outputStram放进printWriter中
                        然后你把这个响应数据读取到printwriter中,  printwriter中放的是outputStream,此时outputStream中就存在相应的数据
                        在try那里呢,  OutputStream outputStream = clientSocket.getOutputStream(),意味着传输给客户端
                        但是你这串代码是在前面,你可能数据还没刷新,就是你一开始读完数据之后呢,你的代码按照顺序是在后面的,但是此时在前面,就可能返回的数据没有,此时后来你
                        才去读写了数据进去,那么这个时候就需要用到flush去刷新一下 */
                    //写数据的时候,需要把数据先写到缓冲区里,然后再统一写硬盘.如果当前缓冲区已经写满了,就直接触发写硬盘操作
                    //如果当前缓冲区还没满,也想提前写硬盘,就可以通过flush来手动"刷新缓冲区"
                    printWriter.flush();
                    System.out.printf("[%s:%d] res: %s,resp:%s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort()
                            , request, response);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpThreadPoolServer tcpThreadEchoServer = new TcpThreadPoolServer(9090);
        tcpThreadEchoServer.start();
    }
}

TCP客户端

public class TcpEchoClient {
    private Socket socket = null;

    //建立连接
    public TcpEchoClient(String serverIp,int serverPort) throws IOException {
        //这里绑定的就是你服务器的ip和端口号
        //然后你new对象之后就通过这个ip和端口号来建立了连接
        socket = new Socket(serverIp,serverPort);
    }
    public void start(){
        System.out.println("与服务器连接成功");

        Scanner scanner = new Scanner(System.in);
        //此时就要把这个请求传给服务器
            try(InputStream inputStream = socket.getInputStream()){
                try(OutputStream outputStream = socket.getOutputStream()){
                    while(true){
                        System.out.print("->");
                        String request = scanner.next();

                        //根据读取的字符串来进行传输
                        PrintWriter printWriter = new PrintWriter(outputStream);
                        printWriter.println(request);
                        printWriter.flush();
                        //从服务器读取响应并解析
                        Scanner resScanner = new Scanner(inputStream);
                        String response = resScanner.next();
                        //把结果输入到控制台
                        System.out.printf("res :%s ,resp:%s\n",request,response);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 9090);
        tcpEchoClient.start();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u13Wwjoh-1650593480288)(E:/MarkDown/note.1/image/image-20220412171052193.png)]

对于UDP的socket来说,
构造方法指定的端口,表示自己绑定哪个端口对于TCP的ServerSocket来说
构造方法指定的端口,也是表示自己绑定哪个端口对于TCP的Socket来说
构造方法指定的端口,表示要连接的服务器的端口~

TCP/IP协议

  • 理解网络传输流程
  • 理解传输层的作用,深入理解TCP的各项特性和机制
  • 对整个TCP/IP协议有系统的理解
  • 对TCP/IP 协议体系下的其他重要协议和技术有一定的了解

  • 本课是网络编程的理论基础。
  • 是一个服务器开发程序员的重要基本功。
  • 是整个网络课程中的重点和难点。
  • 也是各大公司笔试面试的核心考点

TCP/IP 五层协议栈

  • 应用层
  • 传输层
  • 网络层
  • 数据链路层
  • 物理层(不介绍)

应用层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v2GQ7kEe-1650593480289)(E:/MarkDown/note.1/image/image-20220412185946532.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RrEEktGh-1650593480289)(E:/MarkDown/note.1/image/image-20220412190051312.png)]

前端和后端就是通过网络来进行交互的~~
在这个交互的过程中,就需要约定好,前端发啥样的数据,后端回对应的数据~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1L4Zuy3o-1650593480289)(E:/MarkDown/note.1/image/image-20220412190159055.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gPFoRXvv-1650593480290)(E:/MarkDown/note.1/image/image-20220412190222332.png)]

设计一个应用层协议,主要就是包含两个工作

1.明确传输的信息
2.明确数据的组织格式

正是因为这里的应用层协议,可以随心所欲的来指定,这就导致两极分化非常严重!!
大佬设计的协议都非常好.
菜鸡设计的协议就非常糟糕~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BtPcnA7y-1650593480290)(E:/MarkDown/note.1/image/image-20220412190433983.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r7mdtihc-1650593480290)(E:/MarkDown/note.1/image/image-20220412190546871.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N9TmB59h-1650593480290)(E:/MarkDown/note.1/image/image-20220412210948418.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6l0pv6TF-1650593480291)(E:/MarkDown/note.1/image/image-20220412211020649.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JNhOOv5D-1650593480291)(E:/MarkDown/note.1/image/image-20220412211101206.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ohciWYbm-1650593480291)(E:/MarkDown/note.1/image/image-20220412211137605.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5nIQ7lpK-1650593480292)(E:/MarkDown/note.1/image/image-20220412211233989.png)]


传输层

虽然传输层,是操作系统内核实现,程序猿不需要直接和传输层打交道.但是传输层对我们来说仍然意义重大!!进行网络编程都要用到 socket.
一旦你调用了socket 代码就进入到传输层的范畴~~
如果一切顺利,就还好.一旦代码出现一些 bug ~~为了解决理解这些bug,传输层的一些知识就是必要的了
传输层的协议,也是面试中特别爱考的~~TCP协议~~
端口号~
整数,0-65535之间的整数~~
这些知名端口号一般来说都是指服务器的端口号
知名端口号:把0-1024这些端口号,给划分出了一些具体的作用.
很多网络服务是属于非常常用,非常广泛的服务~~
为了更好的管理就给这些服务分配一些专门的端口号~~
并不是强制要求,而只是建议~~
80 http服务器
443 https服务器
22 ssh
23 ftp

如果你自己部署http服务器,可以把他绑定到80,也可以绑定到其他端口上.

像java中知名的http服务器tomcat~使用的默认端口就不是80而是8080~


UDP协议报文

学习一个协议,很多时候都是在研究她的报文格式[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9dBS3dID-1650593480292)(E:/MarkDown/note.1/image/image-20220412212409698.png)]

所谓的把应用层数据报分装成UDP数据报,本质上就是在应用层数据包的基础上,添加了8个字节的报头~~

代码中写的端口号就会被打包到这样的UDP数据报中(在报头中体现)

源端口: 操作系统给客户端分配的端口号

目的端口:服务器的端口号

报文长度:
  此处报文长度是2个字节,范围0-65535,O-64k
  64k 大,还是小?够不够用?
  64k 大小在现代的互联网程序中,非常捉襟见肘了~~
  这是UDP使用中一个非常致命的缺陷,无法表示一个比较大的数据报~~如果确实需要传一个大的数据~~
  
  下策!!! : 可以在应用层,针对大的数据报,进行分包(拆成多个部分),然后再通过多个UDP数据报分别发送   
  这个时候接收方再把收到的几个包重新拼接成完整的数据~~
  太麻烦了~
  拆包组包的代码,写起来非常复杂,要考虑到很多情况(包丢了咋办,包的顺序错咋办
  
  
上策,就是改成TCP ~ TCP没有这样的长度限制~~


校验和:
  是用来验证网络传输的这个数据是否是正确的~~
  网络上传递的数据本质光信号和电信号~
  但是,如果有一些外界干扰,磁场之类的...就可能会导致原有的一些传递的信息发生了改变~~
  正常发一组连续的高频信号因为遇到了一个强磁场,就可能导致高频信号变成低频...
  打个比方
  就可能导致0-1数据就错了~~ bit翻转~~
  需要尽可能的识别出数据是不是错了,不能将错就错~~
  校验和就能帮助我们发现数据中的错误~~
 校验和正确,不能保证数据100%就是对的.但是校验和不正确,一定认为数据是有问题的~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CPTCA1I7-1650593480292)(E:/MarkDown/note.1/image/image-20220412214542342.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pw8ssdZq-1650593480292)(E:/MarkDown/note.1/image/image-20220412215432526.png)]


TCP协议报文

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L53MG9nY-1650593480293)(E:/MarkDown/note.1/image/image-20220412215501876.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nM6wUuOH-1650593480293)(E:/MarkDown/note.1/image/image-20220413225430032.png)]


接下来介绍一些面试题,围绕TCP,可靠连接展开.

确认应答机制

保证可靠传输的核心机制

可靠性:发送方发出去数据之后,能够知道接收方有没有接收到数据

关键就是接收方收到消息之后,给发送方,返回一个应答报文(ACK, acknowledge),表示自己已经收到了~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bdBtDJCN-1650593480293)(E:/MarkDown/note.1/image/image-20220413225925969.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EMNm7T1i-1650593480294)(E:/MarkDown/note.1/image/image-20220413225939576.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LWcigLju-1650593480294)(E:/MarkDown/note.1/image/image-20220413225951514.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8XaOPIbC-1650593480294)(E:/MarkDown/note.1/image/image-20220413230014037.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bKNSPcRh-1650593480294)(E:/MarkDown/note.1/image/image-20220413230111669.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2mEW35jd-1650593480295)(E:/MarkDown/note.1/image/image-20220413230121402.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W0IaE8GZ-1650593480295)(E:/MarkDown/note.1/image/image-20220413230129270.png)]


首先上面的图说明了的的确确会出现后发先至的这种情况,实际上就算是接收到了应答报文,也无法避免信息的后发先至,那么即使我们有了应答报文这种进行标志也无法去阻止后发先到的情况,但是实际上应答报文也并不是去处理这种情况的.你可以理解为对于请求端发出的每一条数据都理应接收到一条确认应答报文(如果没有这条消息对应的应答报文,那么可能请求端没有发送成功这条信息,或者响应端没有发送成功应答报文),这才是协议中精髓,否则就无法遵守协议.

这里的按照字节来进行编号实际上是  第一次发送的报头中序号是1,而报文的长度是1000,,那么确认应答的报文的序号就是1001,
那么下一次的发送数据的编号就是1002这种.

还有一点就是:TCP是在传输层进行的,那么这里实际上是个传输层的TCP数据报,接下来还需要经过网络层等等继续封装,然后发送过去

超时重传

相当于对确认应答进行了补充~~
确认应答是网络一切正常的时候,通过ACK通知发送方我收到了.如果出现了丢包的情况,超时重传机制就要起到效果了~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aaiy9QOk-1650593480295)(E:/MarkDown/note.1/image/image-20220413231743695.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mRxvNUAP-1650593480296)(E:/MarkDown/note.1/image/image-20220413231904454.png)]

假如出现了超时重传的情况,默认处理是认为是对方根本没收到我的信息,是我自己的问题,而不是对方发送了ACK缺丢了
这个时候会进行超时重传,但是假如判断错了,就是因为ACK丢了,这个时候还在进行超时重传,那么导致信息发了两次,很多时候意思都被曲解了

所有需要去处理这个重复发送信息的问题:
  TCP内部有个去重操作,就是接收方第一次收到的数据会放到操作系统内核的"接收缓冲区",那么第二次再去发送同样的数据报的时候,就会辨别这个新的数据跟缓冲区的那个数据序号是不是对应,如果对应说明重复了,这个时候就删除

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fRehEMKf-1650593480296)(E:/MarkDown/note.1/image/image-20220413232557768.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mZ1a2GQF-1650593480297)(E:/MarkDown/note.1/image/image-20220413232650822.png)]


基于上述的两个机制,TCP的可靠性,就得到了有效的保障~~

三次握手(经典面试题)

TCP连接管理,是网络部分最高频的面试题,没有之一!!!

  1. 如何建立连接 : 三次握手

客户端和服务器之间,通过三次交互,完成了建立连接的过程"握手"是一个形象的比喻~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FwJRWc2R-1650593480297)(E:/MarkDown/note.1/image/image-20220413233047940.png)]

这个图过于复杂,这边就拿一部分作为讲解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fl72aA1A-1650593480297)(E:/MarkDown/note.1/image/image-20220413233241153.png)]

在最先发送数据之前,两个主机需要建立连接,建立连接其实也是为了确保以后的通讯没有太大问题

那么客户端是发起连请求的一方,先发送一个SYN同步报文段给服务器,服务器接收到之后就会反馈给客户端一个ACK应答报文,那么这里服务器又会在基本上处于同一个时间发送一个SNY报文给客户端,然后客户端就会继续发送一个ACK
这四次交互,类似于 :老师在网课上提问一个学生,
   老师 :xx同学,你在吗???在的话给我一个回复 SNY
   同学 :老师,我在的  ACK
   同学 :老师,听得见吗??? SNY
   老师 : 听见了,同学  ACK
这样就建立起了连接,那么之所以是三次握手是因为中间两次可以合并成一次,因为他们几乎就是在同一个时间段发出的,所以就直接一次性发出去,这样也提升了效率

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nMs7OM4O-1650593480298)(E:/MarkDown/note.1/image/image-20220413234041188.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mpl0ILR7-1650593480298)(E:/MarkDown/note.1/image/image-20220413234127026.png)]

上图就简单介绍两个TCP在建立连接时的状态

那么三次握手有什么用??? 跟可靠性连接又有什么关系呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H4sndEXP-1650593480298)(E:/MarkDown/note.1/image/image-20220413235440852.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rz3pVhKx-1650593480298)(E:/MarkDown/note.1/image/image-20220413235616816.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3UlezJe8-1650593480299)(E:/MarkDown/note.1/image/image-20220413235847659.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DZYdKlFZ-1650593480299)(E:/MarkDown/note.1/image/image-20220413235913230.png)]


四次挥手,如何断开连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Av3oncXz-1650593480299)(E:/MarkDown/note.1/image/image-20220414000024737.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6EwuxObo-1650593480299)(E:/MarkDown/note.1/image/image-20220414000050174.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQSaBhj7-1650593480300)(E:/MarkDown/note.1/image/image-20220414000145591.png)]


关于TCP断开连接时,两个重要状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IDKJ89B6-1650593480301)(E:/MarkDown/note.1/image/image-20220414000250282.png)]

这个图有一点解释一下: 第二个状态 ,为什么最后一个ACK丢包了 ,B就需要去重传FIN呢???

如果最后一个ACK丢了B就收不到ACK了.
B是无法区分是FIN丢了,还是ACK丢了~~
于是B就假设是FIN丢了,于是就重传FIN(超时重传)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RYTVUb9m-1650593480301)(E:/MarkDown/note.1/image/image-20220414000616145.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3IuQLqPX-1650593480301)(E:/MarkDown/note.1/image/image-20220414000639601.png)]


TCP虽然可靠性是最高的机制,但是TCP 也会尽可能的提高传输效率!!

滑动窗口

滑动窗口存在的意义就是在保证可靠性的前提下,尽量提高传输效率!!!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q1TMLogO-1650593480301)(E:/MarkDown/note.1/image/image-20220415100537294.png)]

注意这里的等待多份数据的ACK发送过来 也并不是要一起等待所有ACK都到达才开始发送下一组数据,而是第一份ACK到达后就开始发送下一组数据
下面的图也说的很清楚,只要1001这个ACK到达,就可以继续去发送4001这个数据,然后继续多发一组(这里也仅仅是多发一组,那么此时你需要等待的ACK范围就是 2001 ~ 4001 这四组,然后下一个等待范围就是 3001~ 5001 的ACK)
如果那这个模型看成是在水平面上的,就可以理解成滑动窗口,不断去滑动,并且在后面我们可以去让这个窗口的大小和窗口的移动速度改变.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aLi6wbcU-1650593480302)(E:/MarkDown/note.1/image/image-20220415100632463.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lAzi2xB0-1650593480302)(E:/MarkDown/note.1/image/image-20220415103443756.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aa8a7MkP-1650593480302)(E:/MarkDown/note.1/image/image-20220415101545219.png)]

当前这个窗口大小越大,可以认为就是传输速度就越快 窗口大了,同一份时间内等待的ACK就更多~~
总的等待ACK的时间就少了~~

可以理解为: 在发送数据的时候,就可以去传输ACK.(这样就节省一组时间) 窗口越大,这个组数就越大,那么 节省的时间就越多


那么接下来有个问题:

会不会出现2001还没到了 而3001到了这种情况???

2001没到 3001到了

丢包 分为两种情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qvGvsJ5-1650593480303)(E:/MarkDown/note.1/image/image-20220415102011986.png)]

有一句话很重要: ACK确认序号的含义就是保证了后一条ACK就能涵盖前一条或者前多条


如果数据丢了,就会出发超时重传,主机B不断的去发送丢了的那条数据的ACK,主机A收到了三条同样的ACK报文就意识倒那条数据丢包了,然后主机A就继续去发送后面的数据,发送一会之后就开始进行重传,这样就补上了.

那么在接收缓冲区那里,实际上那个缺口存在,当重传完成才进行补上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RvQrNlQE-1650593480303)(E:/MarkDown/note.1/image/image-20220415102909997.png)]


流量控制

是滑动窗口的延伸.目的是为了保证可靠性.

在滑动窗口中,窗口越大,传输速率就越高 不光要考虑发送方,还得考虑接收方~~

发送方发的贼快,接收方根本处理不过来了.接收方就会把新收到的包给丢了...…发送方是不还得再重传~~~

这就好比,有些同学写博客,就光图快,没考虑到自己是不是真的理解了~~咔咔一顿复制粘贴,整完了~也没管自己吸收了多少~~导致过了一段时间之后,回头一看,"这个博客真的是我自己写的嘛?"

流量控制的关键,就是得能够衡量接收方的处理速度~~

此处就直接使用接收方接收缓冲区的剩余空间大小,来衡量当前的处理能力~~

==使用接收方接收缓冲区的剩余空间大小,来衡量当前的处理能力==

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hzk4q90Y-1650593480303)(E:/MarkDown/note.1/image/image-20220415143231010.png)]

这样的数据传输过程,可以理解成"生产者消费者模型"
A就是生产者
B的应用程序就是消费者
B的接收缓冲区,就是交易场所~
接收缓冲区肯定有一个总大小~~
随着A发送数据,接收缓冲区里就会逐渐放入一些数据剩余空间就会逐渐缩小.
如果剩余空间比较大,就认为B的处理能力是比较强.就可以让A发的快点.如果剩余空间比较小,就认为B的处理能力是比较弱,就可以让A发的慢点.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qO56vxTn-1650593480303)(E:/MarkDown/note.1/image/image-20220415143313150.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8MqPu4iJ-1650593480304)(E:/MarkDown/note.1/image/image-20220415143512382.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RlY2mZez-1650593480304)(E:/MarkDown/note.1/image/image-20220415144548625.png)]


拥塞控制

也是滑动窗口的延伸,也是限制滑动窗口发送的速率~~

拥塞控制衡量的是,发送方到接收方,这整个链路之间,拥堵情况(处理能力)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JQ2eC7IB-1650593480304)(E:/MarkDown/note.1/image/image-20220415144654769.png)]

A能够发多快,不光取决于B的处理能力,也取决于中间链路的处理能力~

A和B之间的中间节点,有多少个?不知道~~很多~~就很难对这些设备一一衡量~~

拥塞控制的处理方案,就是通过"实验"的方式,逐渐调整发送速度,找到一个比较合适的值(范围)

A开始的时候以一个比较小的窗口来发送数据.如果数据很流畅的就到达了,逐渐加大窗口大小~~

如果加大到一定程度之后,出现了丢包~~(丢包就意味着通信链路出现拥堵了),这个时候再减小窗口~~

通过反复的增大/减小过程逐渐就摸到了一个合适的范围~~拥塞窗口就在这个范围中不断变化,达到"动态平衡"
拥塞窗口跟流量控制窗口都是来保证滑动窗口的的效率
1. 最终的滑动窗口大小 = min(拥塞窗口,流量控制窗口)

2.阈值: 在窗口大小达到了指数增长的最大值,线性增长的最小值 ,出现丢包后的阈值更新为丢包窗口的一半

3.最快最有效的窗口大小是在阈值 ~ 丢包窗口之间

4.丢包之后,窗口大小回归初始大小,并且继续以之前(指数+线性)的速度进行增长
 (原因在下面的图中有提到)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uhgOC4l8-1650593480305)(E:/MarkDown/note.1/image/image-20220415145303532.png)]


延时应答

相当于流量控制的延伸~~
流量控制是踩了下刹车,使发送方,发的不要太快.
延时应答,就想在这个基础上,能够尽量的再让窗口更大一些~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4cKaieLU-1650593480305)(E:/MarkDown/note.1/image/image-20220415145932689.png)]

说明了ACK并不是立即就发送,而是希望这延时的这段时间 主机B能从缓冲区能多拿一点数据,这样就可以保证了缓冲区的剩余空间变多了,也就通过流量控制来保证传输效率更高一些

因为ACK在发送的个时候会提供当前缓冲区的大小(窗口的大小)


至于2G到5G的提升是属于物理层的提升

捎带应答

又是延时应答的延伸

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4u0V393r-1650593480305)(E:/MarkDown/note.1/image/image-20220415150430052.png)]

对于捎带应答来说,ACK和返回响应数据一起发送过去,ACK丢了也就意味着数据丢了,那么这个时候就需要进行超时重传


粘包问题 => 面向字节流

(不仅仅TCP存在粘包,其他的面向字节流的机制,也存在,比如读文件)

TCP粘包指的是粘的是应用层数据报﹒在TCP接收缓冲区中,若干个应用层数据包混在一起了,分不出来谁是谁了~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xhZKIAPE-1650593480306)(E:/MarkDown/note.1/image/image-20220415151029796.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6UN93kUa-1650593480306)(E:/MarkDown/note.1/image/image-20220415151104371.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XtAhgHYs-1650593480306)(E:/MarkDown/note.1/image/image-20220415151112922.png)]


TCP的异常处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1cpgCO4Y-1650593480307)(E:/MarkDown/note.1/image/image-20220415151237411.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uFvXk0C7-1650593480307)(E:/MarkDown/note.1/image/image-20220415151248210.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O2QrQncR-1650593480307)(E:/MarkDown/note.1/image/image-20220415151258812.png)]


可靠性的基础上,保证效率~~
TCP vs UDP对比~~
1.啥时候使用TCP?对可靠性有一定要求~~(日常开发中的大多数情况,都是基于TCP)

⒉啥时候使用UDP?对可靠性要求不高,对于效率要求更高.(典型的例子,机房内部的主机之间通信.分布式系统中)
经典面试题:
基于UDP如何实现可靠传输??[其实在考TCP]
抄作业!!!
本质上就是在应用层基于UDP复刻TCP的机制~~

传输层的协议,只有TCP和UDP嘛?

像当下常见的, LOL, Dota2,吃鸡,王者荣耀...这些对抗性很高的游戏,底层是使用TCP还是UDP ??
 魔兽采用的是TCP/IP协议
 其余大多数游戏采用UDP协议,
 实时战斗游戏的话还是要用UDP了,因为TCP的特性,一旦丢包就会重发,阻塞住后续的数据包因而可能会产生一个较大的瞬时延迟。
 可以容忍延迟并且有很好的屏蔽延迟的设计,如纸牌类和MMO,用TCP
 不能容忍延迟,如DOTA类和动作类,用UDP

网络层

网络层主要是IP协议

IP协议主要完成两方面内容:

1.地址管理

2.路由选择

image-20220417104939815
  1. 4位版本: IP协议的版本号.当前只有两个取值,4和6 0100 或者 0110 当前课堂上主要讨论的是IPv4

  2. 4位首部长度~IP的报头和TCP类似,都是可变的.带有选项4位的取值范围0-15
    这里的单位也是4字节.
    如果取值是1111=> 15,实际表示的首部长度就是60字节 ,所以60字节也是最大限制的首部长度

  3. TOS说是8位,其实只有4位是有效的~~
    4位TOS分别表示:最小延时,最大吞吐量,最高可靠性,最小成本。(同一时刻,只能取一种状态)
    这里的TOS相当于是切换形态~~(类似于变身效果)
    IP协议能够规划处两点之间一条比较合适的路径.翻译翻译,啥叫合适??

  4. 16位总长度~
    UDP好像也有类似的情况~
    16位=>最大长度64k
    因此,单个IP数据报最大长度确实不能超过64k
    如果要构造一个更长的数据报(比如搭载的载荷部分已经超过64k了,咋办?)
    IP协议自身实现了分包和组包这样的操作~~~


  5. 1. 在第四点我们就说过,16位总长度保证单个IP数据报长度不能超过64k,如果超过了,IP协议自身就要进行分包和组包操作
     
    2. 在第五点着重介绍了分包和组包操作
     16位标识,3位标志,13位偏移
     分开的每个IP数据报的长度都不能超过64k,并且在没拆分之前载荷质里存在一个TCP报头,那么拆分之后每个小的IP数据报都存在一个IP报  头,但是只有一个IP数据包存在TCP报头
     
     3.如何区分多个IP数据包从同一个数据中分来的???
     这个时候,16位标识就起到了作用,被分开的IP数据包里面的IP报头中的16位标识相同意味着标识是同一个总得IP数据包
     
     4.3位标志,其实就是一位好使,那一位在IP报头里如果是0表示这个IP报头不是最后一个
       如果是1表示这个包就是分组的最后一个
     
     5.13位片偏移: 也就是说区分哪个包是在前哪个包在后,把顺序拍好才方便我们组包的时候顺序不会乱
     
    
    image-20220417105638998

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XUIWqcfF-1650593480307)(E:/MarkDown/note.1/image/image-20220417111932781.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nuSDwMNZ-1650593480308)(E:/MarkDown/note.1/image/image-20220417105910437.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H29roJ0Y-1650593480308)(E:/MarkDown/note.1/image/image-20220417105918952.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8JLUGG1U-1650593480308)(E:/MarkDown/note.1/image/image-20220417112045792.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FNT9zpdk-1650593480309)(E:/MarkDown/note.1/image/image-20220417112117821.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o490DXy3-1650593480309)(E:/MarkDown/note.1/image/image-20220417112123841.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uIsmG2dH-1650593480309)(E:/MarkDown/note.1/image/image-20220417112142798.png)]


地址管理

首先IP地址是一个点分十进制构成的数据

所以同一个路由器连接的并不都是同一个局域网

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-11gdCGYM-1650593480309)(E:/MarkDown/note.1/image/image-20220417112854379.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjN9KmeF-1650593480310)(E:/MarkDown/note.1/image/image-20220417113040855.png)]

第一部分图:多台主机连接这个路由器,表示在一个局域网里,可以进行通信,多台主机主机号不同

第二部分,路由器的LAN口连接家里光猫的WAN口,然后其余设备(电视,电脑)又可以连接光猫的LAN口

第三部分:光猫的WAN口又连接了运营商的WAN口,运营商的设备是又外网IP的,这个外网IP是可以连接到很多服务器上去的

运营商IP:
    这个IP不是只给我一个人用的
    而是所有接入这个运营商设备的局域网,都在共用着同一个IP
    可能不只是我自己,我们整个小区,上万号人,都在使用这同一个外网IP~~
	这就好比我买个东西,我得写收件人地址~~
	陕西省,西安市,未央区,陕西科技大学~(陕科大,有几万号人)
	这几万号人写的收件地址,都是这一串~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qtk7SXiE-1650593480310)(E:/MarkDown/note.1/image/image-20220417112930074.png)]

到底前多少个bit位是网络号?咋规定的呢?固定3个字节嘛??其实是不固定的!!!!
引入了一个“子网掩码"这样的概念~~来表示多少个bit位是网络号~~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HWWUCvNA-1650593480310)(E:/MarkDown/note.1/image/image-20220417113723118.png)]

一个点分十进制的IP地址,化成2进制后,我们无法去分辨从哪到哪是网络号和主机号

此时我们还可以知道这台主机的子网掩码,通过子网掩码化成二进制后,1的位置都是网络号,0的位置都是主机号

这样就可以分辨出主机号和网络号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ip8CGvFb-1650593480311)(E:/MarkDown/note.1/image/image-20220417114100269.png)]


一些特殊的IP地址~~
	如果IP的主机号为全0,该IP就表示网络号.(局域网里的一个正常的设备,主机号不能设为О)
	
	如果IP的主机号为全1(255),该IP就表示"广播地址".往这个广播地址上发的消息,整个局域网中都能收到
	
	IP地址是127开头的,该IP都表示“环回IP",表示主机自己~~
	
	127.0.0.1(环回IP中的典型代表)由主机A到主机A
	
	IP地址是10开头,192.168 开头,172.16-172.31开头,表示该IР地址是一个局域网内部的IP(内网IP)除此之外,剩下的IP称为外网IP(直		接在广域网上使用的IP)
	
要求外网IP一定是唯一的.每个外网IP都会对应到唯一的一个设备.
内网IP只是在当前局域网中是唯一的.不同的局域网里,可以有相同的内网IP的设备~~
最后一句话需要理解:同一个局域网中是不可能存在相同的IP,下面就会详细介绍到

本来咱们预期IP地址应该就表示一个网络上的唯一位置.结果这咋同一个IP能表示不同设备了呢??

当前IPv4协议,使用的IP地址是32位的整数.32位能表示的数据范围 42亿9千万


如果给每个设备都分配一个唯一的IP地址,意味着世界上的设备就不能超过42亿9千万~~随着网络的发展,现在世界上的设备越来越多,已经超过了42亿9千万.
让每个设备都有唯一的IP地址,不现实了~
(现在尤其是移动互联网的兴起)
如何解决这个问题???
	1.动态分配IР地址.让每个设备连上网的时候,才有IP,不联网的时候就没IP(这个IP就可以给别人用)但是这个方案不能从根本上解决问题~~     (设备没有减少, IP地址也没有增加...)治标不治本~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ImMpyMlc-1650593480311)(E:/MarkDown/note.1/image/image-20220417115027953.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oIpALDF4-1650593480311)(E:/MarkDown/note.1/image/image-20220417115146362.png)]


假设在那个局域网中有有两台设备都去通过运营商的外网IP去访问CCTALK服务器,这个时候CCTALK就看到有台相同的设备IP(都是运营商外网IP)来请求访问服务器,那么这个时候服务器是如何去分别这两台设备呢??? 因为他们接入的IP地址都是相同的,这里进一步区分就靠端口号

在下面的图中国可以看到,他们的源IP是相同的,但是端口号是不同的,此时就区分出来了,那么CCTALK就通过端口号就将数据发送到不同的主机上去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s2XGQikr-1650593480311)(E:/MarkDown/note.1/image/image-20220417115246892.png)]

那么端口号是服务器进行分配的,可能会导致端口号分配的时候也是相同的???[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-47yEUquD-1650593480312)(E:/MarkDown/note.1/image/image-20220417115907585.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RydjkAS7-1650593480312)(E:/MarkDown/note.1/image/image-20220417115923096.png)]


IP地址网段划分,是通过子网掩码的方式划分的…
在历史上(有子网掩码之前)
是简单粗暴的通过"分类”的方式来划分的.把IP地址分成了A, B,C,D,E
这五类,每一类,分别都有几位是网络号,几位是主机号…(淹没在历史长河中了)
[很多教科书上都有.并且笔试的时候偶尔会窜出来)


路由选择

路由选择,也就是规划路径.
两个设备之间,要找出一条通道,能够完成传输的过程~~
要想找出通道,前提是,得先认识路~~

实际上每个连接的路由器的路由表中都会有当前路由器表示的局域网中的网络号,然后我们消息通过很多路由器去探索去到达目的地

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9a5bPX9i-1650593480312)(E:/MarkDown/note.1/image/image-20220417120157089.png)]


数据链路层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8EcHolzD-1650593480313)(E:/MarkDown/note.1/image/image-20220417120504973.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EnxhnSIr-1650593480313)(E:/MarkDown/note.1/image/image-20220417120534664.png)]

mac地址实际上是每台网卡的地址,也就是每台主机的物理地址[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6oedJ1BM-1650593480313)(E:/MarkDown/note.1/image/image-20220417120659656.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7sTeDWOQ-1650593480314)(E:/MarkDown/note.1/image/image-20220417152810200.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9MbTfx2L-1650593480314)(E:/MarkDown/note.1/image/image-20220417153031122.png)]

关于ARP报文,实际上是跟mac地址有关的,在数据链路层和物理层起到作用,要是想进一步了解可以去计算机网络133页查看

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fyczE2q8-1650593480314)(E:/MarkDown/note.1/image/image-20220417153124011.png)]


一点补充:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nvNBgXKU-1650593480315)(E:/MarkDown/note.1/image/image-20220417153408624.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TmWA52cy-1650593480315)(E:/MarkDown/note.1/image/image-20220417153436145.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n71i8UOj-1650593480315)(E:/MarkDown/note.1/image/image-20220417153446178.png)]


6:00 ~ 7:00 起床刷B站
7:00 ~7:30 洗漱吃早饭
7:30 ~ 12:00 打一会游戏
12:01 ~ 12:02 学习
12:02 ~ 12:03 学习
12:04 ~ 12:05 学习
12:05 ~ 12:06 学习
12:06 ~ 12:07 学习
12:07 ~ 12:08 学习
12:08 ~ 12:09 学习
12:09 ~ 12:10 学习
12:10 ~ 12:50 学习
12:51 ~ 18:00 打一会游戏
18:01 ~ 18:30 吃完饭
18:31 ~21:30 看耀阳直播卖丝袜

段划分,是通过子网掩码的方式划分的…
在历史上(有子网掩码之前)
是简单粗暴的通过"分类”的方式来划分的.把IP地址分成了A, B,C,D,E
这五类,每一类,分别都有几位是网络号,几位是主机号…(淹没在历史长河中了)
[很多教科书上都有.并且笔试的时候偶尔会窜出来)


路由选择

路由选择,也就是规划路径.
两个设备之间,要找出一条通道,能够完成传输的过程~~
要想找出通道,前提是,得先认识路~~

实际上每个连接的路由器的路由表中都会有当前路由器表示的局域网中的网络号,然后我们消息通过很多路由器去探索去到达目的地

[外链图片转存中…(img-9a5bPX9i-1650593480312)]


数据链路层

[外链图片转存中…(img-8EcHolzD-1650593480313)]

[外链图片转存中…(img-EnxhnSIr-1650593480313)]

mac地址实际上是每台网卡的地址,也就是每台主机的物理地址[外链图片转存中…(img-6oedJ1BM-1650593480313)]

[外链图片转存中…(img-7sTeDWOQ-1650593480314)]


[外链图片转存中…(img-9MbTfx2L-1650593480314)]

关于ARP报文,实际上是跟mac地址有关的,在数据链路层和物理层起到作用,要是想进一步了解可以去计算机网络133页查看

[外链图片转存中…(img-fyczE2q8-1650593480314)]


一点补充:

[外链图片转存中…(img-nvNBgXKU-1650593480315)]

[外链图片转存中…(img-TmWA52cy-1650593480315)]

[外链图片转存中…(img-n71i8UOj-1650593480315)]


6:00 ~ 7:00 起床刷B站
7:00 ~7:30 洗漱吃早饭
7:30 ~ 12:00 打一会游戏
12:01 ~ 12:02 学习
12:02 ~ 12:03 学习
12:04 ~ 12:05 学习
12:05 ~ 12:06 学习
12:06 ~ 12:07 学习
12:07 ~ 12:08 学习
12:08 ~ 12:09 学习
12:09 ~ 12:10 学习
12:10 ~ 12:50 学习
12:51 ~ 18:00 打一会游戏
18:01 ~ 18:30 吃完饭
18:31 ~21:30 看耀阳直播卖丝袜

你可能感兴趣的:(笔记,Socket,网络编程,TCP,UDP)