5

在多线程环境下使用udpclient进行异步UDP接收出现“访问已注销对象”的异常,怎么办?

最近打算使用C#写一个仿飞秋的局域网即时通信工具,但是在使用多线程异步收发UDP数据时老是出现“访问对象已注销”的异常,查了好久都没有找到原因,只能求助于网友啦。
完整代码可以在我的github上下载:WAW
说说具体情况吧:
1.为了进行udp数据的异步接收,我写了一个异步接收类AsyncUDPclient。

  public class AsyncUDPclient
    {
        private IPEndPoint recEP, sendEP;
        private UdpClient udpReceive, udpSend;
        private UdpState udpReceiveState, udpSendState;

        //定义消息接收事件
        public event MessageReceivedEventHandle MessageReceived;

        // 异步状态同步
        //private ManualResetEvent sendDone = new ManualResetEvent(false);
        //private ManualResetEvent receiveDone = new ManualResetEvent(false);

        public AsyncUDPclient()
        {
            recEP = new IPEndPoint(IPAddress.Any, InfoSet.IpPort.Port);//允许从任意远程主机终节点接收数据
            sendEP = new IPEndPoint(IPAddress.Any, InfoSet.IpPort.Port);//允许向任意远程主机终节点发送数据,发送和接收使用同一个端口
            udpReceive = new UdpClient(recEP);
            udpSend = new UdpClient();

            udpReceiveState = new UdpState();
            udpReceiveState.udpClient = udpReceive;
            udpReceiveState.ipEndPoint = recEP;

            udpSendState = new UdpState();
            udpSendState.udpClient = udpSend;
            udpSendState.ipEndPoint = sendEP;
        }

        public void ReceiveMsg()
        {
            udpReceive.BeginReceive(new AsyncCallback(ReceiveCallback), udpReceiveState);
            //receiveDone.WaitOne();
        }

        private void ReceiveCallback(IAsyncResult iar)
        {
            UdpState s = iar.AsyncState as UdpState;
            if(iar.IsCompleted)
            {
                IPEndPoint ep = s.ipEndPoint;
                UdpClient u = s.udpClient;
                Byte[] receiveBytes = u.EndReceive(iar, ref ep);

                //调用消息接收事件处理方法
                MessageEventArgs args = new MessageEventArgs();
                args.remoteIP = ep;
                args.buffer = receiveBytes;
                if (MessageReceived != null)
                {
                    MessageReceived(this, args);
                }
                //receiveDone.Set();

                u.BeginReceive(new AsyncCallback(ReceiveCallback), s);//此处需要验证s.ipEndPoint的值是否已经变化?
            }
        }

        public void SendMsg(byte[] datagram,int bytes,IPEndPoint sendEP)
        {
            udpSendState.ipEndPoint = sendEP;
            udpSend.BeginSend(datagram, bytes,sendEP, new AsyncCallback(SendCallback), udpSendState);
            Thread.Sleep(100);
        }

        private void SendCallback(IAsyncResult iar)
        {
            UdpState s = iar.AsyncState as UdpState;
            s.udpClient.EndSend(iar);
            //sendDone.Set();
        }

        public void StopClient()
        {
            udpSend.Close();
            udpReceive.Close();
            Thread.Sleep(100);
        }
    }

    /// <summary>
    /// 消息接收事件委托
    /// </summary>
    /// <param name="Sender"></param>
    /// <param name="e"></param>
    public delegate void MessageReceivedEventHandle(object Sender, MessageEventArgs e);
    public class MessageEventArgs : EventArgs
    {
        public byte[] buffer;
        public IPEndPoint remoteIP;

        public MessageEventArgs() : base()
        {
            remoteIP = new IPEndPoint(IPAddress.Any, 0);
        }
    }
    public class UdpState
    {
        public UdpClient udpClient;
        public IPEndPoint ipEndPoint;
    }

2.当程序启动之后,在程序主窗口类frmMain.cs里开了一个新线程监听指定端口,用于接收UDP数据,然后主窗口程序广播上线消息。

     public partial class frmMain : Form
    {
        //用于整个程序收发数据的对象
        public AsyncUDPclient AsyncUDP;

        private Thread udplistenthread;
        private DataReceive startreceive;

        public frmMain()
        {
            InitializeComponent();
            _userstate = wawState.SignIn;
            Rectangle rec = Screen.GetWorkingArea(this);
            this.ClientSize = new Size(234, rec.Height - 100);
            this.Location = new Point((int)(rec.Width * 0.8), (int)(rec.Height * 0.05));
            this.MaximumSize = new Size(260, rec.Height);
            this.MinimumSize = new Size(234, 100);
            this.chTag.Width = 0;
            this.chUser.Width = (int)(this.lvwUsers.Width * 0.3);
            this.chIP.Width = (int)(this.lvwUsers.Width * 0.4);
            this.chHostname.Width = this.lvwUsers.Width - this.chUser.Width - this.chHostname.Width;

            AsyncUDP = new AsyncUDPclient();//收发数据对象初始化
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            //开启监听线程,新线程执行StartListenUdp方法
            startreceive = new DataReceive(lvwUsers, lblUserCount,AsyncUDP);
            udplistenthread = new Thread(new ThreadStart(startreceive.StartListenUdp));
            udplistenthread.IsBackground = true;
            udplistenthread.Start();

            MsgBoardCast boardcast = new MsgBoardCast(AsyncUDP);
            //boardcast.BoardCast(wawCMD.WAW_BC_SIGNIN);

            int i = 0;
            foreach(IPAddress addr in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
            {
                if(addr.AddressFamily==AddressFamily.InterNetwork)
                {
                    cmbIpList.Items.Add(addr.ToString());
                    if (addr.ToString()==InfoSet.IpPort.Address.ToString())
                    {
                        cmbIpList.SelectedIndex = i;
                    }
                    i++;                    
                }
            }
        }
        }

StartListenUdp方法如下(就是调用异步接收类AsyncUDPclient的ReceiveMsg方法开始异步接收):

        public void StartListenUdp()
        {          
            AsyncClient.MessageReceived += new MessageReceivedEventHandle(DataConfig);
            AsyncClient.ReceiveMsg();
        }

但是在运行过程中一直很出现异常,貌似是说调用udpclient对象是该对象已经注销,但我检查了很久都没有查处问题,这个问题一直卡了我整整两周时间,实在是没办法了。
以下是调试报错截图:
图片说明

查看全部
shangjindetudou
3-_-3
2018/04/13 15:28
  • udp
  • 即时通信
  • 多线程
  • 异常
  • 点赞
  • 收藏
  • 回答
    私信

3个回复