将我的 c# 项目拆分为两个单独的项目后,一个项目崩溃了,没有任何例外。可能与不同的线程有关?

After spliting my c# project into two seperate projects, one project crashes without any exception. Might have something to do with different threads?

提问人:Ango619 提问时间:7/5/2023 最后编辑:Ango619 更新时间:7/6/2023 访问量:53

问:

我目前正在开发我的第一个 c# 项目,它是一个聊天室应用程序,采用 .net 7.0 的 c# win 表单。 用户可以创建聊天室并充当服务器,也可以直接加入另一个聊天室。 对于通信,我使用套接字类。

我开始构建一个项目,但现在要求有两个独立的项目,可以单独运行。 现在我的解决方案中有 3 个项目,一个是组合项目,另外两个是客户端和服务器的单独项目。 组合解决方案按预期工作,服务器项目也是如此,但客户端项目崩溃而不引发异常。 它是完全相同的代码,我不明白为什么一个运行良好,而另一个只是关闭。

我想我可以识别导致崩溃的代码行,或者至少这是我在按 F10 浏览调试器中的每一行代码时可以转到的最后一行代码。 当我到达这一行并按 F10 转到下一行时,应用程序关闭。

var amountBytes = await _clientSocket.ReceiveAsync(bytes);

这行代码位于 try 块内,我没有到达 catch 块,应用程序只是关闭。while 循环的第一次遍历和 ReceiveAsync() 是成功的,在 while 循环的第二轮中,它会在该行崩溃。但它只在单独的客户端项目中这样做,而不是在完全相同代码的组合项目中这样做。

代码行位于我编写的通过套接字接收消息的任务中,这是整个任务,代码行位于任务的开头。

private async Task ReceiveMessagesAsync()
        {
            Byte[] bytes = new byte[1024];
            while (true)
            {
                if (_clientSocket != null && _clientSocket.Connected)
                {
                    try
                    {
                        //Receive
                        var amountBytes = await _clientSocket.ReceiveAsync(bytes);

                        //If Server closes, we get a nullbyte
                        if (amountBytes == 0)
                        {
                            //UI
                            SetStatusToDisconnected();
                            HideOwnNameLabel();
                            DisplayNotification("Server wurde geschlossen");

                            break;
                        }

                        string decodedMessage = Encoding.UTF8.GetString(bytes, 0, amountBytes);

                        //If we receive a message
                        if (decodedMessage.StartsWith(_messageIdentifyer))
                        {
                            string message = decodedMessage.Substring(_messageIdentifyer.Length);
                            //Prepare the datamodel
                            DataModel? messageModel = JsonSerializer.Deserialize<DataModel>             (message);
  
                            if (messageModel != null)
                            {
                                DisplayMessage(messageModel);
                            }
                        }

                        //If we receive the userlist
                        if (decodedMessage.StartsWith(_listIdentifyer))
                        {
                            _usersDictionary.Clear();

                            //Convert the long string to an Array
                            string[] tempArray = decodedMessage.Split(new[] { _listIdentifyer }, StringSplitOptions.RemoveEmptyEntries);
                            string username = "";
                            int clientID = 0;
                            //we go through the loop with i +=2, since two following array item represent a single user.
                            for (int i = 0; i < tempArray.Length; i += 2)
                            {
                                clientID = Convert.ToInt32(tempArray[i]);
                                username = tempArray[i + 1];
                                //Dont add myself to the userlist
                                if (clientID != _personalId)
                                {
                                    _usersDictionary.Add(clientID, username);
                                }
                            }
                            //UI
                            DisplayOnlineClients();
                            PopulateListBox();
                        }
                    }
                    catch (Exception ex)
                    {

                        Console.WriteLine($"Client [{_personalId}]" + ex.Message);
                        //UI
                        DisplayNotification("Verbindung zum Server verloren");
                        HideOwnNameLabel();
                        SetStatusToDisconnected();
                        break;
                    }
                }
            }
        }

我是初学者,所以让我解释一下我的思维过程: 我使用了一个任务,因为我不希望 UI 在我们等待消息时冻结。任务将建议线程池使用任何线程 is whishes 工作,直到它命中 await,并在 await 结果出现后使用任何线程 is whishes 恢复。如果我不使用任务,我的应用程序将在我等待消息时冻结。

我绝对不确定,但我脑海中的某些东西告诉我,这可能是在不同的线程中发生的未经处理的异常,如果这有意义的话?

我试图用这个异常处理程序捕获任何内容:

static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
        {
            Exception exception = (Exception)e.ExceptionObject;

            // Log or handle the exception here
            Console.WriteLine("Unhandled Exception:");
            Console.WriteLine(exception.ToString());

        }

但这无济于事,应用程序只是关闭,控制台中没有错误消息。

我发现的另一件事: 我第一次上线时,我们谈论它成功地接收了数据。 紧接着,套接字的 connected 属性更改为 false。 我确实读过这方面的内容,从我所读到的内容来看,连接属性表示套接字的最后状态,并且是不可识别的。这让我感到困惑,因为我刚刚成功收到了一些东西,然后它变成了假的。var amountBytes = await _clientSocket.ReceiveAsync(bytes);

我已经在这个问题上坐了好几天了,我在任何地方都找不到解决方案,因为我的问题似乎很特别。

我的肚子告诉我它与多个线程有关,但这可能是错误的,我是初学者......

无论是什么,我都会不胜感激。 提前致谢。

编辑:我还尝试在任务开始时删除if(socket.connected)条件,但没有做任何事情。

编辑2:一位社区成员要求提供调用ReceiveMessageAsync()的方法,所以我在这里添加它:

public async Task ConnectAsync(string ipadress)
    {
        _clientForm.NotificationTextbox.Clear();

        try
        {
            //Create endpoint and socket
            _clientEndpoint = new IPEndPoint(IPAddress.Parse(ipadress), _port);
            _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //Connect
            await _clientSocket.ConnectAsync(_clientEndpoint!);

            byte[] tempbytes = new byte[1024];

            //Wait for ID from Server
            var receivedData = await _clientSocket.ReceiveAsync(tempbytes);

            string messageString = Encoding.UTF8.GetString(tempbytes, 0, receivedData);

            //Check if receivedData is indeed the ID
            if (messageString.StartsWith(_IDIdentifyer))
            {

                string[] parts = messageString.Split(new string[] { _IDIdentifyer }, StringSplitOptions.RemoveEmptyEntries);
                _personalId = Convert.ToInt32(parts[0]);
                Console.WriteLine("Client hat ID erhalten:" + _personalId);

                //Send own Nickname to the Server                    
                tempbytes = Encoding.UTF8.GetBytes(_nicknameIdentifyer + _nickname!);
                await _clientSocket.SendAsync(tempbytes);

                //UI
                SetStatusToConnected();
                ConnectButtonLogic();
                DisplayOwnName();

                //Connection established now, received messages get handled in Task ReceiveMessages
                _ = ReceiveMessagesAsync();
            }
            else
            {
                Console.WriteLine("Failed to receive ID");
                //UI
                DisplayNotification("Konnte ID nicht beziehen, versuchen Sie es erneut.");
                SetStatusToDisconnected();
                HideOwnNameLabel();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            //UI
            SetStatusToDisconnected();
            HideOwnNameLabel();
        }
    }

编辑:我找到了解决方案,这是其他东西!

我使用的是 win 表单,另一个表单(这是主表单)称为当前表单,当新表单成功打开时,我的代码由 load 方法自动执行。 我在以前的表单上使用,所以我的新表单在很短的时间内就被关闭了,我认为我的代码导致了这种情况。 我把这条线改成了,现在它工作了。我想如果我将其改回并设置应用程序不应在主窗体关闭时关闭,而是在最后一个窗体关闭时关闭的设置,这是明智的。this.Close()this.Hide()this.Close()

.NET WinForms 套接字 异步 崩溃

评论

0赞 majixin 7/5/2023
你尚未发布任何 UI 更新代码,但可能需要检查所有 UI 更新方法中是否都需要调用。如果尝试从未创建 UI 的线程更新 UI,则该线程将直接终止。此外,在您报告套接字断开连接后。你有没有跟踪代码,你是否以某种方式执行?var amountBytes = await _clientSocket.ReceiveAsync(bytes);SetStatusToDisconnected();
0赞 Ango619 7/5/2023
SetStatusToDisconnected 只是 UI 内容,因此用户知道他不再连接。我有一个击倒并关闭套接字的方法,但是该方法没有被调用,我什至试图注释掉该代码,所以我确信它永远不会被调用,但它没有帮助。
0赞 Ango619 7/5/2023
是的,我的 UI 更新方法都被调用了。我的意思是,我可以处理抛给我的异常,但应用程序只是关闭,而 Visual Studio 没有向我显示任何错误或异常。
0赞 Ango619 7/5/2023
应用程序以退出代码 0 关闭也非常奇怪,据我所知,这表明一切正常。
0赞 majixin 7/5/2023
我认为展示调用的方法很重要,因为在一些情况下,你从循环中掉出这个方法。然后,您问题的查看者不知道会发生什么。异常处理是在应用程序内部记录,而不是记录到日志文件中。它还发表声明。因此,任何异常处理和 a 都将退出您的循环。根据调用方端发生的情况,确定代码是否进入退出方案。private async Task ReceiveMessagesAsync()break;whilebreak;break;while

答:

0赞 Ango619 7/6/2023 #1

我找到了解决方案,这是别的东西!

我使用的是 win 表单,另一个表单(这是主表单)称为当前表单,当新表单成功打开时,我的代码由 load 方法自动执行。我正在使用这个。Close(),所以我的新表单在很短的时间内就被关闭了,我认为我的代码导致了这种情况。我把行改成了这个。Hide(),现在它工作了。我想如果我把它改回这个是明智的。Close() 并设置应用程序不应在 mainForm 关闭时关闭的设置,而是在最后一个 Form 关闭时关闭。