class UdpClientClass { /// <summary> /// 構建用戶端 /// </summary> /// <param name="servierIpAddress">伺服器iP地址或者網域名稱</param> /// <param name="sevierPort">伺服器監聽連接埠</param> /// <param name="locadPort">本地監聽連接埠</param> /// <param name="timeOut">逾時等待時間</param> public UdpClientClass(string servierIpAddress, int sevierPort, int locadPort, int timeOut) { if (FpHelper.CheckIpAddress(ref servierIpAddress) == true) { try { ServerIPE = new IPEndPoint(IPAddress.Parse(servierIpAddress), sevierPort); UdpListenClient = new UdpClient(locadPort);//固定通訊連接埠 UdpListenClient.Client.ReceiveTimeout = 3000; const long IOC_IN = 0x80000000; const long IOC_VENDOR = 0x18000000; const long SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; byte[] optionInValue = { Convert.ToByte(false) }; byte[] optionOutValue = new byte[4]; UdpListenClient.Client.IOControl((IOControlCode)SIO_UDP_CONNRESET, optionInValue, optionOutValue); } catch (System.Exception ex) { MessageBox.Show("綁定連接埠失敗" + ex.Message.ToString()); } } } /// <summary> /// UDP發送類,綁定了一個固定的連接埠 /// </summary> private static UdpClient UdpListenClient; public event EventHandler MessageChanged; /// <summary> /// 接收到伺服器訊息改變後觸發的事件 /// </summary> /// <param name="user"></param> public void OnMessageChanged(UdpClientClass fp) { if (MessageChanged != null) { MessageChanged(fp, null); } } /// <summary> /// 伺服器端的IP與連接埠 /// </summary> private IPEndPoint ServerIPE = null; bool IsReceiving = false; public void Send(byte[] data, int len) { int len1 = UdpListenClient.Send(data, len, ServerIPE); if (!IsReceiving) StartAndLsn(); } private Thread ClientRecThread; private void StartAndLsn() { IsReceiving = true; ClientRecThread = new Thread(new ThreadStart(RecF));//啟動新線程做接收 ClientRecThread.IsBackground = true; ClientRecThread.Start(); }//啟動並且 監聽 伺服器發來的資料 private void RecF()//接收資料做服務 { byte[] btRec = null; while (IsReceiving) { IPEndPoint remoteIPE = new IPEndPoint(IPAddress.Any, 0); btRec = UdpListenClient.Receive(ref remoteIPE);//UDP接收資料 if (btRec.Length > 0 && remoteIPE.Address == ServerIPE.Address)//只處理特定的服務端的資料 { System.Windows.Forms.MessageBox.Show("res"); } else { } } }//迴圈接收資料 }遇到如下兩個問題
1.UdpListenClient.Receive接受資料位元阻塞模式,一直等待對方的返回,網上詢問這個問題的很多,解答的卻很多答非所問,仔細查看了UdpClient 類發現裡面有一個Socket熟悉,
關於通訊的底層問題都通過該熟悉來設定,該熟悉又設定發送或者接受逾時的設定,如
UdpListenClient.Client.ReceiveTimeout = 3000;//設定逾時接收返回
2.udp通訊發現有時會產生遠程主機強迫關閉了一個現有的串連的錯誤網上尋找了很多,終於看到一篇
在公司一項目的UDPMessage Service開發中時不時的會遇到這樣一個問題:在UDP通訊過程中,如果用戶端中途斷開,伺服器會收到一個SocketException,錯誤ID為10054,描述是“遠程主機強迫關閉了一個現有的串連”,緊接著的事就可怕了,UDP服務終止監聽,所有用戶端都受到了影響。也就是說一個用戶端引起的異常導致了整個系統的崩潰。這個問題可是太嚴重了。
地球人都知道,UDP是不需連線的,怎麼會出現這個異常呢?百度了一圈,發現有這個問題的現象還不少,可就是沒有一個有效回複。再GOOGLE一圈,有點眉目了。找到了一個微軟的解釋和一個DOTNET的解決方案:
微軟的解釋:http://support.microsoft.com/kb/263823
DOTNET的處理方法:http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic1887.aspx
不過處理方法似乎對參數的設定不太正確:byte[] optionInValue = { Convert.ToByte(true) };byte[] optionOutValue;
按照這樣設定還是會拋出該異常。首先,根據微軟的解釋,optionInValue 傳入的應該是false,而不是true;其次,根據微軟的解釋,optionOutValue應該是一個DWORD值,不應不賦值,或設為null。
根據以上兩點,將以上兩句改為:byte[] optionInValue = { Convert.ToByte(false) };byte[] optionOutValue = new byte[4];
經過測試,類比500個使用者進行登入、收發訊息、登出、異常退出、再串連,均沒有再拋出該異常。服務表現穩定。