首页 > 解决方案 > 使用 BinaryReader/Writer 的 C# Socket 从读取器获取错误数据

问题描述

我正在为班级做一个项目,我必须创建一个套接字服务器,然后是一堆从队列中请求票的假客户端。我的一切工作都很好,但是,当我从 Socket 流中读取我的 Ticket 对象时,我只收到了第一个发送的对象。

在这一行Console.WriteLine($"ID {paramData.ClientID} got ticket {tick.TicketID} for show {tick.EventName}");中,我得到了在 for 循环期间生成的唯一客户端 ID,但是,我每次都得到 0 的票证 ID。

我有一些想法,但我不确定为什么会这样。首先,通过套接字发送第一张票,然后每次都重新使用该票,但我不确定为什么会发生这种情况,因为我在发送之前将其出列。

另一个想法是我只是在做完全错误的线程,但我不明白同一张票是如何出现在不同的线程上的。

这是执行代码的屏幕截图,其中包含 10 次购买尝试和 5 个可用门票 在此处输入图像描述

完整代码:

class Program
{
    static ConcurrentQueue<Ticket> _TicketStorage = new ConcurrentQueue<Ticket>();
    static Socket ServerSocket;

    static void Main(string[] args)
    {
        Console.WriteLine("Starting Server ...");

        // Gets the event name to apply to all the tickets
        Console.Write("Enter Name of Event: ");
        var eventName = Console.ReadLine();

        // Allows the user to set the ticket count or defaults to 500
        Console.Write("\nEnter Max Number of Tickets (Default: 500): ");
        var ticketCountTry = int.TryParse(Console.ReadLine(), out int TicketCountResult);

        // Setups up the ticket q
        Console.WriteLine("Initilizing Ticket Storage");
        for (int i = 0; i < (ticketCountTry ? TicketCountResult : 5); i++)
        {
            _TicketStorage.Enqueue(new Ticket(eventName, i));
        }

        Console.Clear();

        // Finish line for application
        Console.WriteLine("Server is ready to process requests");

        var ServerThread = Task.Factory.StartNew(()=> 
        {
            StartServerListener();
        },TaskCreationOptions.LongRunning);

        StartFakeClients();

        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }

    static void StartFakeClients()
    {
        Console.Write("Enter number of tickets to attempt to purchase (Default: 600): ");
        var attemptNumberTry = int.TryParse(Console.ReadLine(), out int attemptAmount);

        // Setup all the connections to be in the ready state
        for (int i = 0; i < (attemptNumberTry ? attemptAmount : 10); i++)
        {
            ThreadPool.QueueUserWorkItem(FakeClientWorker, new
            {
                ClientID = i
            });

            Thread.Sleep(TimeSpan.FromSeconds(2));
        }
    }

    static void FakeClientWorker(object state)
    {
        dynamic paramData = state;

        // Create a connection to the server
        var Connection = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        Connection.Connect(IPAddress.Loopback, 11000);


        using (var bReader = new BinaryReader(new NetworkStream(Connection, true), Encoding.UTF8, false))
        {
            // Create my high level reader to parse incoming data
            var dataBlock = bReader.ReadString();

            if (!string.IsNullOrEmpty(dataBlock))
            {
                Ticket tick = JsonConvert.DeserializeObject<Ticket>(dataBlock);
                Console.WriteLine($"ID {paramData.ClientID} got ticket {tick.TicketID} for show {tick.EventName}");
            }
            else
            {
                // Didn't get a ticket
                Console.WriteLine($"ID {paramData.ClientID} didn't get a ticket");
            }
        }
    }

    static void StartServerListener()
    {
        IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 11000);

        ServerSocket = new Socket(AddressFamily.InterNetwork,
        SocketType.Stream, ProtocolType.Tcp);
        ServerSocket.Bind(localEndPoint);
        ServerSocket.Listen(100);

        while (true)
        {
            var clientSocket = ServerSocket.Accept();

            // You just got a new client asking for a ticket
            // Send this connection to another thread and then continue listening for connections
            ThreadPool.QueueUserWorkItem(DoServerWork, clientSocket);
        }
    }

    static void DoServerWork(object clientSocket)
    {
        // Get the next ticket from the q
        var hasNewTickets = _TicketStorage.TryDequeue(out Ticket EventTicket);

        // Start a writer
        var n = new NetworkStream((Socket)clientSocket, false);
        using (var bWriter = new BinaryWriter(new NetworkStream((Socket)clientSocket, true), Encoding.UTF8, false))
        {
            // I have my writer now I need to send this data
            bWriter.Write((hasNewTickets ? JsonConvert.SerializeObject(EventTicket) : String.Empty));
        } // Dispose of the advanced network stream, Dispose of the binary writer, close the connection
    }
}

/// <summary>
/// A structure for storing information about an event
/// </summary>
public class Ticket
{
    /// <summary>
    /// Unique identifer for the ticket 
    /// </summary>
    public readonly int TicketID;

    /// <summary>
    /// Event name this ticket represents
    /// </summary>
    public readonly String EventName;

    public Ticket(String EventName, int ID)
    {
        this.EventName = EventName;
        TicketID = ID;
    }
}

标签: c#multithreadingsockets

解决方案


我相信,这不是“BinaryReader/Writer 问题”。您正在使用JsonConvert(我认为来自Json.Net)进行对象的序列化和反序列化。我想如果你调试你的项目,你会发现dataBlock传递给JsonConvert.DeserializeObject<Ticket>()方法就好了。

真正的原因是您的Ticket类没有默认构造函数。在这种情况下,JsonConvert将尝试将构造函数参数名称与 JSON 中的值匹配。这就是为什么你得到正确的EventName,但默认TicketID(0)。尝试将您的构造函数更改为:

public Ticket(String EventName, int TicketID)
{
    this.EventName = EventName;
    this.TicketID = TicketID;
}

推荐阅读