【自定义网络协议】Java基于Vert.x的自定义TCP协议实现

ops/2024/10/20 6:37:11/

在现代的软件开发中,TCP协议广泛应用于需要高效、低延迟数据传输的场景。相较于HTTP协议,TCP提供了更底层的控制和更高的性能,适用于嵌入式设备、实时数据传输等应用。Vert.x是一个基于事件驱动、异步和多线程的高效开发框架,特别适合用于构建TCP服务。本文将介绍如何使用Vert.x在Java中实现自定义TCP协议。

一、环境准备

创建Maven项目:首先,创建一个Maven项目,并添加Vert.x核心依赖。

<dependencies>  <dependency>  <groupId>io.vertx</groupId>  <artifactId>vertx-core</artifactId>  <version>4.2.7</version>  </dependency>  
</dependencies>

二、定义协议格式

在自定义TCP协议时,需要定义协议的格式。这里采用一个简单的二进制格式:
协议结构实体类

java">
/*** 协议消息结构*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProtocolMessage<T> {/*** 消息头*/private Header header;/*** 消息体(请求或响应对象)*/private T body;/*** 协议消息头*/@Datapublic static class Header {/*** 魔数,保证安全性*/private byte magic;/*** 版本号*/private byte version;/*** 序列化器*/private byte serializer;/*** 消息类型(请求 / 响应)*/private byte type;/*** 状态*/private byte status;/*** 请求 id*/private long requestId;/*** 消息体长度*/private int bodyLength;}}

三、实现编码器和解码器

vert.x 的TCP 服务器收发的消息是 Buffer 类型,不能直接写入一个对象。因此,我们需要编码器和解码器,将 Java 的消息对象和 Buffer进行相互转换。

java">
/*** 协议消息解码器*/
public class ProtocolMessageDecoder {/*** 解码** @param buffer* @return* @throws IOException*/public static ProtocolMessage<?> decode(Buffer buffer) throws IOException {// 分别从指定位置读出 BufferProtocolMessage.Header header = new ProtocolMessage.Header();byte magic = buffer.getByte(0);// 校验魔数if (magic != ProtocolConstant.PROTOCOL_MAGIC) {throw new RuntimeException("消息 magic 非法");}header.setMagic(magic);header.setVersion(buffer.getByte(1));header.setSerializer(buffer.getByte(2));header.setType(buffer.getByte(3));header.setStatus(buffer.getByte(4));header.setRequestId(buffer.getLong(5));header.setBodyLength(buffer.getInt(13));// 解决粘包问题,只读指定长度的数据byte[] bodyBytes = buffer.getBytes(17, 17 + header.getBodyLength());// 解析消息体ProtocolMessageSerializerEnum serializerEnum = ProtocolMessageSerializerEnum.getEnumByKey(header.getSerializer());if (serializerEnum == null) {throw new RuntimeException("序列化消息的协议不存在");}Serializer serializer = SerializerFactory.getInstance(serializerEnum.getValue());ProtocolMessageTypeEnum messageTypeEnum = ProtocolMessageTypeEnum.getEnumByKey(header.getType());if (messageTypeEnum == null) {throw new RuntimeException("序列化消息的类型不存在");}switch (messageTypeEnum) {case REQUEST:RpcRequest request = serializer.deserialize(bodyBytes, RpcRequest.class);return new ProtocolMessage<>(header, request);case RESPONSE:RpcResponse response = serializer.deserialize(bodyBytes, RpcResponse.class);return new ProtocolMessage<>(header, response);case HEART_BEAT:case OTHERS:default:throw new RuntimeException("暂不支持该消息类型");}}}

编码器

java">
public class ProtocolMessageEncoder {/*** 编码** @param protocolMessage* @return* @throws IOException*/public static Buffer encode(ProtocolMessage<?> protocolMessage) throws IOException {if (protocolMessage == null || protocolMessage.getHeader() == null) {return Buffer.buffer();}ProtocolMessage.Header header = protocolMessage.getHeader();// 依次向缓冲区写入字节Buffer buffer = Buffer.buffer();buffer.appendByte(header.getMagic());buffer.appendByte(header.getVersion());buffer.appendByte(header.getSerializer());buffer.appendByte(header.getType());buffer.appendByte(header.getStatus());buffer.appendLong(header.getRequestId());// 获取序列化器ProtocolMessageSerializerEnum serializerEnum = ProtocolMessageSerializerEnum.getEnumByKey(header.getSerializer());if (serializerEnum == null) {throw new RuntimeException("序列化协议不存在");}Serializer serializer = SerializerFactory.getInstance(serializerEnum.getValue());byte[] bodyBytes = serializer.serialize(protocolMessage.getBody());// 写入 body 长度和数据buffer.appendInt(bodyBytes.length);buffer.appendBytes(bodyBytes);return buffer;}
}

四、实现TCP服务器

创建一个服务处理类,处理请求和响应

java">
public class TcpServerHandler implements Handler<NetSocket> {@Overridepublic void handle(NetSocket netSocket) {// 处理连接netSocket.handler(buffer -> {// 接受请求,解码ProtocolMessage<RpcRequest> protocolMessage;try {protocolMessage = (ProtocolMessage<RpcRequest>) ProtocolMessageDecoder.decode(buffer);} catch (IOException e) {throw new RuntimeException("协议消息解码错误");}RpcRequest rpcRequest = protocolMessage.getBody();// 处理请求// 构造响应结果对象RpcResponse rpcResponse = new RpcResponse();try {// 获取要调用的服务实现类,通过反射调用Class<?> implClass = LocalRegistry.get(rpcRequest.getServiceName());Method method = implClass.getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());Object result = method.invoke(implClass.newInstance(), rpcRequest.getArgs());// 封装返回结果rpcResponse.setData(result);rpcResponse.setDataType(method.getReturnType());rpcResponse.setMessage("ok");} catch (Exception e) {e.printStackTrace();rpcResponse.setMessage(e.getMessage());rpcResponse.setException(e);}// 发送响应,编码ProtocolMessage.Header header = protocolMessage.getHeader();header.setType((byte) ProtocolMessageTypeEnum.RESPONSE.getKey());ProtocolMessage<RpcResponse> responseProtocolMessage = new ProtocolMessage<>(header, rpcResponse);try {Buffer encode = ProtocolMessageEncoder.encode(responseProtocolMessage);netSocket.write(encode);} catch (IOException e) {throw new RuntimeException("协议消息编码错误");}});}
}

实现TCP服务器

java">public class VertxTcpServer implements HttpServer {private byte[] handleRequest(byte[] requestData)  {// 在这里编写处理请求的逻辑,根据 requestData 构造响应数据并返回return "Hello, client!".getBytes();}@Overridepublic void doStart(int port) {// 创建 Vert.x 实例Vertx vertx = Vertx.vertx();// 创建 TCP 服务器NetServer server = vertx.createNetServer();// 处理请求server.connectHandler(new TcpServerHandler());// 启动 TCP 服务器并监听指定端口server.listen(port, result -> {if (result.succeeded()) {System.out.println("TCP server started on port " + port);} else {System.err.println("Failed to start TCP server: " + result.cause());}});}public static void main(String[] args) {new VertxTcpServer().doStart(8888);}
}

五、编写一个TCP客户端用于测试服务器。

java">
public class VertxTcpClient {private final static String HOST = "127.0.0.1";private final static Integer PORT = 8888;private final static byte SETSERIALIZER = 0x1;private final static byte VERSION = 0x1;private final static byte MAGIC = 0x1;private final static byte TYPE = 0x1;public void start() {try {// 发送 TCP 请求Vertx vertx = Vertx.vertx();NetClient netClient = vertx.createNetClient();netClient.connect(PORT, HOST,result -> {if (result.succeeded()) {System.out.println("Connected to TCP server");io.vertx.core.net.NetSocket socket = result.result();// 发送数据// 构造消息ProtocolMessage<Object> protocolMessage = new ProtocolMessage<>();ProtocolMessage.Header header = new ProtocolMessage.Header();header.setMagic(MAGIC);header.setVersion(VERSION);header.setSerializer((SETSERIALIZER);header.setType(TYPE);header.setRequestId(IdUtil.getSnowflakeNextId());protocolMessage.setHeader(header);protocolMessage.setBody(null);// 编码请求try {Buffer encodeBuffer = ProtocolMessageEncoder.encode(protocolMessage);socket.write(encodeBuffer);} catch (IOException e) {throw new RuntimeException("协议消息编码错误");}// 接收响应socket.handler(buffer -> {try {ProtocolMessage<Response> ResponseProtocolMessage = (ProtocolMessage<Response>) ProtocolMessageDecoder.decode(buffer);} catch (IOException e) {throw new RuntimeException("协议消息解码错误");}});} else {System.err.println("Failed to connect to TCP server");}});// 记得关闭连接netClient.close();} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {new VertxTcpClient().start();}
}

六、总结
本文介绍了如何使用Vert.x在Java中实现自定义TCP协议。通过定义协议格式、实现TCP服务器和客户端,我们可以构建高效、低延迟的数据传输系统。Vert.x的异步、事件驱动特性使得它在处理大量并发连接时表现优异,非常适合用于实时通信、物联网等场景。希望本文对你有所帮助,祝你编程愉快!


http://www.ppmy.cn/ops/121913.html

相关文章

【Linux网络】详解TCP协议(3)

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; Linux网络 &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解 TCP的流量控制和滑动窗口 的相关内容。 如果看到最后您觉得这篇…

Linux和指令初识

前言 Linux是我们在服务器中常用的操作系统&#xff0c;我们有必要对这个操作系统有足够的认识&#xff0c;并且能够使相关的指令操作。今天我们就来简单的认识一下这个操作的前世今生&#xff0c;并且介绍一些基础的指令操作 Linux的前世今生 要说Linux&#xff0c;还得从U…

深度学习基础—卷积神经网络示例

1.卷积神经网络的结构 在之前的博客《深度学习—简单的卷积神经网络》&#xff0c;仅由卷积层构成网络的全部&#xff0c;这还不是标准的网络结构&#xff0c;本文将继续介绍标准的卷积神经网络结构有哪些&#xff1f; 深度学习基础—简单的卷积神经网络https://blog.csdn.net…

qemu-system-aarch64开启user用户模式网络连接

一、问题 在使用qemu构建arm64的虚拟机时&#xff0c;虚拟机没有网络&#xff0c;桥接方式相对麻烦&#xff0c;我只是需要联网更新即可。与宿主机的通信我使用共享文件夹即可满足要求。 使用指令启动虚拟机时&#xff0c;网络部分的参数为 -net user,hostfwdtcp::10022-:22 …

【目标检测】桥梁表面缺陷检测数据集6710张7类缺陷VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;6718 标注数量(xml文件个数)&#xff1a;6718 标注数量(txt文件个数)&#xff1a;6718 标注…

vue出现Component name “Politic“ should always be multi-word错误

效果 原因 组件名不能为单个单词&#xff0c;怕和html标签混淆 解决方法 1.选择多个单词区分 2.修改package.json里的rules规则&#xff0c;忽略文件命名校验

MATLAB GUI组件全解析:构建交互式应用程序

MATLAB的图形用户界面&#xff08;GUI&#xff09;是一个功能强大的工具&#xff0c;它允许开发者创建直观且用户友好的界面。这些界面&#xff0c;也称为应用程序或app&#xff0c;提供了点击控制&#xff0c;使得用户无需学习编程语言或输入命令即可运行应用程序。本文将详细…

模板方法模式

简介 模板方法模式&#xff08;Template Method Pattern&#xff09;又叫作模板模式&#xff0c;指定义一个操作中的算法的框架&#xff0c;而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤&#xff0c;属于行为型设计模…