基于Socket、线程与Mutex的C#项目中CSV文件修改失效求助
问题描述
我是Luís,正在开发ServiMoto公司的移动服务管理客户端/服务器系统,服务涵盖园林检查、消防干预、邮政配送及披萨外卖。该系统支持骑手客户端连接服务器接收任务、更新任务状态及请求新任务,服务器通过C# Socket实现,利用Mutex保证CSV文件的独占访问,用于记录已分配任务与客户端信息。
当前遇到的问题:调用UpdateCompletedTask方法标记任务完成时,CSV文件始终无变化,多次修改该方法仍未解决,恳请技术帮助。
核心代码
Server类
// Object Mutex to ensure exclusive access to CSV files static Mutex mutex = new Mutex(); // Method to update the CSV file with the completion of a task static void UpdateCompletedTask(string fileName, string taskId) { // Locks the mutex to ensure exclusive access mutex.WaitOne(); try { string[] lines = File.ReadAllLines(fileName); for (int i = 0; i < lines.Length; i++) { if (lines[i].StartsWith(taskId)) { // Replaces "in progress" with "completed" only if "in progress" is present if (lines[i].Contains("in progress")) { lines[i] = lines[i].Replace("in progress", "completed"); } break; } } File.WriteAllLines(fileName, lines); } finally { // Releases the mutex mutex.ReleaseMutex(); } } // Method to assign a new task to the client and update the corresponding CSV file static void AllocateNewTask(string fileName, string clientId) { // Locks the mutex to ensure exclusive access mutex.WaitOne(); try { // Logic to assign a new task to the client and update the CSV file // For example, it may involve reading the file to find an available task and updating its status } finally { // Releases the mutex mutex.ReleaseMutex(); } } // Server IP address and port IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); int port = 8888; // Starts the server and listens for connections server = new TcpListener(ipAddress, port); server.Start(); Console.WriteLine("Server started..."); // Accepts client connection TcpClient client = server.AcceptTcpClient(); Console.WriteLine("Client connected!"); // Prepares network streams NetworkStream stream = client.GetStream(); byte[] data = new byte[256]; StringBuilder response = new StringBuilder(); int bytesRead; // Reads data received from the client while ((bytesRead = stream.Read(data, 0, data.Length)) != 0) { response.Append(Encoding.ASCII.GetString(data, 0, bytesRead)); Console.WriteLine("Received message: {0}", response.ToString()); // Checks the type of received message if (response.ToString().StartsWith("CONNECT")) { // Responds with success byte[] msg = Encoding.ASCII.GetBytes("100 OK"); stream.Write(msg, 0, msg.Length); Console.WriteLine("Response sent: 100 OK"); } else if (response.ToString().StartsWith("TASK_COMPLETE")) { // Extracts the ID of the completed task string completedTaskId = response.ToString().Substring("TASK_COMPLETE".Length).Trim(); // Updates the corresponding CSV file UpdateCompletedTask("Service_A.csv", completedTaskId); // Responds with task completion confirmation byte[] msg = Encoding.ASCII.GetBytes("TASK_COMPLETED"); stream.Write(msg, 0, msg.Length); Console.WriteLine("Response sent: TASK_COMPLETED"); } else if (response.ToString() == "REQUEST_TASK") { // Logic to assign a new task to the client and update the CSV file // Here you can call the AllocateNewTask() method to assign the new task } else if (response.ToString() == "QUIT") { // Responds with connection closure byte[] msg = Encoding.ASCII.GetBytes("400 BYE"); stream.Write(msg, 0, msg.Length); Console.WriteLine("Response sent: 400 BYE"); // Closes the connection client.Close(); break; } else { // Responds with error byte[] msg = Encoding.ASCII.GetBytes("ERROR"); stream.Write(msg, 0, msg.Length); Console.WriteLine("Response sent: ERROR"); } // Clears the StringBuilder for the next message response.Clear(); }
客户端类
// Server IP address and port string serverIp = "127.0.0.1"; int port = 8888; // Creates an instance of the TCP client TcpClient client = new TcpClient(serverIp, port); Console.WriteLine("Connected to server..."); // Prepares network streams NetworkStream stream = client.GetStream(); byte[] data = new byte[256]; string response = string.Empty; Console.WriteLine("Enter your Client ID: "); string clientId = Console.ReadLine(); // Sends connection message string connectMessage = "CONNECT"; byte[] connectMsg = Encoding.ASCII.GetBytes(connectMessage); stream.Write(connectMsg, 0, connectMsg.Length); Console.WriteLine("Message sent: {0}", connectMessage); // Reads the server's response int bytesReceived = stream.Read(data, 0, data.Length); response = Encoding.ASCII.GetString(data, 0, bytesReceived); Console.WriteLine("Response received: {0}", response); while (true) { Console.WriteLine("Choose an option:"); Console.WriteLine("1. Complete task"); Console.WriteLine("2. Request new task"); Console.WriteLine("3. Quit"); Console.Write("Option: "); string option = Console.ReadLine(); switch (option) { case "1": // Sends task completion message Console.WriteLine("Enter the ID of the completed task: "); string completedTaskId = Console.ReadLine(); string completionMessage = $"TASK_COMPLETE <{completedTaskId}>"; byte[] completionMsg = Encoding.ASCII.GetBytes(completionMessage); stream.Write(completionMsg, 0, completionMsg.Length); Console.WriteLine("Message sent: {0}", completionMessage); // Reads the server's response int completionBytesReceived = stream.Read(data, 0, data.Length); response = Encoding.ASCII.GetString(data, 0, completionBytesReceived); Console.WriteLine("Response received: {0}", response); break; case "2": // Sends request for new task string requestMessage = "REQUEST_TASK"; byte[] requestMsg = Encoding.ASCII.GetBytes(requestMessage); stream.Write(requestMsg, 0, requestMsg.Length); Console.WriteLine("Message sent: {0}", requestMessage); // Reads the server's response int requestBytesReceived = stream.Read(data, 0, data.Length); response = Encoding.ASCII.GetString(data, 0, requestBytesReceived); Console.WriteLine("Response received: {0}", response); break; case "3": // Sends quit message string quitMessage = "QUIT"; byte[] quitMsg = Encoding.ASCII.GetBytes(quitMessage); stream.Write(quitMsg, 0, quitMsg.Length); Console.WriteLine("Message sent: {0}", quitMessage); // Reads the server's response int quitBytesReceived = stream.Read(data, 0, data.Length); response = Encoding.ASCII.GetString(data, 0, quitBytesReceived); Console.WriteLine("Response received: {0}", response); // Closes the connection and exits the loop client.Close(); return; default: Console.WriteLine("Invalid option."); break; } }
问题分析与解决方案
核心问题:任务ID提取不匹配
客户端发送的任务完成消息格式是TASK_COMPLETE <任务ID>,但服务器提取任务ID时仅去除了空格,保留了尖括号<>,导致传递给UpdateCompletedTask的taskId是带尖括号的字符串(比如<T1001>),而CSV文件中任务行是以纯任务ID开头(比如T1001, ..., in progress),lines[i].StartsWith(taskId)自然匹配失败,根本不会执行状态替换逻辑。
修复方案:修改服务器端任务ID提取逻辑
将服务器中提取任务ID的代码改为:
string completedTaskId = response.ToString().Substring("TASK_COMPLETE".Length).Trim().Trim('<', '>');
这样就能去掉尖括号,得到纯任务ID,确保和CSV中的任务ID格式一致。
其他排查点
- 文件路径验证:确认
Service_A.csv存在于服务器程序的当前工作目录(可通过Directory.GetCurrentDirectory()输出验证),或者使用绝对路径避免路径问题。 - 任务状态检查:确保CSV中目标任务的状态确实是
in progress,否则替换逻辑不会执行。 - 添加调试日志:在
UpdateCompletedTask方法中添加日志,验证是否找到目标行、是否执行了替换:
static void UpdateCompletedTask(string fileName, string taskId) { mutex.WaitOne(); try { string[] lines = File.ReadAllLines(fileName); bool found = false; bool updated = false; for (int i = 0; i < lines.Length; i++) { if (lines[i].StartsWith(taskId)) { found = true; if (lines[i].Contains("in progress")) { lines[i] = lines[i].Replace("in progress", "completed"); updated = true; } break; } } File.WriteAllLines(fileName, lines); Console.WriteLine($"任务ID {taskId}:找到行={found},更新状态={updated}"); } finally { mutex.ReleaseMutex(); } }
- Mutex使用验证:确保没有其他线程持有Mutex未释放,不过当前代码中
finally块保证了释放,问题概率较低,但如果是多客户端场景,需确保每个操作都正确释放Mutex。
内容的提问来源于stack exchange,提问作者Luís Briga
相关产品推荐
相关产品推荐

