博主主页: 码农派大星.
数据结构专栏:Java数据结构
数据库专栏:MySQL数据库
JavaEE专栏:JavaEE
关注博主带你了解更多数据结构知识
1.应用层
之前编写完了基本的 java socket ,要知道,我们之前所写的所有代码都在应⽤层,都是为了 完成某项业务.
应用层是我们以后工作开发中经常用到的,主要涉及两种情况:
1.使用别人已经创建好的应用层协议(比如http)
2.自己定义应用层协议:
1)明确前后端交互过程中,需要传递哪些信息
2)明确组织这些信息的格式
针对信息组织方式有很多种(确保前端后端是同一种方式)
关于组织数据的格式:
1.xml
通过标签来组织数据
<request>
<userID>10</userID>
<position>E45I60</position>
</request>
优点:可读性提高
缺点:标签写起来非常繁琐,传输的时候消耗额外网络带宽
2.json
当前使用非常主流的网络通信的数格式
通过键值对来组织数据
{
"userID":"10",
"position":"E40I60"
}
优点:可读性较高,比xml简洁
缺点:传输的时候消耗额外网络带宽
3.yml(yaml)
强制要求了数据的格式
request:
userld: 1001;
position:"E60N45"
优点:可读性非常高,不额外消耗带宽
4.google protobuffer
前三个方案,都是关注可读性, protobuffer关注性能,牺牲了可读性(通过二进制的方式组织数据)
而protobuffer直接通过"位置"约定字段的含义,不需要传输key的名字,也会针对传输的数值,进行二进制的编码,起到一些"压缩"效果,极大缩短了传输的数据体积,带宽消耗就越小,效率越高.
2.传输层
传输层包含两个重要的协议UDP和TCP。
UDP:无连接,不可靠,面向数据报,全双工
TCP:有连接,可靠,面向字节流,全双工
端口号:占2个字节写一个服务器的时候必须手动指定一个端口号,用来区分不同程序。但写客户端的时候不用手动指定,系统会自动分配。同一时刻,同一个机器上,同一个协议,一个端口只能被一个进程绑定,一个进程可以绑定多个端口
1-1023称为知名端口号,给一些比较知名的服务器使用例如:22为ssh服务器,80为http协议,443为https服务器。1024-65535为普通端口号。
1.UDP协议
UDP报头:一共占8个字节,每一个部分都各占两个字节。
UDP报文长度:占2个字节,最长65535即64kb,这个长度是定长的,不能最初修改。
校验和: 网络传输过程中,受到环境因素(电信号,电磁波,光信号)的影响,使里面的传输信号发生改变,校验和存在的目的就是为了"发现"或"纠正",这样的错误.
随着业务发展,UDP的64KB已经不够满足业务要求了 ,所以TCP就很好来解决,并且对长度就没有限制了,自身也有可靠传输机制,代码修改成本也比较低
2.TCP协议
TCP全称为"传输控制协议(Transmission Control Protocol"),要对数据的传输进⾏⼀个详细的控制
源/⽬的端⼝号:表⽰数据是从哪个进程来,到哪个进程去;
32位序号/32位确认号: 后面介绍 ;
4位TCP报头⻓度:表⽰该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最⼤⻓度是15 * 4=60
保留(6)位: 充分吸取UDP报文长度无法扩展的教训,保留(6)位用来考虑未来的可扩展性,用来未来新增某个属性或者某个属性长度不够用,就可把保留位拿出来用.
6位标志位:
◦ URG:紧急指针是否有效
◦ ACK:确认号是否有效
◦ PSH:提⽰接收端应⽤程序⽴刻从TCP缓冲区把数据读⾛
◦ RST:对⽅要求重新建⽴连接;我们把携带RST标识的称为复位报⽂段
◦ SYN:请求建⽴连接;我们把携带SYN标识的称为同步报⽂段
◦ FIN:通知对⽅,本端要关闭了,我们称携带FIN标识的为结束报⽂段
2.1.确认应答
TCP的一个重要机制就是可靠传输,可靠传输就是当发送方把信息发出去,能够知道接收方是否接受了数据,一旦发现对方没有接受数据,那么发送方就会通多一定的手段来进行补救。
当发送方把信息发出去,接收方如收到信息,那么就会返回一个应答报文给发送方,如果发送方收到了这个报文,那么就证明信息发送成功了。但是在这个过程中可能会出现一些问题:例如我连续发送了两次请求,但是我后发送的消息却先响应了。
为了避免出现这样的情况,TCP就需要确保应答报文和发出去的数据能够相互对应,即使出现后发先至的现象时,仍然能够按照正常的顺序来理解数据:我们发数据的时候就给数据编一个号,同时接收端做出响应也编一个号,这个时候就需要用到报头中的32位序号和32位确认序号。
--------------------------------------------------完美分割线------------------------------------------------------------
TCP将每个字节的数据都进⾏了编号.即为序列号
每⼀个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下⼀次你从哪⾥开始发.
接收方这边调用read的时候如果没有数据,就会阻塞等待(前面代码写的是scanner读取, 本质上就是调用InputStream.read),此时虽然1001-2000这个数据到了,但是B不会让read解除阻塞,读到1001-2000这个数据还是要继续阻塞等待.直到说1-1000这个数据到达之后,read才会解除阻塞才会读取到1-1000,1001-2000,2001-3000数据,确保发送方write的顺序和接收方read的顺序是始终保持一致的
B接收方这边,操作系统内核里,会有一段内存空间,作为"内存缓冲区",收到的数据,就会先在接收缓冲区排队等待,只到开头的数据到了,应用程序才能真正读取到里面的数据.