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

Unity C# TCP客户端需关闭Socket才发送数据的问题求助

问题分析与解决方案

你遇到的问题核心有两个关键点,导致消息无法即时被服务器接收:


1. 服务器端构造函数的阻塞问题

你的User类构造函数里有一个持续读取readLine()的循环,这个循环会阻塞线程直到客户端关闭Socket——因为客户端没有发送换行符,readLine()会一直等待输入结束,导致User线程的run()方法根本没机会执行,直到客户端断开连接,这个循环才会退出,构造函数执行完成,run()才开始运行。

// 问题代码:User构造函数中的阻塞循环
String temp = null;
while((temp = in.readLine()) != null) {
    data = temp;
}

2. 客户端未发送换行符,服务器readLine()无法触发返回

服务器用in.readLine()读取数据,这个方法的特性是必须读取到换行符(\n或\r\n)或者流结束才会返回。而你的客户端发送的是纯文本"Message From Client",没有添加换行符,所以服务器会一直阻塞在readLine()调用上,直到客户端关闭Socket,流结束才会拿到数据。


修复方案

客户端修改:添加换行符并确保流正确刷新

在发送的消息末尾添加换行符,让服务器的readLine()能识别到消息结束;另外可以调整NetworkStream的使用方式,确保数据正确发送:

TcpClient client = new TcpClient();
client.Connect("127.0.0.1", 1990);
NetworkStream stream = client.GetStream();
stream.ReadTimeout = 10;
stream.WriteTimeout = 10;
// 添加换行符,让服务器readLine()能识别消息结束
string message = "Message From Client\n";
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(message);
stream.Write(outStream, 0, outStream.Length);
stream.Flush();
// 如果需要保持连接,可以不要立即关闭client,比如添加延时测试
// System.Threading.Thread.Sleep(5000);
// client.Close();

服务器端修改:移除构造函数的阻塞循环,将读取逻辑移到run()方法中

把构造函数里的读取逻辑移到run()方法的循环里,这样线程启动后就能即时处理客户端发送的消息,而不是在构造阶段就阻塞:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.*;
import java.util.Vector;

public class Server {
    public static volatile Vector<User> usersList = new Vector<>();

    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(1990);
        Socket socket = null;
        DataInputStream in = null;
        DataOutputStream out = null;
        int i = 1;
        while(true) {
            try {
                socket = ss.accept();
                in = new DataInputStream(socket.getInputStream());
                out = new DataOutputStream(socket.getOutputStream());
                // STARTING NEW DEDICATED THREAD
                User user = new User(i, socket.getInetAddress().toString(), socket, in, out);
                usersList.add(user);
                user.start();
            } catch (Exception e) {
                e.printStackTrace();
            }
            i++;
        }
    }
}

class User extends Thread {
    int uid = -1;
    int opponentUid = -1;
    String ipAddress = null;
    String data = null;
    Socket s;
    DataInputStream in;
    DataOutputStream out;

    // 修改构造函数:移除阻塞的readLine循环
    User(int uid, String ipAddress, Socket s, DataInputStream in, DataOutputStream out) throws Exception {
        this.uid = uid;
        this.ipAddress = ipAddress.replace("/", "");
        this.s = s;
        this.in = in;
        this.out = out;
        // 初始化时可以先发送uid给客户端
        out.writeUTF(String.valueOf(uid));
    }

    public void run() {
        System.out.println("\nUser Id: " + uid + "\nIP Address: " + ipAddress);
        try {
            String temp = null;
            // 将读取逻辑移到run方法的循环中,实时处理消息
            while((temp = in.readLine()) != null) {
                data = temp;
                System.out.println("Client " + uid + ": " + data);
                
                String[] vars = data.split("#");
                // STRING SPLITTER TO GET THE DESTINATION IP
                if(vars[0].equals("matchmaking")) {
                    for(User client : Server.usersList) {
                        if(client.opponentUid == -1 && client.uid != this.uid) {
                            client.opponentUid = this.uid;
                            this.opponentUid = client.uid;
                            out.writeUTF("matched#" + String.valueOf(client.uid));
                            client.out.writeUTF("matched#" + String.valueOf(this.uid));
                        }
                    }
                } else if(vars[0].equals("message")) {
                    for(User client : Server.usersList) {
                        if(this.opponentUid == client.uid) {
                            client.out.writeUTF(vars[1]);
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("Client " + uid + " Disconnected");
            Server.usersList.remove(this);
        }
    }
}

额外说明

  • readLine()会自动忽略换行符,所以你在服务器拿到的data不会包含末尾的\n,不影响后续的字符串分割逻辑。
  • 如果后续需要发送更复杂的消息,建议定义统一的消息格式(比如固定长度的消息头+消息体),避免依赖换行符作为分隔符,这样更稳定。

内容的提问来源于stack exchange,提问作者Madhusudan Sharma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:24:56