【问题标题】:How to detect when data is received by udpclient?如何检测udpclient何时收到数据?
【发布时间】:2014-10-28 08:26:18
【问题描述】:

我目前正在做一个家庭使用的小程序(主要是因为我每次想告诉爸爸一些事情时都懒得跑下楼梯)。

现在关于我的问题:

一旦 udpclient 收到来自对等方的消息,如何让此代码调用我的 ReceiveText 方法? (注意:只有 2 台电脑运行这个,两个 ip 都被硬编码到里面)


感谢 Ahmed Ilyas,​​上述问题同时得到了解答,但是我遇到了一个问题,他发布的链接中显示了该问题:

消息就像这样发送得很好:

sendMe.Connect("192.168.0.121", 60500);
sendBytes = Encoding.ASCII.GetBytes(txtSend.Text);
sendMe.Send(sendBytes, sendBytes.Length);
sendMe.Close();

问题似乎出在接收端,从我从异常中读取的内容来看,当我尝试实际使用发送的数据时,它似乎正在发生,因此我可以为我的富文本框正确格式化等.

这是接收和解析传入数据的代码:

UdpClient sendMe = new UdpClient(60500);
UdpClient illReceive = new UdpClient(60501);
Byte[] sendBytes;
string returnData;
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);

...
try
{
        illReceive.BeginReceive(new AsyncCallback(RecieveText), null);
    }
    catch (Exception e)
    {
        MessageBox.Show(e.ToString());
}
...

void ReceiveText(IAsyncResult res)
{
    try
    {
        System.Reflection.Assembly a = _
System.Reflection.Assembly.GetExecutingAssembly();
        System.IO.Stream s = a.GetManifestResourceStream("IPSend.ns.wav");
        SoundPlayer player = new SoundPlayer(s);
        player.Play();

        byte[] received = illReceive.EndReceive(res, ref RemoteIpEndPoint);
        returnData = Encoding.UTF8.GetString(received);
        TextRange tr = new _
TextRange(rtbPast.Document.ContentEnd, rtbPast.Document.ContentEnd);
        tr.Text = String.Format("{0} - Yorrick: {1} {2}", _
DateTime.Now.ToShortTimeString(), returnData, Environment.NewLine);
        tr.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Red);
        tr.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Black);
        illReceive.BeginReceive(new AsyncCallback(RecieveText), null);
    }
    catch (Exception e)
    {
        MessageBox.Show(e.ToString());
    }
}

PS:我现在正在使用 MessageBox.Show,只是为了让我可以清楚地查看抛出的异常,据我了解,这似乎是 ReceiveText 方法中的某个问题。

抛出的异常: (第一行的翻译:“调用线程无法打开这个对象,因为它由另一个线程拥有。”)


尝试 C.Evenhuis 的代码后出现新异常(为 WPF 更改后): 而这个异常背后的代码:

void ReceiveText(IAsyncResult res)
{
    try
    {
        byte[] received = illReceive.EndReceive(res, ref RemoteIpEndPoint);
        returnData = Encoding.UTF8.GetString(received);

        InformUser(returnData);

        illReceive.BeginReceive(new AsyncCallback(ReceiveText), null);
    }
    catch (Exception e)
    {
        MessageBox.Show(e.ToString());
    }
}

void InformUser(string data)
{
    // InvokeRequired tells you you're not on the correct thread to update the _
//rtbPast
    if (rtbPast.Dispatcher.CheckAccess())
    {
        // Call InformUser(data) again, but on the correct thread
        rtbPast.Dispatcher.Invoke(new Action<string>(InformUser), data);

        // We're done for this thread
        return;
    }

    System.Reflection.Assembly a = _
System.Reflection.Assembly.GetExecutingAssembly();
    System.IO.Stream s = a.GetManifestResourceStream("IPSend.ns.wav");
    SoundPlayer player = new SoundPlayer(s);
    player.Play();

    TextRange tr = new TextRange(rtbPast.Document.ContentEnd, _
rtbPast.Document.ContentEnd);
    tr.Text = String.Format("{0} - Pa: {1} {2}", _
DateTime.Now.ToShortTimeString(), data, Environment.NewLine);
    tr.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Red);
    tr.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Black);
}

PS:如果您需要查看我的整个代码,我已将其发布在 Pastebin 上,主要是因为实际发布在这里有点太长了:Entire code on Pastebin

【问题讨论】:

  • 我对套接字编程的经验并不多(只是用Java做了一个简单的),但经过快速搜索,我发现了这个stackoverflow.com/questions/221783/…这意味着你需要2个UdpClients,一个用于发送和接收。
  • 我明白了,我会在那时添加,谢谢 :) 现在我只需要一种方法来检测远程 pc 何时发回消息,以便它可以触发 RecieveText() 方法
  • 用新代码编辑了原始问题,感谢@Ahmedilyas :)
  • 您两次提到抛出异常。但是您没有一次说过异常是什么或异常的堆栈跟踪是什么。就您发布的代码而言,我没有注意到任何明显的致命问题。但是您应该使用与接收文本相同的编码来发送文本。如果您希望在 I/O 完成时调用名为 ReceiveText 的方法,则应该在调用 BeginReceive() 方法时使用该名称,而不是 RecieveText。 (即,如果您实际上发布了您正在使用的代码,那么您有一个不同的、拼写错误的接收方法)。

标签: c# wpf methods call udpclient


【解决方案1】:

这个问题是因为像 ReceiveText 这样的 AsyncCallback 方法通常在与“主”线程不同的线程中运行。控件只能从创建它们的线程访问。

要解决这个问题,您需要拆分“接收”部分:

    void ReceiveText(IAsyncResult res)
    {
        try
        {
            byte[] received = illReceive.EndReceive(res, ref RemoteIpEndPoint);
            returnData = Encoding.UTF8.GetString(received);

            InformUser(returnData);

            illReceive.BeginReceive(new AsyncCallback(RecieveText), null);
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString());
        }
    }

来自“显示”部分:

    void InformUser(string data)
    {
        // InvokeRequired tells you you're not on the correct thread to update the rtbPast
        if (rtbPast.InvokeRequired)
        {
            // Call InformUser(data) again, but on the correct thread
            rtbPast.Invoke(new Action<string>(InformUser), data);

            // We're done for this thread
            return;
        }

        System.Reflection.Assembly a = System.Reflection.Assembly.GetExecutingAssembly();
        System.IO.Stream s = a.GetManifestResourceStream("IPSend.ns.wav");
        SoundPlayer player = new SoundPlayer(s);
        player.Play();

        TextRange tr = new TextRange(rtbPast.Document.ContentEnd, rtbPast.Document.ContentEnd);
        tr.Text = String.Format("{0} - Yorrick: {1} {2}", DateTime.Now.ToShortTimeString(), data, Environment.NewLine);
        tr.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Red);
        tr.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Black);
    }

【讨论】:

  • 您错过了我使用的是 WPF,而不是 winforms 的事实;) .InvokeRequired 和 .Invoke 不被 WPF 中的控件使用,而是由 .Dispatcher.CheckAccess().Dispatcher.Invoke 管理但是,在对您的代码进行了这 2 次小调整之后,我仍然遇到与以前非常相似的错误,我刚刚在我的 OP 中发布了新异常的图像。
  • @Yorrick 对不起,完全错过了!我看到“rtbPast”,立即假定 WinForms RichTextBox 控件(即使 TextRange 在 WinForms 中不存在)。
  • @Yorrick 我不知道足够多的 WPF 来回答这个问题;也许您还应该发布更新的代码(使用Dispatcher 的代码),这样我就可以删除这个答案,其他人可以尝试解决这个问题。
  • 完成,除了.InvokeRequired.Invoke 的语法不同之外,代码几乎相同。
【解决方案2】:

Yorrick,您在 InformUser() 中的“检查访问”逻辑被颠倒了。如果当前线程与调度程序关联,则 CheckAccess() 返回 true。仅当 CheckAccess() 返回 false 时才应调用 Invoke()。当前发生的情况是您正在调用 InformUser(),CheckAccess() 返回 false,并且执行落入之前给您带来麻烦的相同代码,因为您仍在从错误的线程访问控件。

你需要的是这个(否定检查):

if (!rtbPast.Dispatcher.CheckAccess()) // Invoke only when CheckAccess returns false
{
    // Call InformUser(data) again, but on the correct thread
    rtbPast.Dispatcher.Invoke(new Action<string>(InformUser), data);

    // We're done for this thread
    return;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多