You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 09:12:16