【JavaEE初阶】网络原理(1)

欢迎关注个人主页:逸狼


创造不易,可以点点赞吗~

如有错误,欢迎指出~



互联网中最主流的时TCP/IP五层协议(应用层,传输层,网络层,数据链路层,物理层),应用层是程序员日常开发中最常用到的一层(可以使用已经开发好的协议,也可以自己定义应用层协议),其他层则 操作系统/硬件/驱动/已经实现好了,不可能"自定义"

自定义应用层协议

  1. 明确前后端交互过程,需要传递哪些信息
  2. 明确组织这些数据的格式

比如要开发一个外卖软件,打开软件首先需要 展示一个"商家列表",其中交互的信息包括:

  • 请求:用户id,用户位置
  • 响应:商家列表(包含多个商家,其中有商家的名字,图片,距离,评分等等)

数据的组织格式有很多种,使用现有的格式即可,下面介绍几种常用的格式

xml(传统方案)

xml是通过"成对的标签"表示"键值对"信息

例如,表示Id 为1001的用户的位置在E45N60处

<request>
    <userId>1001</userId>
    <position>E45N60</position>
</request>

缺点:xml在网络传输时会消耗 大量的带宽(网络通信中,带宽是非常贵的硬件资源) 

与html区别

xml和html很类似,都是成对的标签夹住一些内容,区别是

xml的标签(键值对)都是程序员自定义的

html的标签,都是固定的(已经有一套标准,约定好了哪些是合法的标签,都有具体的含义)

JSON(主流)

JSON相比于xml,可读性更好,同时能够节省一定的带宽

json也是键值对的形式,

  • 键和值使用 : 分割
  • 键值对之间使用 , 分割
  • 所有键值对都是用 {} 括起来
{
    "userId":1001,
    "position":"E60N45"
}

 json没有明确数据格式,当数据都在一行,且数据量很大时(20~30个键值对),json的可读性就下降了

yml(yaml)

对比json,yml强制要求了数据组织的格式:yml强制要求了键值对必须独占一行,而且"嵌套结构"必须通过 缩进 来表示

request:
    userId:1001
    position:"E60N45"

google protobuffer

前三个方案都关注可读性,protobuffer则关注性能,牺牲了可读性(通过二进制的方式组织数据->二进制数据无法用肉眼阅读,调试相关程序时比较麻烦)

protobuffer直接通过"位置"约定字段的含义,不需要传输key的名字

针对传输的数值,进行二进制编码,起到一些"压缩"的效果(极大的缩减了要传输的数据体积=>带宽消耗减小=>效率越高)

传输层负责数据从发送方到达接收方

端口号

端口号(Port):标识了⼀个主机上进⾏通信的不同的应⽤程序;

端口号是一个整数,用来区分不同的进程. 同一时刻,同一个机器上,同一协议,一个端口号只能被一个进程绑定,一个进程可以绑定多个端口号

取值范围

端口号是通过2个字节的无符号整数表示的,取值范围0-65535

  • 0这个端口比较特殊,表示 随机设定空闲端口,不会使用
  • 1-1023属于已经被设定好了的(有些知名的服务器(上世纪80,90年代,现在大部分不再使用),已经提前预定好了这个端口),这些端口称为"知名端口号",日常开发时,要避开
    • 如ssh服务器使⽤22端⼝
    • ftp服务器,使⽤21端⼝
    • telnet服务器,使⽤23端⼝
    • http服务器,使⽤80端⼝
    • https服务器,使⽤443端口
  • 1024-65535:操作系统动态分配的端⼝号.客户端程序的端⼝号,就是由操作系统从这个范围分配的.

 端口类型

业务端口: 编写服务器时需要绑定至少一个端口(业务端口)和客户端进行交互

管理端口:通过网络通信,让 服务器绑定另一个端口(管理端口),通过这个端口编写一个客户端给服务器发送一些"控制类"的请求(比如让服务器重新加载某个数据/某个配置/修改服务器的某个功能)

调试端口:针对服务器的运行状态进行检测和调试 或者需要查看服务器运行中某个关键变量的数值等等 ,不能使用调试器(使用调试器调试这个服务时,会时服务器的一些线程被阻塞住,无法给客户端正确提供服务).  这时让服务器绑定另一个端口(调试端口),然后实现一些相关的 打印关键变量的逻辑.客户端发送对应的调试请求

UDP协议

实际上的UDP的数据没有"换行"动作

UDP报头长度固定是8字节,分为四个字段(每个2字节),它们之间 没有指定的分隔符,而是通过固定长度来区分的

端口 

网络通信中涉及4个关键信息:源IP,目的IP,源端口,目的端口. udp中使用2字节长度表示端口号,若使用10这样的端口号,在系统中就会被截断

报文长度

UDP报文长度:报头长度+ 载荷长度

UDP报文长度单位 是"字节",比如 报文长度1024,表示整个UDP数据报就是1024字节,由于udp的长度用2字节来表示这个长度,所以udp报文长度最大就是65535(64KB)=>当年(30年前)算挺大的数字,但放在今天就捉襟见肘了(可能一张照片的体积就10MB左右了)

如果使用UDP协议传输一个很大的数据,会非常麻烦~

方案

  1. 直接使用TCP =>TCP对于长度没有限制,TCP自身也带有可靠传输这样的机制 ,对于整个通信质量来说也是有利的;再者这样代码修改的成本也比较低
  2. 把一个大的数据包拆成多个,分别进行传输=>被否决了 ,原因是 实现分包和 组包的过程充满不确定性,非常复杂
  3. 把UDP长度字段 扩展成4个字段 =>不行. 作为一个网络协议不是单方面修改就行了,服务器要改,客户端的也要改,除非把全世界所有手机/pc/服务器所有涉及到udp协议的地方都进行统一的修改,所以相比于修改UDP,未来发明一个新的协议代替UDP可能要跟简单一些.

校验和

由于网络传输中非常容易出现错误(电信号/光信号/电磁波 受到环境的干扰使里面的信号发生改变(比特翻转 :0->1,1->0))

 校验和存在的目的是 能够"发现"或者"纠正"这里出现的错误 ,就可以给传输的数据中,引入 额外信息(chechsum),用来发现(若只是为了发现错误,携带的额外信息可以少一些)/纠正(若想要纠正错误,携带的额外信息就更多了,会消耗更多带宽) 传输数据的错误

校验和工作原理

结合内容/内容的片段,生成校验和

UDP中使用2个字节作为校验和,UDP使用CRC校验和(循环冗余校验)

CRC校验和

把UDP数据报整个数据都进行遍历,分别取出每个字节,往一个两个字节的变量进行累加,由于整个数据可能很多,可能会使结果溢出,但是重点关注的是最终加的结果,关心校验和结果是否会在传输中发生改变

接收方可以根据数据的内容,按照同样的算法 再算一遍校验和,得到checksum2,如果传输的数据没有发生任何改变,此时算出来的checknum1 == checknum2. 反之,若不相等,就可以认为数据网络传输中出错了

在计算校验和的过程中,可能会存在不同的数据,生成的校验和相同,但是概率很低很低

除了CRC校验和,还可能用到一些其他的算法实现校验和,如MD5和SHA1(和MD5非常类似)

 MD5算法

本质上是一个"字符串hash算法",背后的实现过程是一个"数学过程",简单理解为"套公式"

md5的特点
  1. 定长:无论输入的字符串长度多长,算出来的md5的结果都是固定长度  =>适合做校验和 算法
  2. 分散:输入的内容,即使是只有一点点变化,得到的md5值都会相差很大(越分散,出现hash冲突的概率就越小)  =>适合做hash算法
  3. 不可逆:根据输入内容,计算md5非常简单,但是如果已知md5值,还原出原始的内容,理论上是不可行的(基于人类当下算力水平) =>适合做加密算法

上一篇:【重学 MySQL】七十七、掌握存储过程与存储函数的查看、修改与删除技巧-查看存储过程与存储函数


下一篇:基于SpringBoot足球场在线预约系统的设计与实现