Protostuff反序列化失败,导致Tomcat程序shutdown问题

Protostuff是一个基于protobuf实现的序列化方法。前段时间,初次接手项目,需要将java bean存储到redis中,一切编码完毕,上测试环境,程序很快就会停止,后来查看tomcat日志,发现如下错误:

后来经过多次定位,发现新写的java bean,新写了带参的构造函数,没有添加默认的无参构造函数。而一个类如果已经写构造函数,编译的时候jvm不会默认添加无参构造函数,这就导致了问题。在Protostuff源码中有这么一段代码,需要使用对应java bean的无参构造函数,这样就导致了如上错误的出现。

public Schema<T> getSchema() {
            Schema<T> schema = this.schema;
            if(schema == null) {
                synchronized(this) {
                    schema = this.schema;
                    if(this.schema == null) {
                        if(Message.class.isAssignableFrom(this.typeClass)) {
                            try {
                                Message<T> m = (Message)this.typeClass.newInstance();//使用的无参构造函数创建类
                                this.schema = schema = m.cachedSchema();
                            } catch (InstantiationException var5) {
                                throw new RuntimeException(var5);
                            } catch (IllegalAccessException var6) {
                                throw new RuntimeException(var6);
                            }
                        } else {
                            this.schema = schema = this.strategy.newSchema(this.typeClass);
                        }
                    }
                }
            }

<think>首先,用户的问题是关于在服务器端接收客户端发送的数据并进行反序列化处理。用户提到了序列化反序列化,参考了引用[1]、[2]、[3],这些引用涉及序列化对象、Socket传输和C#实现。 关键点: - 用户想实现服务器端接收数据并反序列化。 - 引用中提到了使用Socket、BinaryFormatter、MemoryStream等。 - 用户指定了使用C#,因为引用[3]是C#代码。 我的回答需要: - 使用中文。 - 结构清晰,逐步解决问题。 - 确保真实可靠。 - 遵循系统级指令:行内数学表达式用$...$,独立公式用$$...$$(但在这个问题中可能不需要数学表达式),LaTeX语法正确。 - 生成相关问题。 回顾系统级指令: - 行内数学表达式用$...$:例如,如果有数学内容,如$x^2$。 - 独立公式用$$...$$。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾添加引用标识,如[^1]。 在这个问题中,可能不需要数学表达式,因为主题是编程和序列化。但如果有相关概念,我可以考虑。 用户的问题核心:如何在服务器端接收客户端发送的数据并反序列化。 从引用中: - 引用[1]:简单模拟序列化。 - 引用[2]:序列化对象的类需要在两端一致,使用dll或Protocol Buffers。 - 引用[3]:C#代码示例,使用BinaryFormatter序列化,MemoryStream,Socket发送。 所以,我应该基于C#提供解决方案。 步骤: 1. **理解序列化反序列化**:序列化是将对象转换为字节流,以便传输或存储;反序列化是将字节流还原为对象。 2. **使用Socket接收数据**:服务器端需要监听Socket连接,接收数据。 3. **反序列化数据**:使用如BinaryFormatter或其他序列化工具。 在C#中,常见序列化方式: - BinaryFormatter(但注意,它在.NET Core/5+中不推荐使用,因为安全原因)。 - JSON序列化(如Newtonsoft.Json或System.Text.Json)。 - XML序列化。 - Protocol Buffers(如Google.Protobuf)。 用户提到引用[2]中Protocol Buffers,但我应该基于标准C#方式。 由于引用[3]使用BinaryFormatter,我可以先基于那个,但提醒潜在问题。 结构回答: - 介绍序列化反序列化的概念。 - 提供服务器端代码示例。 - 解释关键步骤。 - 添加注意事项。 然后,生成相关问题。 最终回答格式: - 回答部分。 - §§相关问题§§部分。 确保引用:如果我在回答中引用用户的引用,添加引用标识,如[^1]。 现在,构建回答。 **步骤1: 设置服务器Socket** - 创建Socket,绑定到IP和端口,监听连接。 **步骤2: 接收数据** - 接受客户端连接,接收数据到字节数组。 **步骤3: 反序列化** - 使用BinaryFormatter或类似工具反序列化字节数组为对象。 代码示例基于引用[3]。 注意:BinaryFormatter在.NET Framework中可用,但在.NET Core中需要添加兼容性包或使用其他方式。 提供完整示例。 回答大纲: 1. **介绍**:简要解释序列化反序列化在Socket通信中的作用。 2. **服务器端实现步骤**: a. 创建Socket并绑定。 b. 监听连接。 c. 接受客户端连接。 d. 接收数据。 e. 反序列化数据。 3. **代码示例**:提供C#代码。 4. **注意事项**:如类定义一致、安全考虑。 5. **生成相关问题**。 使用中文。</think>### 服务器端接收数据并反序列化的实现(基于C#) 在Socket通信中,服务器端需要接收客户端发送的序列化数据并进行反序列化处理。序列化是将对象转换为字节流的过程,便于网络传输;反序列化则是将字节流还原为原始对象[^1]。这个过程要求客户端和服务器端的序列化类定义一致,否则会导致反序列化失败(如引用[2]所述)。以下基于C#提供逐步实现方案。我使用`System.Net.Sockets`和`System.Runtime.Serialization.Formatters.Binary`(BinaryFormatter),但注意:BinaryFormatter在.NET Core/5+中已不推荐使用(因安全漏洞),建议在实际项目中改用JSON或Protocol Buffers。代码示例基于引用[3]优化。 #### 步骤1: 定义序列化类(确保客户端和服务器端共享) 序列化对象前,必须定义一个可序列化的类。客户端和服务器端应使用相同的类定义(或通过共享DLL实现类一致性,避免反序列化错误[^2])。 ```csharp using System; using System.Runtime.Serialization; [Serializable] // 标记为可序列化 public class UserData { public string Name { get; set; } public int Age { get; set; } // 添加其他属性 } ``` #### 步骤2: 服务器端实现(接收数据并反序列化) 服务器端代码包括以下关键步骤: 1. **创建Socket并绑定端口**:监听指定IP和端口。 2. **接收客户端连接**:使用`Accept`方法接受连接。 3. **接收字节数据**:从Socket流中读取字节数组。 4. **反序列化字节数据**:将字节流还原为对象。 ```csharp using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Runtime.Serialization.Formatters.Binary; class Server { static void Main() { // 1. 创建Socket并绑定到端口 IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 5684); // 监听所有IP,端口5684 Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); listener.Bind(endPoint); listener.Listen(10); // 允许最多10个客户端排队连接 Console.WriteLine("服务器启动,等待客户端连接..."); while (true) { // 2. 接受客户端连接 Socket clientSocket = listener.Accept(); Console.WriteLine($"客户端 {clientSocket.RemoteEndPoint} 已连接"); // 3. 接收字节数据 byte[] buffer = new byte[1024]; // 缓冲区大小根据数据量调整 int bytesReceived = clientSocket.Receive(buffer); byte[] data = new byte[bytesReceived]; Array.Copy(buffer, data, bytesReceived); // 复制有效数据 // 4. 反序列化数据 try { using (MemoryStream ms = new MemoryStream(data)) { BinaryFormatter formatter = new BinaryFormatter(); UserData userData = (UserData)formatter.Deserialize(ms); // 反序列化为对象 Console.WriteLine($"反序列化成功: 姓名={userData.Name}, 年龄={userData.Age}"); } } catch (Exception ex) { Console.WriteLine($"反序列化失败: {ex.Message}"); } // 关闭连接 clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); } } } ``` #### 关键点解释 - **接收数据**:`clientSocket.Receive` 方法读取字节流,缓冲区大小应足够容纳序列化数据(实际中可循环读取以处理大数据)。 - **反序列化**:使用`MemoryStream`包裹字节数组,`BinaryFormatter.Deserialize` 将流还原为对象。反序列化前,确保`UserData`类在服务器端定义(或引用共享DLL[^2])。 - **错误处理**:添加`try-catch`块捕获异常(如类不匹配或数据损坏)。 - **性能优化**:对于高并发,使用异步方法(如`AcceptAsync`和`ReceiveAsync`)避免阻塞线程。 #### 注意事项 - **类一致性**:客户端和服务器端必须共享相同的序列化类。否则,反序列化失败。解决方案: - 将类定义在共享DLL中,两端引用(如引用[2]所述)。 - 使用跨平台序列化库(如Protocol Buffers或JSON.NET),它们通过协议定义文件(.proto)确保兼容性[^2]。 - **安全风险**:BinaryFormatter易受攻击(如反序列化漏洞)。在.NET Core中,推荐改用`System.Text.Json`或`Newtonsoft.Json`: ```csharp // 替代方案:JSON反序列化示例 string jsonString = Encoding.UTF8.GetString(data); UserData userData = JsonSerializer.Deserialize<UserData>(jsonString); ``` - **大数据处理**:如果数据量大,使用`NetworkStream`分块读取,避免内存溢出。 通过以上步骤,服务器端能可靠接收并反序列化客户端数据。完整项目需添加客户端代码(序列化数据并发送),引用[3]提供了客户端示例[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值