最近打算使用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对象是该对象已经注销,但我检查了很久都没有查处问题,这个问题一直卡了我整整两周时间,实在是没办法了。
以下是调试报错截图: