1.应用层
主要涉及两种情况:
(1)使用大佬们已经创建好的应用层协议.
(2)自己定义应用层协议.
[1]明确前后端交互过程中,需要传递哪些信息.
比如开发一个外卖软件,展示"商家列表"
此处就需要先确定传递的信息是啥.
a.请求:用户id; 用户所处的位置
b.响应:商家列表,包含多个商家,包含商家的名字,距离,图片,评分...
[2]明确,组织这些信息的格式.
确保前端和后端的格式是一致的.
不用凭空发明,使用现有的格式就可以了.
---------------------------------------------------------------------------------------------------------------------------------
1.xml(传统的方案)
通过"成对的标签"表示"键值对"的信息;
<request>
<userId>1001</userId>
<position>E45N60</position>
</request>
xml进行网络传输时有一个明显的缺点:消耗大量的带宽.网络通信中带宽是非常贵的硬件设备.
xml和html的差别: xml里的标签(键值对)都是程序员自定义的;
html里的标签,都是固定的,已经有一套标准,约定好哪些标签是合法标签,这些标签都是什么含义.
2.JSON(当前更主流的网络通信的数据格式)
相比于xml来说,可读性也是很好的,同时能节省一定的带宽.
{
"userId":1001,
"position":"E45N60"
}
JSON也是"键值对"格式:
键和值使用":"分割
键值对之间使用","分割
所有的键值对,都用"{}"括起来
{"userId":1001,"position":"E45N60"}这种JSON也是合法的,但是可读性较差.
3.yml(yaml)
强制要求了数据组织格式:
键值对必须独占一行.
而且"嵌套"结构,必须通过缩进来表示.
request:
userId:1001
position:"E60N45"
4.google protobuffer(关注性能,牺牲了可读性)
通过二进制的方式来组织数据.
protobuffer直接通过"位置"约定字段的含义,不需要传输key的名字.
也会针对传输的数值,进行二进制的编码,起到一些"压缩"的效果.
二进制数据无法肉眼阅读,调试相关数据就会比较麻烦.
2.传输层
---------------------------------------------------------------------------------------------------------------------------------
端口号
是一个整数,用来区分不同的进程.通过两个字节的无符号整数来表示的(0--65535),
实际上0这个端口比较特殊(随机使用一个端口),不会被使用(1--65535),1--1023属于已经被预定好的
(一些知名服务器已经提起预定了这些端口) 这样的端口被称为"知名端口号",日常开发时也会避开.
说是"知名",但是大部分服务器现在都已经不使用了.(80=>http, 443=>https, 22=>ssh)
同一时刻,同一个机器上,同一个协议,一个端口只能被一个进程绑定.一个进程可以绑定多个端口.
---------------------------------------------------------------------------------------------------------------------------------
编写服务器,肯定至少需要绑定一个端口,和客户端进行交互.
服务器运行的过程中,希望能够对这个服务器的行为,进行一些"控制"
比如让服务器重新加载某个数据/某个配置/修改服务器的某个功能
也可以通过网络通信来完成上述功能,可以让服务器绑定另一个端口(管理端口),
通过这个端口,编写一个客户端,给服务器发送一些"控制类"请求.
需要针对服务器运行状态进行检测和调试,需要查看服务器运行中某个关键变量的数值时.
千万不能使用调试器,会导致服务器的一些线程被阻塞,无法给客户端提供正确的服务.
可以通过日志打印,但是不太方便(要修改代码&&重启服务器)
可以让服务器绑定一个端口,然后实现一些相关的打印关键变量的逻辑,客户端发送对应调试请求.
---------------------------------------------------------------------------------------------------------------------------------
UDP协议
实际上UPD的数据并没有换行的操作;由UDP报头+UDP载荷组成;
UDP报头总共八个字节,固定长度,四个字段没有指定分隔符,而是通过固定长度来区分的.
UDP报文长度就是报头长度+载荷长度.
UDP长度
是由两个字节来表示的,则意味着最大值为65535==>64KB
一旦超过64KB载荷就会被截断.
解决方案:
(1)把大的数据包拆分成多个(很难行得通,分包组包充满不确定性,不一定能全都安全传输到)
(2)直接使用TCP(TCP对于长度是没有限制的,并且自身也带有可靠传输的机制,对整体通信质量有利,代码修改的成本比较低)
关于校验和
前提:网络传输的过程是,是非常容易出错的,光电信号,受环境干扰,使里面传输的情况发生改变.
校验和存在的目的就是为了能"发现"或者"纠正"这里出现的错误;
就可以在传输的过程中加入额外信息来发现/纠正传输的错误;(只是发现错误携带的信息可以较少,
若想纠正信息携带的额外信息就得很多了.
---------------------------------------------------------------------------------------------------------------------------------
校验和具体是怎么工作的呢?
希望校验和可以更加严格的检查数据的内容,可以结合内容/内容片段生成校验和,
如果出错的数据没有被校验和检查出来,这种情况也是会发生的.
UDP中使用两个字节作为校验和.
UDP使用简单有效的方案:CRC校验和(循环冗余校验)
把UDP数据报整个数据都进行遍历,分别取出每个字节,往一个两个字节的变量上累加.
发送方:基于UDP数据,计算得到一个校验和checksum1;
接收方:基于收到的UDP数据,计算得到一个新的校验和checksum2;
最终比较checksum1和checksum2的值.
还有两个比较经典的校验和算法:
MD5算法
本质上是一个"字符串hash算法",背后的实现过程是一个"数学过程",简单理解成套公式.
特点:
1.定长:无论输入的字符串有多长,算出的md5都是固定长度.(适合做为校验和算法)
2.分散:输入的内容,哪怕只有一点点发生改变,得到的md5值都会相差很多.(适合做为hash算法,出现hash冲突的概率低)
3.不可逆:根据输入的内容计算出md5非常简单,但是如果已知md5值还原出原来的内容理论上是不可行的.(合适做为加密算法)
SHA1算法
---------------------------------------------------------------------------------------------------------------------------------
基于UDP的应⽤层协议
NFS: ⽹络⽂件系统
TFTP: 简单⽂件传输协议
DHCP: 动态主机配置协
BOOTP: 启动协议(⽤于⽆盘设备启动)
DNS: 域名解析协议
---------------------------------------------------------------------------------------------------------------------------------
TCP协议
4位首部长度:
TCP报头的长度.UDP协议报头固定就是8个字节.对TCP来说,报头的长度是可变长的.
表示TCP报头长度有多少个4字节,故TCP头部最大长度为15 * 4 = 60字节.
保留6位:
保留位,虽然现在不用,可以以备不时之需.吸取UDP教训,当未来TCP需要新增属性/某个属性长度
不够用了,就可以把保留位拿出来,用作对应的作用.
TCP的结构不需要发生太大的改变,使得升级变得容易.用于考虑未来的可扩展性.
40字节选项:
TCP报头变长的主要原因,可以有也可以没有.