Java Socket服务端无法接收C#客户端消息的问题求解
问题描述
使用C#开发客户端Socket程序对接Java实现的Socket服务端时,C#客户端始终提示消息发送成功,但Java服务端读取到的消息内容始终为空。
C#客户端原始代码
using Godot; using System; using System.Net; using System.Net.Sockets; using System.Text; public class Scanning: Control { // Called when the node enters the scene tree for the first time. public override void _Ready() { string toSend = "Handshake:Interface"; IPEndPoint serverAddress = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 429); Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); clientSocket.Connect(serverAddress); // Sending int toSendLen = System.Text.Encoding.ASCII.GetByteCount(toSend); byte[] toSendBytes = System.Text.Encoding.ASCII.GetBytes(toSend); byte[] toSendLenBytes = System.BitConverter.GetBytes(toSendLen); clientSocket.Send(toSendLenBytes); clientSocket.Send(toSendBytes); Console.WriteLine("Client received: " + rcv);*/ clientSocket.Close(); } }
Java服务端原始代码
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.ArrayList; public class ClientHandler { public PrintWriter out; public BufferedReader in; public Socket clientSocket; public Thread thread; public ClientHandler(Socket client) { clientSocket = client; System.out.println("Processing connection from " + clientSocket.getInetAddress()); thread = new Thread(new Runnable() { @Override public void run() { try { out = new PrintWriter(clientSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF-8")); ArrayList<String> cmdBuffer = new ArrayList<String>(); while (!Main.test.get()) { String msg = in.readLine(); if(msg.startsWith("Handshake")) { System.out.println("LOG: " + msg); String[] work = msg.split(":"); if (!MessageChannel.clients.contains(work[1])) { respond("1/I/Handshake successful for/" + work[1]); respond("Connected: " + MessageChannel.clients); MessageChannel.buffer.put(work[1], cmdBuffer); MessageChannel.clients.add(work[1]); } } else if (msg.startsWith("Disconnect")) { System.out.println("LOG: " + msg); String[] work = msg.split(":"); MessageChannel.clients.remove(work[1]); MessageChannel.buffer.remove(work[1]); out.println("I/Disconnected/" + work[1]); } else if(msg.startsWith("List")) { System.out.println("LOG: " + msg); respond("0/Connected: " + MessageChannel.clients); } else { String[] work = msg.split(":"); if (MessageChannel.clients.contains(work[0])){ if (work[1].equals("Lookup")) { out.println(MessageChannel.buffer.get(work[0]).size() +"/Lookup:"); for (String e : MessageChannel.buffer.get(work[0])) { out.println(e); } MessageChannel.buffer.replace(work[0], new ArrayList<String>()); } else { System.out.println("LOG: " + msg); MessageChannel.buffer.get(work[1]).add(work[2]); respond("0/I/Success"); } } } } } catch (IOException e1) { e1.printStackTrace(); } } }); thread.start(); } public void respond(String msg) { out.println(msg); System.out.println("LOGR: " + msg); } }
故障原因
- 消息读取规则不匹配:Java端使用
BufferedReader.readLine()读取消息,该方法必须读到换行符(\n或\r\n)才会返回一行完整内容,C#端发送的消息未携带换行符,服务端会一直阻塞等待行结束标记,无法返回有效消息。 - 冗余数据干扰解析:C#端额外发送了4字节的消息长度头,但Java端没有编写对应读取长度头的逻辑,这4个字节会被当作普通消息内容,直接打乱后续文本解析。
- 编码不统一:C#端使用ASCII编码发送内容,Java端使用UTF-8编码读取,纯英文场景虽不会触发乱码,但协议不统一会为后续非英文内容传输埋下隐患。
- 连接关闭逻辑错误:C#端发送完数据立刻调用
Close()强制关闭Socket,会触发TCP连接重置,缓冲区中未被服务端读取的数据会被直接丢弃,进一步导致服务端读空。 - 代码存在无效片段:C#代码中残留未闭合的注释片段
*/,本身存在语法错误。
修复方案
C#客户端修改
- 删除多余的4字节长度头发送逻辑,匹配服务端行文本协议规则。
- 在发送的消息末尾添加换行符
\n,对齐readLine()的读取要求。 - 统一使用UTF-8编码发送内容,和服务端编码规则对齐。
- 替换强制关闭逻辑为优雅关闭,发送完成后先调用
Shutdown()标记连接结束,等待缓冲区数据传输完成后再关闭连接。 - 删除残留的无效注释代码。
修改后的核心发送逻辑示例:
public override void _Ready() { // 消息末尾加换行符 string toSend = "Handshake:Interface\n"; IPEndPoint serverAddress = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 429); Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); clientSocket.Connect(serverAddress); // 统一用UTF8编码,直接发送完整行内容 byte[] toSendBytes = System.Text.Encoding.UTF8.GetBytes(toSend); clientSocket.Send(toSendBytes); // 优雅关闭连接,确保数据发送完成 clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); }
服务端可选优化
现有Java服务端的行文本解析逻辑无需大改,只需补充空值判断即可:in.readLine()在连接异常断开时会返回null,直接调用startsWith()会触发空指针异常,在读取消息后增加判断即可:
String msg = in.readLine(); // 连接断开时退出循环,释放资源 if (msg == null) { break; } // 保留原有消息解析逻辑
如果后续需要改用带长度头的二进制协议,再同步修改两端逻辑:C#端确保长度字段字节序和Java端对齐,Java端放弃readLine()方法,先读取固定4字节长度,再按长度读取对应字节数转成字符串即可。
内容的提问来源于stack exchange,提问作者hypertext
相关产品推荐
相关产品推荐

