问题描述
开始我为了接收多个端口收到的UDP包数据,我创建了多个线程,但当UDP包发送很快时,接收就会丢包,后来我在多个线程中用了异步的方法,如下代码所示,多线程执行StartListening()方法,在StartListening()方法中执行了ReadCallback(IAsyncResultar),可是我发现,在我跟踪到ReadCallback(IAsyncResultar)中时,发现只有一个ReadCallback线程(从vs2005开发环境中的线程监视中看到的),难道多个线程执行StartListening()方法,并不能产生多个ReadCallback线程吗?请各位高手指点,急待解决!!usingSystem;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.Text;usingSystem.Threading;usingSystem.Data;usingSystem.Data.SqlClient;//StateobjectforreadingclientdataasynchronouslypublicclassStateObject{//Clientsocket.publicSocketworkSocket=null;//Sizeofreceivebuffer.publicconstintBufferSize=255;//Receivebuffer.publicbyte[]buffer=newbyte[BufferSize];//Receiveddatastring.publicStringBuildersb=newStringBuilder();}publicclassAsynchronousSocketListener{//Incomingdatafromclient.publicstaticstringdata=null;//Threadsignal.publicstaticManualResetEventallDone=newManualResetEvent(false);publicstaticintCount=1;publicAsynchronousSocketListener(){}publicstaticvoidStartListening(){byte[]bytes=newByte[255];IPHostEntryipHostInfo=Dns.Resolve(Dns.GetHostName());IPAddressipAddress=ipHostInfo.AddressList[0];IPEndPointlocalEndPoint=newIPEndPoint(ipAddress,2020);Socketlistener=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);try{listener.Bind(localEndPoint);IPEndPointsender=newIPEndPoint(IPAddress.Any,0);EndPointtempRemoteEP=(EndPoint)sender;try{while(true){allDone.Reset();StateObjectso2=newStateObject();so2.workSocket=listener;listener.BeginReceiveFrom(so2.buffer,0,StateObject.BufferSize,0,reftempRemoteEP,newAsyncCallback(ReadCallback),so2);allDone.WaitOne();}}catch(Exceptione){Console.WriteLine(e.ToString());}}catch(Exceptione){Console.WriteLine(e.ToString());}Console.WriteLine("nPressENTERtocontinue...");Console.Read();}publicstaticvoidReadCallback(IAsyncResultar){allDone.Set();StateObjectso=(StateObject)ar.AsyncState;Sockets=so.workSocket;so.sb.Remove(0,so.sb.Length);IPEndPointsender=newIPEndPoint(IPAddress.Any,0);EndPointtempRemoteEP=(EndPoint)sender;intread=s.EndReceiveFrom(ar,reftempRemoteEP);if(read>0){//so.sb.Append(Encoding.ASCII.GetString(so.buffer,0,read));//so.sb.Append(Encoding.Default.GetString(so.buffer,0,read));for(inti=0;i<read;i++){so.sb.Append(string.Format("{0:X2}",so.buffer[i]));}}else{}if(so.sb.Length>1){//Allthedatahasbeenread,sodisplaysittotheconsole.stringstrContent;strContent=so.sb.ToString();Count=Count+1;Console.WriteLine(String.Format("Count{0}Read{1}bytefromsocket"+"data={2}",Count,strContent.Length,strContent));if(strContent.IndexOf("454F46")>=0)//挂断指令{s.Close();//问题出现了,一挂断就出错}}}privatestaticvoidSend(Sockethandler,EndPointremoteEP,Stringdata){//ConvertthestringdatatobytedatausingASCIIencoding.byte[]byteData=Encoding.ASCII.GetBytes(data);handler.BeginSendTo(byteData,0,byteData.Length,0,remoteEP,newAsyncCallback(SendCallback),handler);}privatestaticvoidSendCallback(IAsyncResultar){try{Sockethandler=(Socket)ar.AsyncState;intbytesSent=handler.EndSend(ar);}catch(Exceptione){Console.WriteLine(e.ToString());}}publicstaticintMain(String[]args){StartListening();return0;}}
解决方案
解决方案二:
当然不会有多个ReadCallBack(),这个回调只在接收成功后才调用,并不是在接收的时候调用。所以你只能看到一个ReadCallBack()。因为其它的接收还堵在那里呢。
解决方案三:
代码比较多,没仔细看。其实你的代码并没有太大的问题,之所以出现丢包是因为你同步信号释放的时机不对。ReadCallback(IAsyncResultar)回调方法中你第一时间就释放了allDone,这虽然能够第一时间给接收的线程一个信号让其马上开始下一次循环,但你没注意到,其实这个时候listener还是停留在上次的阻塞状态,在listener.EndReceiveFrom(ar,reftempRemoteEP)方法没执行之前,listener无法接受新的数据,所以,数据发送快了时候自然会丢包。解决办法很简单,allDone.Set()放到intread=s.EndReceiveFrom(ar,reftempRemoteEP)后面执行即可。其实你这个程序没必要异步接收的,反正你的主线程都是经常性地阻塞去监听的,把listener.BeginReceiveFrom改为同步接收:listener.ReceiveFrom然后在开一条新线程处理接收的数据岂不更简单直观?这样你就不需要引入ManualResetEvent手动同步类了。最后需要指明的是,你这样的异步方法任何时候都只是一条ReadCallback线程,vs2005的显示是正确的,异步的本质就是开一条新的县城去同步接收而已。
解决方案四:
s.close()出现的问题应该是系统会强制抛出一个异常,大致的信息应该是:“远程主机强制关闭了一个现有的连接”之类的。。。把s.close()放到try..catch语句里面就可以了。