我叫 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 函数中,但仍然没有任何改变。
我的猜测是
lines[i].StartsWith(taskId)
永远不会返回 true。也许是因为你的 ID 包含在标签中,即 <42>
?使用调试器应该很容易找到它。
UpdateCompletedTask
中设置断点。到底达到了吗?taskId
和 lines
看起来像预期的那样吗?lines[i].Replace("in progress", "completed")
了吗?虽然您可能可以通过足够的努力来完成这样的工作,但您的代码中存在很多问题。特别是您对字符串的依赖,如果使用正确的类型,这可以轻松隐藏编译器可能捕获的问题。
正如其他人提到的,有更好的方法来做事:
使用网络流进行通信是困难,请参阅Stephen Clearys TCP FAQ了解您需要做的所有事情。特别是关于消息框架的部分。
但幸运的是,您不需要自己这样做。有各种各样的高级协议和使用这些协议的库。这些往往更容易使用,因为您可以发送消息或对象,而不仅仅是字节流。基于请求-响应的协议(例如 http 或 gRPC)听起来最适合您的用例。
虽然纯文本/csv 文件有时可以用作 erzats 数据库,但从长远来看,使用真实的数据库可能会节省您的时间和精力。
有免费的全功能数据库,如 postgres,或者流程数据库中更简单的数据库,如 SQLite。无论哪种情况,通常都会使用对象关系映射器 (ORM) 来为您在数据库行和对象之间进行转换。 实体框架(EF)可能是最受欢迎的选择。
我个人会将此作为一个 ASP.Net webapi 项目(即 http),并通过 EF 访问 SQLite 后端。应该有大量关于如何设置的教程和其他资源。