- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
最近返校做毕业设计,嵌入式和服务器需要敲定一个通信协议.
我问:"服务端选什么协议,MQTT或者Websocket?" 。
他来句:"TCP" 。
我又确认了一遍:"用什么通信协议?" 。
他又来了句:"TCP" 。
给我气笑了。最后在我的坚持下,他才勉强以他啥都能做,以我为准的理由妥协了.
简单回想盘点下问题:
他的回答:
TCP位于传输层,而这位大哥想做的就是基于Socket去完成网络消息传输。(Socket本身并不是协议,而是一个调用接口,通过Socket,我们才能使用TCP/IP协议) 。
MQTT,Websocket属于应用层协议,基于TCP.
首先,如果你使用过Java的原生Socket API,并进行过实验,你就能知道Socket是无法处理半包与粘包的.
什么是半包与粘包?可以参考我的Netty进阶文章 。
测试演示:
服务端代码:
缩小服务端的接收缓冲区为20字节,客户端10次发送"abc"数据.
请不要拿readUTF方法进行测试,sendUTF与readUTF是配套使用的,jdk原生进行了半包粘包的处理,对于发送消息添加了额外的消息用于处理问题.
如readLine等方法都是基于特定条件的处理了半包粘包问题(如本方法为换行划分一个消息帧),不适用于通用消息的网络传输.
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author zko0
* @date 2023/3/5 0:02
* @description
*/
public class SimpleSocketServer {
public static void main(String[] args) {
DataInputStream dataInputStream=null;
ServerSocket serverSocket=null;
Socket accept=null;
try {
serverSocket= new ServerSocket(8081);
System.out.println("socket服务创建");
//accept
accept = serverSocket.accept();
System.out.println("client连接");
//只设置接收缓冲区大小
accept.setReceiveBufferSize(10);
InputStream inputStream = accept.getInputStream();
dataInputStream= new DataInputStream(inputStream);
while (true){
//创建byte数组用于接收数据
byte[] bytes=new byte[10];
dataInputStream.read(bytes);
System.out.println(new String(bytes));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (dataInputStream!=null)dataInputStream.close();
if (serverSocket!=null)serverSocket.close();
if (accept!=null)accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端代码:
public class SimpleSocketClient {
public static void main(String[] args) {
Socket socket =null;
DataOutputStream dataOutputStream=null;
try{
socket=new Socket("127.0.0.1", 8081);
System.out.println("socket连接");
OutputStream outputStream= socket.getOutputStream();
dataOutputStream= new DataOutputStream(outputStream);
for (int i = 0; i < 10; i++) {
dataOutputStream.writeBytes("abc");
}
dataOutputStream.flush();
while (true);
}catch (IOException e) {
e.printStackTrace();
}
}
}
测试结果:
如下图所示,Server接收Client消息存在着半包粘包问题,一次消息abc无法被完整接收,在服务端第一次接收到了a,第二次接收到了bc+abc+ab一次完整+两次一半的消息.
很明显,如果服务端直接通过获取的消息进行处理,是存在问题的,对于TCP消息,需要进行半包和粘包的处理,才能使用消息.
正如那位“高含金量选手”所说,要对半包粘包进行处理。但是这种处理不仅仅是直接使用标识符就可以的,对于多出的额外信息或者不足的消息,是需要保留来拼凑出完整消息的。这部分你可以参考一下Java中 readLine() 方法 。
解决方式:
使用标识符进行消息帧分割 。
这种方式只适用于特定场景,因为在发送的消息中如果存在你的标识符,那么在进行帧解码的时候,就会将这部分内容作为标识符将字符串分割。由于分割位置错误,一次消息你可能会得到分割后的多个错误消息.
就比如 readLine() 使用于读取一行数据.
使用短连接 。
这种方式可以解决粘包的问题,但是无法解决半包问题。因为一次发送后,连接就会中断。那么Server获取的数据只会小于单次发送的数据.
定长帧解码器 。
在发送数据中,定义协议。如设置前5个字节代表发送数据的长度.
如果发送abc三字节内容,发送数据为:0 0 0 0 3 97 98 99 。
服务器读取前5个字节,就知道需要再读取3字节内容,为一次完整数据。如果单次只读取了2字节,那么会等待1个字节内容,拼凑出最后的3字节,单次完整消息.
你可以参考 readUTF() 方法,思路相同.
如果你了解TCP,你就会知道 TCP协议是自带心跳 的.
KEEP_ALIVE 在TCP协议里,本身的心跳包机制SO_KEEPALIVE,系统默认设置的是7200秒的启动时间, 默认是关闭的 .
有三个参数可以设置:p_keepalive_time、tcp_keepalive_probes、tcp_keepalive_intvl 。
分别表示连接闲置多久才开始发心跳包、连发几次心跳包没有回应表示连接已断开、心跳包之间间隔时间.
但是众所周知,几乎所有基于TCP协议的应用层协议,都自定义了心跳报文:如WebSocket,MQTT 。
Socket中Client关闭,Server能自动感受到连接断开啊?:
当SocketClient应用关闭,系统调用Socket的close、或者进程结束。操作系统会发送 FIN 数据包给服务器,所以Sevrer能够关闭TCP连接.
如果你在Client运行中,拔掉网线或者关闭电源。这时Client是没有机会发送 FIN 数据包的,这时Server就无法感知到Client已经断开了连接.
为什么TCP自带心跳还需要应用层定义心跳:
这部分比较偏向应用层内容,仅为个人感想.
在应用层协议中,协议报文都是对应用每个功能定制,且缩小了数据量的.
以MQTT3.1举例 。
MQTT第一次报文为CONNETCT报文。在一个网络连接上,客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接 .
报文内容:
固定头:1--->确定了报文的类型:CONNECT 。
可变头:
有效载荷:CONNECT报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的标志决定是否包含这些字段。如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码.
仅仅是一个客户端连接报文,就设计的非常全面且优雅,且完全契合应用目的.
个人思考后,个人认为将应用层协议分为 通用协议 与 特定系统协议 俩种.
通用协议:如HTTP协议,Websocket,MQTT协议,搭载用户自定义数据体进行通讯 。
特定系统协议:如Mysql协议,Redis的RESP协议,都是基于特定应用自己开发的一套网络协议 。
对于应用开发,个人认为通用协议更为合理,不仅进行了上述众多问题的处理。而且还有对应协议的应用意义:如身份验证等.
实在难以想象那种代码水平是否能将Socket自定义协议与消息处理写出基本的骨架出来。希望技术人员专注于技术,共同进步,不要盲目自大.
如果你有较好思考,欢迎指点.
最后此篇关于TCP与应用层协议的文章就讲到这里了,如果你想了解更多关于TCP与应用层协议的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
internal protocol Reducer { associatedtype S : BaseState associatedtype A : BaseActi
我在考虑我的应用程序中的验证检查,我认为在任何模型上调用 ValidatorFactory,实现 Validee,这意味着说哪个类负责 ValidatorCreation 听起来不错。但是下面的代码不
我已经定义了 2 个协议(protocol)。我需要第一个 (NameProtocol) 来执行 Equatable 协议(protocol)。而另一个类 (BuilderProtocol) 有一个返
在上传方面,WebDAV 协议(protocol)在哪些方面优于 HTTP 协议(protocol)。 Socket Upload 协议(protocol)和 WebDav Upload 协议(pro
是否可以在任何版本的 Swift 中扩展具有混合类/协议(protocol)类型约束的协议(protocol)?例如,仅当 Self 是 UIViewController 的子类并且符合 Protoc
我有一个协议(protocol) (ProtocolA),其中包含符合第二个协议(protocol) (ProtocolB) 的单个属性。 public protocol ProtocolA {
NSObject 协议(protocol)带有常用的协议(protocol)模板,但它似乎并不是协议(protocol)实际实现所必需的。将其排除在外似乎完全没有任何改变。那么,协议(protocol
我想根据这两种协议(protocol)的一般特征(例如开销(数据包)、安全性、信息建模和可靠性)来比较 OPC UA 和 MQTT。我在哪里可以找到每个协议(protocol)的开销和其他特性的一些示
使用 Swift 4,我正在尝试编写一个自定义协议(protocol),它提供对 @objc 协议(protocol)的一致性。 一些代码 更具体地说,我有一个自定义协议(protocol) Sear
我想定义一个在 Viper 架构中使用的协议(protocol),以使用具有弱属性的协议(protocol)在 Viper 组件之间建立连接,但我收到以下错误消息: 'weak' may only b
我在同一个网络中有 3 个 docker 容器: 存储 (golang) - 它提供了用于上传视频文件的 API。 主播 (nginx) - 它流式传输上传的文件 反向代理 (姑且称之为代理) 我有
我打算在我的项目中使用 php socket。它需要用户登录才能根据 session 填充内容。所以我的问题是,TCP/IP 协议(protocol)也像 HTTP 协议(protocol)一样为每个
目前,我的网站有两个版本。一种带有 https://-证书,一种没有。我想将我网站的 http 版本上的所有用户 301 重定向到我网站的 https://版本。 这似乎不可能,因为创建重定向将导致重
目前,我的网站有两个版本。一种带有 https://-证书,一种没有。我想将我网站的 http 版本上的所有用户 301 重定向到我网站的 https://版本。 这似乎不可能,因为创建重定向将导致重
我有一个 Swift View Controller ,它定义了一个在 Objective-C View Controller 中应该遵循的协议(protocol): ChildViewControl
我在客户那里有数百个硬件设备,需要通过telnet接口(interface)发送HTTP数据。 目标是等待数据的 Apache 2 Web 服务器和 PHP 脚本。 这已经可以正常工作了,但是我们发现
我发现如果我创建一个这样的协议(protocol): protocol MyProtocol { } 我不能这样做: weak var myVar: MyProtocol? 我找到了解决这个问题的方法
Xcode 基于模板生成了这个头文件: // this file is XYZAppDelegate.h #import @interface XYZAppDelegate : UIRespond
我在 github 中有一个公开的存储库,我正在开发一个开源应用程序,用于制作产品目录和小型 cms 内容。 我还有一个私有(private)仓库(不托管在github),它是在托管在github的开
您好,我想让别人看到私有(private) repo 代码,但不想公开我的 repo ,也不希望他们有能力更改内容。这可能吗?我查看了网站的“管理”部分,但没有找到合适的内容。谢谢大家。 最佳答案 据
我是一名优秀的程序员,十分优秀!