異世界


2012年6月26日 星期二

UDP Server (二)

UDP Server 程式碼(二)

PS:
                                                                                      
在Win7 64Bit 下執行發生問題
IPEndPoint mIPPoint = new IPEndPoint(IPAddress.Any, mPort);
mSocketServer.Bind(mIPPoint); <== 問題發生點,還未解決
訊息: {"一次只能用一個通訊端位址 (通訊協定/網路位址/連接埠)。"}

問題已經找到解決方法但原因未明確 :   @@ Port 被佔用了@@

     我將 UdpServer 在FormLoad 中實體化,並執行Socket.Bind(IPEndPoint) 造成錯誤。{"一次只能用一個通訊端位址 (通訊協定/網路位址/連接埠)。"}

     只要在 FormLoad 完成後,以Timer 或 Button.Clock 執行就不會發生。

     另,在MS Vistudio 2010 的 Debug環境中,發現 xxxxx.vshost.exe 總是在背景中被執行著,該項尚未澄清是否會影響 Socket.Bind(IPEndPoint) 。

     因為,當發生{"一次只能用一個通訊端位址 (通訊協定/網路位址/連接埠)。"} 後,我用手動更改 Port 後就可正常運作。我懷疑是 Debug環境造成。


                                                                                      
該版本,修正同一個 IP 要求不同 Port 的問題,同一個事件處裡程式可以處裡多個 Port 的服務。
主要將 UdpEventArgs 的建構式參數原用
       UdpEventArgs (Stream msg, string ip)

改成
UdpEventArgs(Stream msg, IPEndPoint ipend)

UDP Server 程式碼


 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
 
namespace SBM75e_4
{
    /// <summary>
    /// 事件委派宣告
    /// </summary>
    public delegate void UdpEventHandler(object sender, UdpEventArgs e);
 
    public class UDP_Server
    {
        /// <summary>
        /// 非同步接收資料的回呼
        /// </summary>
        private void EndRecvCallBack(IAsyncResult result)
        {
            try
            {
                /*傳回非同步物件*/
                Socket mResultSocket = result.AsyncState as Socket;
 
                /*呼叫 EndReceiveFrom 非同步接收*/
                int mRecvBytes = mResultSocket.EndReceiveFrom(result, ref mRemotePoint);
 
                /*將資料置入Stream */
                Stream mStream = BytesToStream(mRecvBuffer, mRecvBytes);
 
                /* Invoke */
                ((System.Windows.Forms.Form)mOwner).Invoke(
                    mSetUdpReceivedDlg, 
                    mStream,
                    (IPEndPoint)mRemotePoint
                  ); //.. List<byte>
 
            }
            catch (Exception ex)
            {
                //((System.Windows.Forms.Form)m_Owner).Invoke(m_SetUdpReceivedDlg, ex.Message); //??????
            }
            finally
            {
                /*重新呼叫非同步接收*/
                mSocketServer.BeginReceiveFrom(
                    mRecvBuffer, 
                    0, 
                    mRecvBuffer.Length, 
                    SocketFlags.None, 
                    ref mRemotePoint, 
                    new AsyncCallback(EndRecvCallBack), 
                    mSocketServer
                  );
            }
 
        }
 
        #region 公用程式 Bytes、Stream 轉換
        /// <summary>
        /// 將 Byte陣列 轉為 Stream
        /// </summary>
        /// <returns>Stream</returns>
        public static Stream BytesToStream(byte[] bytes)
        {
            Stream stream = new MemoryStream(bytes);
            return stream;
        }
 
        public static Stream BytesToStream(byte[] bytes, int Length)
        {
            Stream stream = new MemoryStream(bytes, 0, Length);
            return stream;
        }
        /// <summary>
        /// 將 Stream 轉為 Byte陣列
        /// </summary>
        public static byte[] StreamToBytes(Stream stream)
        {
            byte[] bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
 
            /*將 stream 指針指向開頭*/
            stream.Seek(0, SeekOrigin.Begin);
            return bytes;
        }
        #endregion
 
        /// <summary>
        /// 委派處理函式
        /// </summary>
        //private void OnUdpReceivedText(Stream msg, string ip, int port)
        private void OnUdpReceivedText(Stream msg, IPEndPoint ipend)
        {
            UdpEventArgs e = new UdpEventArgs(msg, ipend);
 
            if (UdpReceived != null) //. 當事件變數不為 null 則執行之
            {
                UdpReceived(this, e);
            }
        }
 
        /// <summary>
        /// 委派宣告
        /// </summary>
        private delegate void SetUdpReceived(Stream msg, IPEndPoint ipend);        
        
        /// <summary>
        /// 委派欄位
        /// </summary>
        private SetUdpReceived mSetUdpReceivedDlg;
        /// <summary>
        /// 本機端 Socket物件
        /// </summary>
        private Socket mSocketServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        /// <summary>
        /// 接收資料的緩衝區
        /// </summary>
        private byte[] mRecvBuffer=new byte[10240];
        /// <summary>
        /// 初始化遠端IP和 Port端點物件
        /// </summary>
        private EndPoint mRemotePoint = new IPEndPoint(IPAddress.Any, 0);
        /// <summary>
        /// 服務 Port
        /// </summary>
        private int mPort = 0;
        /// <summary>
        /// 儲存擁有者
        /// </summary>
        private object mOwner;
        /// <summary>
        /// UDP Received 事件
        /// </summary>
        public event UdpEventHandler UdpReceived; 
 
        /// <summary>
        /// 建構式
        /// </summary>
        public UDP_Server(object Owner, int APort)
        {
            mOwner = Owner;
            mPort = APort;
 
            /*初始化Sockt物件*/
            mSocketServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
 
            /*指定要以機器上所有的網路介面,開啟的 Port*/
            IPEndPoint mIPPoint = new IPEndPoint(IPAddress.Any, mPort);
 
            /*繫結 IP 和 Port 資訊*/
            mSocketServer.Bind(mIPPoint);
 
            /*設定委派處理函式*/
            mSetUdpReceivedDlg = OnUdpReceivedText;
 
            //..啟動非同步接受資料
            mSocketServer.BeginReceiveFrom(
                mRecvBuffer, 
                0, 
                mRecvBuffer.Length, 
                SocketFlags.None, 
                ref mRemotePoint, 
                new AsyncCallback(EndRecvCallBack), 
                mSocketServer
              );
        }
 
        /*建立以 UCP 為協定的Socket*/
        Socket m_SocketClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        IPAddress m_RemoteAddress = new IPAddress(0);
        public int UDP_Send(String RemoteIP, int RemotePort, byte[] TxBuffer, int ByteCount)
        {
            //("遠端IP位置 : ");
            // IPAddress m_RemoteAddress = IPAddress.Parse(RemoteIP) ;
            // if (m_RemoteAddress != null)
            if (IPAddress.TryParse(RemoteIP, out m_RemoteAddress))
            {
                /*初始化IPEndPoint物件,表示要連結的遠端IP位置和Port號*/
                EndPoint m_IPPoint = new IPEndPoint(m_RemoteAddress, RemotePort);
                try
                {
                    /*送出訊息*/
                    m_SocketClient.SendTo(TxBuffer, m_IPPoint);
                    return (0);
                }
                catch
                {
                    return (-1);
                }
            }
 
            return (-1);
 
        }
 
    }
 
    /// <summary>
    /// 定義專用事件參數類別
    /// </summary>
    public class UdpEventArgs:EventArgs
    {
        private IPEndPoint mIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
        private Stream mMSG = new MemoryStream();
 
        public UdpEventArgs(Stream msg, IPEndPoint ipend)
        {
            MSG = msg;
            mIpEndPoint.Address = ipend.Address;
            mIpEndPoint.Port = ipend.Port;
        }
 
        public IPEndPoint EndPoint
        {
            get { return mIpEndPoint; }
            private set { mIpEndPoint = value; }
        }
 
        public Stream MSG
        {
            get { return mMSG; }
            private set { mMSG = value; }
        }
    }
 
    
}


使用該 Class
 
int iPort = 5168;
UDP_Server udpADAM;

啟動UDP封包接收伺服



udpADAM = new UDP_Server(this, iPort);    // 建立物件
udpADAM.UdpReceived += onUdpReceived;    // 串接事件

事件程式可以處裡多個 Port 的服務



 

 

 

/* UDP Server 事件 */
        private void onUdpReceived(object sender, UdpEventArgs e)
        {
            Byte[] bytes = UDP_Server.StreamToBytes(e.MSG);
 
            // e.EndPoint.Address;
            switch (e.EndPoint.Port)
            { 
                case 4001:
                    break;
                case 4002:
                    break;
                case 4003:
                    break;
                case 4004:
                    break;
                default:
                    break;
            }            
          // .....
        }