C# 套接字:客户端错误处理 'a' 作为客户端的 ID

C# Socket: Client Mishandle 'a' as the Client's id

我制作的两个程序无法运行。有服务器和客户端。服务器通过给用户一个 ID(从 0 开始)来接受许多客户端。服务器根据服务器的 ID 将命令发送到特定的客户端。 (示例:200 个客户端连接到 1 个服务器。服务器选择的 ID 为“5”,因此服务器将向所有客户端发送命令,客户端将询问服务器他想在哪个 ID 上执行命令,如果它是“5”,则该客户端将执行并将数据发送到服务器)。 客户端有很多命令,但为了创建具有相同错误的最小代码,我只使用了 1 个命令 (dir)。基本上,服务器将命令发送给客户端,如果它与客户端当前 ID 和服务器当前 ID 匹配,它将处理该命令。默认情况下,服务器的当前ID为10。以下是帮助想回答的人的命令列表:

服务器命令:

客户:

using System;
using System.Speech.Synthesis;
using System.Windows.Forms;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
namespace clientControl
{
    class Program
    {
        public static string directory = @"C:\";
        public static int id = -10;
        public static Socket sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        static void Main(string[] args)
        {
            Connect();
            getSession();
            ReadResponse(sck);
        }
        static byte[] readResponseFunc()
        {
            long fileSize = 0;
            string fileSizeInString = null;
            byte[] fileSizeInByteArray = new byte[1024];
            int fileSizeLength = sck.Receive(fileSizeInByteArray);
            for (int i = 0; i < fileSizeLength; i++)
            {
                fileSizeInString = fileSizeInString + (char)fileSizeInByteArray[i];
            }
            try
            {
                fileSize = Convert.ToInt64(fileSizeInString);
            }
            catch { Console.WriteLine(fileSizeInString); }
            sck.Send(Encoding.ASCII.GetBytes("a"));

            byte[] responseUnknown = new byte[1];
            sck.Receive(responseUnknown);
            if (Encoding.ASCII.GetString(responseUnknown) == "b")
            {
                byte[] dataInByteArray = new byte[fileSize];
                int dataLength = sck.Receive(dataInByteArray);
                return dataInByteArray;
            }
            return Encoding.ASCII.GetBytes("ERROR RESPONSE FUNC");
        }
        static void getSession()
        {
            byte[] message_1 = Encoding.ASCII.GetBytes("make_id");
            sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
            byte[] responseUnknown = new byte[1];
            if (Encoding.ASCII.GetString(responseUnknown) == "a")
            {
                sck.Send(Encoding.ASCII.GetBytes("b"));
                sck.Send(message_1);
            }
            byte[] receivedID = readResponseFunc();
            id = Convert.ToInt32(Encoding.ASCII.GetString(receivedID));
        }
        static bool SocketConnected(Socket s)
        {
            bool part1 = s.Poll(1000, SelectMode.SelectRead);
            bool part2 = (s.Available == 0);
            if (part1 && part2)
                return false;
            else
                return true;
        }
        static void ReadResponse(Socket sck)
        {
            while (true)
            {

                if (SocketConnected(sck) == true)
                {
                    try
                    {
                        string response = Encoding.ASCII.GetString(readResponseFunc());

                        byte[] message_1 = Encoding.ASCII.GetBytes("get_id");
                        sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
                        byte[] responseUnknown = new byte[1];
                        if (Encoding.ASCII.GetString(responseUnknown) == "a")
                        {
                            sck.Send(Encoding.ASCII.GetBytes("b"));
                            sck.Send(message_1);
                        }
                        byte[] response_2InByteArray = readResponseFunc();
                        string response_2 = Encoding.ASCII.GetString(response_2InByteArray);
                        if (Convert.ToInt32(response_2) == id)
                        {
                            if (response == "dir")
                            {
                                string resultOfDirring = "Current Directory: " + directory + "\n\n";
                                string[] folderListingArray = Directory.GetDirectories(directory);
                                foreach (string dir in folderListingArray)
                                {
                                    string formed = "DIRECTORY: " + Path.GetFileName(dir);
                                    resultOfDirring = resultOfDirring + formed + Environment.NewLine;
                                }
                                string[] fileListingArray = Directory.GetFiles(directory);
                                foreach (var file in fileListingArray)
                                {
                                    FileInfo fileInfo = new FileInfo(file);
                                    string formed = "FILE: " + Path.GetFileName(file) + " - FILE SIZE: " + fileInfo.Length + " BYTES";
                                    resultOfDirring = resultOfDirring + formed + Environment.NewLine;
                                }
                                byte[] message_11 = Encoding.ASCII.GetBytes(resultOfDirring);
                                sck.Send(Encoding.ASCII.GetBytes(message_11.Length.ToString()));
                                byte[] responseUnknown_11 = new byte[1];
                                if (Encoding.ASCII.GetString(responseUnknown_11) == "a")
                                {
                                    sck.Send(Encoding.ASCII.GetBytes("b"));
                                    sck.Send(message_11);
                                }
                            }
                        }
                        else { }
                    }
                    catch { if (SocketConnected(sck) == false) { Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }; }
                }
                else if (SocketConnected(sck) == false) { Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }
            }
        }
        static void Connect()
        {
            while (true)
            {
                try
                {
                    sck.Connect(IPAddress.Parse("127.0.0.1"), 80);
                    break;
                }
                catch { }
            }
        }
    }
}

服务器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Threading;

namespace serverControl
{
    class Program
    {
        public static int ftpNum = 1;
        public static List<int> listOfClient = new List<int>();
        public static TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
        public static string message = null;
        public static int id = 0;
        public static int selected_id = 10;
        static void Main(string[] args)
        {
            server.Start();
            Thread startHandlingClientThread = new Thread(startHandlingClient);
            startHandlingClientThread.Start();
            while (true)
            {
                Console.Write(":> ");
                string rawmessage = Console.ReadLine();
                if (rawmessage == "list")
                {
                    Console.WriteLine("SELECTED ID: " + selected_id);
                    Console.WriteLine("List of Clients ID:");
                    for (int i = 0; i < listOfClient.Count; i++)
                    {
                        Console.WriteLine(listOfClient[i]);
                    }
                    message = rawmessage+"PREVENT_REPETITION_IN_COMMAND";
                }
                else if (rawmessage.Contains("set ")) { int wantedChangeId = Convert.ToInt32(rawmessage.Replace("set ", "")); selected_id = wantedChangeId; message = rawmessage+ "PREVENT_REPETITION_IN_COMMAND"; }
                else
                {
                    message = rawmessage;
                }
            }
        }

        static byte[] readResponseFunc(Socket sck)
        {
            long fileSize = 0;
            string fileSizeInString = null;
            byte[] fileSizeInByteArray = new byte[1024];
            int fileSizeLength = sck.Receive(fileSizeInByteArray);
            for (int i = 0; i < fileSizeLength; i++)
            {
                fileSizeInString = fileSizeInString + (char)fileSizeInByteArray[i];
            }
            fileSize = Convert.ToInt64(fileSizeInString);

            sck.Send(Encoding.ASCII.GetBytes("a"));

            byte[] responseUnknown = new byte[1];
            sck.Receive(responseUnknown);
            if (Encoding.ASCII.GetString(responseUnknown) == "b")
            {
                byte[] dataInByteArray = new byte[fileSize];
                int dataLength = sck.Receive(dataInByteArray);
                return dataInByteArray;
            }
            return Encoding.ASCII.GetBytes("ERROR RESPONSE FUNC");
        }
        static void startHandlingClient()
        {
            while (true)
            {
                handleClient(server);
            }
        }
        static void handleClient(TcpListener clientToAccept)
        {
            Socket sck = clientToAccept.AcceptSocket();
            Thread myNewThread = new Thread(() => ReadResponse(sck));
            myNewThread.Start();
        }
            static bool SocketConnected(Socket s)
            {
                bool part1 = s.Poll(1000, SelectMode.SelectRead);
                bool part2 = (s.Available == 0);
                if (part1 && part2)
                    return false;
                else
                    return true;
            }
        static void ReadResponse(Socket sck)
        {
            Thread myNewThread = new Thread(() => SendtoClient(sck));
            myNewThread.Start();
            Thread.Sleep(2000);
            while (true)
            {

                if (SocketConnected(sck) == true)
                {
                    try
                    {
                        byte[] dataInByteArray = readResponseFunc(sck);

                        string response = Encoding.ASCII.GetString(dataInByteArray);
                        Console.WriteLine("res: " + response);
                        if (response != "make_id" && response != "get_id") { Console.WriteLine(response); }
                        if (response == "make_id")
                        {
                            Console.WriteLine("Someone wants an ID");
                            byte[] message_1 = Encoding.ASCII.GetBytes(id.ToString());
                            listOfClient.Add(id);
                            // START
                            sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
                            byte[] responseUnknown = new byte[1];
                            if (Encoding.ASCII.GetString(responseUnknown) == "a")
                            {
                                sck.Send(Encoding.ASCII.GetBytes("b"));
                                sck.Send(message_1);
                            }
                            id++;
                        }
                        if (response == "get_id")
                        {
                            byte[] message_1 = Encoding.ASCII.GetBytes(selected_id.ToString());
                            sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
                            byte[] responseUnknown = new byte[1];
                            if (Encoding.ASCII.GetString(responseUnknown) == "a")
                            {
                                sck.Send(Encoding.ASCII.GetBytes("b"));
                                sck.Send(message_1);
                            }
                        }
                    }
                    catch { if (SocketConnected(sck) == false) { Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }; }
                }
                else if (SocketConnected(sck) == false) { Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }
            }
        }
        static void SendtoClient(Socket sck)
        {
            string tempmessage = null;
            while (true)
            {
                if (SocketConnected(sck) == true)
                {
                    if (tempmessage != message)
                    {
                        if (!message.Contains("PREVENT_REPETITION_IN_COMMAND"))
                        {
                            byte[] message_1 = Encoding.ASCII.GetBytes(message);
                            sck.Send(Encoding.ASCII.GetBytes(message_1.Length.ToString()));
                            byte[] responseUnknown = new byte[1];
                            if (Encoding.ASCII.GetString(responseUnknown) == "a")
                            {
                                sck.Send(Encoding.ASCII.GetBytes("b"));
                                sck.Send(message_1);
                            }
                        }
                        tempmessage = message;
                    }
                }
                else if (SocketConnected(sck) == false)
                { Console.WriteLine("Client Disconnected: " + sck.RemoteEndPoint); break; }
            }
        }
    }   
}

问题: 问题出在 GetSession 或 ReadResponseFunc 函数中。客户端认为服务器收到的他的 ID 是 'a'(它应该是一个整数)。我不想要一个建议我使用其他库或 TcpClient class

赏金:

我会悬赏给解决问题的人,没有过期时间。

你的代码逻辑很混乱。我的问题是:为什么要在服务器和客户端之间来回发送 'a' 和 'b'?是否确认已收到消息?

总之,从我刚才做的快速测试来看,问题似乎出在你的服务器的第59行:

sck.Send(Encoding.ASCII.GetBytes("a"));

这是我在测试过程中得出的结论:

  1. 服务器执行。
  2. 客户端执行。
  3. 客户端向服务器发送"make_id"的长度(客户端第51行)
  4. 客户端等待对 return 的响应。
  5. 服务器读取值并发回'a'(服务器第59行)

您可能需要花一些时间理顺您的协议,以减少混乱并更有条理。这将帮助像我这样的人,并且您更容易发现错误。

用户'Bobby'已经发现了您的问题。功劳归于他。

我进一步建议您使用较少的线程,因为线程同步在正确执行时需要一些努力:从不同线程访问的所有数据都必须通过锁来保护,这样就不会在 CPU 缓存。使用 "threading synchronisation primitives" 中的 .net Monitor 完成该作业。

关于线程本身:
服务器中应该只有一个线程。该线程从一个列表(由 Monitor 保护)中获取所有客户端,在收到连接尝试时将它们添加到该列表中。在每个客户端上,它检查传入的消息,评估它们并在需要时回复自己的消息。

客户端也只有一个线程,它会循环(不要忘记在循环中休眠,否则您将 100% 使用已用 CPU 核心),在需要时发送消息并等待回复消息发送时间。

关于消息:
我已经在对 Bobby 的回答的评论中提出了一些建议。我建议您创建一个具有 Serialize() 和 Deserialze() 的 CMessage class 来创建一个字节数组以从 CMessage 成员发送(序列化内容),反之亦然,从接收到的字节中填充成员。然后,您可以在两个程序中使用此 class,并且您将拥有共同的解决方案。