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
相关产品推荐
相关产品推荐

