在套接字、线程和互斥体项目中读取和修改 csv 文件时遇到困难

问题描述 投票:0回答:1

我叫 Luís,目前正在开发一个项目,其中包括实施客户端/服务器系统来管理 ServiMoto 公司提供的移动服务。服务包括树木和花园检查、消防干预、邮政递送和披萨递送。该系统允许客户端(自行车)连接到服务器来接收任务、更新任务状态和请求新任务。服务器管理这些请求,将任务分配给客户端,并将分配的任务和客户端的记录保存在 CSV 文件中。该项目是使用套接字和定义的通信协议在 C# 中实现的。

目前我的两个主要代码是:

类服务器:

// 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;
    }
}

问题是,当我想使用 UpdateCompletedTask 函数声明任务已完成时,csv 文件不会更改,并且始终保持不变。有人可以帮助我吗?

我更改了函数的代码几次,因为我认为问题出在 UpdateCompletedTask 函数中,但仍然没有任何改变。

c# .net sockets tcp distributed-system
1个回答
0
投票

我的猜测是

lines[i].StartsWith(taskId)
永远不会返回 true。也许是因为你的 ID 包含在标签中,即
<42>
?使用调试器应该很容易找到它。

  1. UpdateCompletedTask
    中设置断点。到底达到了吗?
  2. taskId
    lines
    看起来像预期的那样吗?
  3. 达到
    lines[i].Replace("in progress", "completed")
    了吗?

虽然您可能可以通过足够的努力来完成这样的工作,但您的代码中存在很多问题。特别是您对字符串的依赖,如果使用正确的类型,这可以轻松隐藏编译器可能捕获的问题。

正如其他人提到的,有更好的方法来做事:

网络通讯

使用网络流进行通信是困难,请参阅Stephen Clearys TCP FAQ了解您需要做的所有事情。特别是关于消息框架的部分。

但幸运的是,您不需要自己这样做。有各种各样的高级协议和使用这些协议的库。这些往往更容易使用,因为您可以发送消息对象,而不仅仅是字节流。基于请求-响应的协议(例如 http 或 gRPC)听起来最适合您的用例。

存储

虽然纯文本/csv 文件有时可以用作 erzats 数据库,但从长远来看,使用真实的数据库可能会节省您的时间和精力。

有免费的全功能数据库,如 postgres,或者流程数据库中更简单的数据库,如 SQLite。无论哪种情况,通常都会使用对象关系映射器 (ORM) 来为您在数据库行和对象之间进行转换。 实体框架(EF)可能是最受欢迎的选择。

我个人会将此作为一个 ASP.Net webapi 项目(即 http),并通过 EF 访问 SQLite 后端。应该有大量关于如何设置的教程和其他资源。

© www.soinside.com 2019 - 2024. All rights reserved.