基于UDP协议的Socket服务器

UDP协议服务器开发与应用
本文探讨了基于UDP协议的服务器开发,相比TCP协议,UDP协议无连接特性使得服务器实现更为简单。文章详细介绍了服务器启动、异步数据接收、数据处理及发送数据的功能,并提供了数据接口供用户自定义数据处理逻辑。同时,文中还展示了如何实现向指定客户端、广播、多播发送数据的功能。

      上篇文章介绍了自己开发的基于TCP协议的服务器,也具有一定的扩展性;既然做了TCP的,那自然就得把UDP的也研究下,因此本篇文章将探讨下基于UDP协议的服务器。

       废话少说,下面开始介绍思路,由于UDP是无连接的,所以相对于TCP协议的服务器就简单很多,不需要去监听客户端,也不需要对会话进行维护,只需要对数据进行相应的处理就可以;因此整体思路就很明显了,用异步方式来接收客户端的数据,将数据放入队列中,用一个线程组来处理队列中的数据,每次从队列中取一个数据包进行处理,对于线程组中线程数可根据实际情况设定,首先给出成员定义:

       private Socket server = null;
       private bool serverStart = false;//服务器是否启动
        private int serverPort = 3130;//服务器端口
        private int clientPort = 3131;//远程客户端端口
        private IPAddress localIp;//服务器绑定的IP地址
        private string brodcastIp;//广播IP地址

        private byte[] tempReceiveData = null;//临时数据接收器
        private EndPoint tempRemotePoint = null;//远程客户端
        private Queue<BufferData> receivedBuffer;//数据缓冲区

        private Thread[] threadDataHandler;//数据处理线程
        private int threadDataHandlerNum = 1;//数据处理线程数

        private IDataHandler iRceivedDataHandler = null;//数据处理接口

        private Mutex m_ServerMutex;  // 只能有一个服务器

       
            启动服务器后,绑定端口,启动数据处理线程组,开始异步接收数据:

      

        /// <summary>
        /// 启动服务器
        /// </summary>
        public void Start()
        {
            if (this.serverStart)
            {
                return;//服务器已经启动
            }
            this.server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            this.tempReceiveData = new byte[this.server.ReceiveBufferSize];
            IPAddress[] ip = Dns.GetHostAddresses(Dns.GetHostName());
            IPEndPoint addr = new IPEndPoint(ip[0], this.serverPort);
            this.localIp = ip[0];
            //算出广播IP地址
            string str = this.localIp.ToString();
            int len = str.LastIndexOf('.');
            str = str.Substring(0, len + 1);
            this.brodcastIp = str + "255";
            //绑定IP,端口
            this.server.Bind(addr);
            this.serverStart = true;
            this.threadDataHandler = new Thread[this.threadDataHandlerNum];
            for (int i = 0; i < this.threadDataHandlerNum; i++)
            {
                this.threadDataHandler[i] = new Thread(new ThreadStart(this.HandlerReceivedData));
                this.threadDataHandler[i].Start();
            }
            this.AsyncBeginReceive();//开始接收数据
        }

     

              异步接收数据及其回调函数:

 

       /// <summary>
        /// 异步接收数据
        /// </summary>
        private void AsyncBeginReceive()
        {
            try
            {
                if (this.serverStart)
                {
                    this.server.BeginReceiveFrom(this.tempReceiveData, 0, this.server.ReceiveBufferSize, SocketFlags.None, 
                    ref this.tempRemotePoint, this.AsyncReceiveCallback, this);
                }
            }
            catch { }
        }
        /// <summary>
        /// 异步接收数据的回调函数
        /// </summary>
        /// <param name="iar"></param>
        private void AsyncReceiveCallback(IAsyncResult iar)
        {
            try
            {
                iar.AsyncWaitHandle.Close();
                if (this.serverStart)
                {
                    AsyncBeginReceive();
                    int byteReadLen = this.server.EndReceiveFrom(iar, ref this.tempRemotePoint);
                    byte[] temp = new byte[byteReadLen];
                    Array.Copy(this.tempReceiveData, temp, byteReadLen);
                    BufferData buffer = new BufferData(this.tempRemotePoint, temp);
                    this.receivedBuffer.Enqueue(buffer);
                }
            }
            catch
            { }
        }


           异步发送数据及其回调函数:

        /// <summary>
        /// 开始异步发送数据
        /// </summary>
        /// <param name="buffer"></param>
        private void AsyncBeginSend(IPAddress ip, string data)
        {
            try
            {
                byte[] byteData = Encoding.ASCII.GetBytes(data);
                EndPoint iep = (EndPoint)new IPEndPoint(ip, this.clientPort);
                this.server.BeginSendTo(byteData, 0, byteData.Length, SocketFlags.None, iep, 
                 new AsyncCallback(this.AsyncSendCallback), this);
            }
            catch { }
        }
        /// <summary>
        /// 开始异步发送数据
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="data"></param>
        private void AsyncBeginSend(string ip, int port, string data)
        {
            try
            {
                byte[] byteData = Encoding.ASCII.GetBytes(data);
                EndPoint iep = (EndPoint)new IPEndPoint(IPAddress.Parse(ip), port);
                this.server.BeginSendTo(byteData, 0, byteData.Length, SocketFlags.None, iep, 
                 new AsyncCallback(this.AsyncSendCallback), this);
            }
            catch { }
        }
        /// <summary>
        /// 异步发送数据的回调
        /// </summary>
        /// <param name="iar"></param>
        private void AsyncSendCallback(IAsyncResult iar)
        {
            try
            {
                this.server.EndSendTo(iar);
                iar.AsyncWaitHandle.Close();
            }
            catch
            { }
        }


            缓冲区数据处理函数:

        /// <summary>
        /// 处理缓冲区中的数据
        /// </summary>
        /// <summary>
        /// 处理缓冲区中的数据
        /// </summary>
        private void HandlerReceivedData()
        {
            while (this.serverStart)
            {
                lock (this.receivedBuffer)
                {
                    if (this.receivedBuffer.Count == 0) continue;
                    BufferData buffer = this.receivedBuffer.Dequeue();
                    if (this.iRceivedDataHandler != null) this.iRceivedDataHandler.ReceivedDataHandler(buffer);
                }
            }
        }             

 

                数据处理线程组中的每个线程都会调用该函数,所以加个锁是必要的,对于数据的解析,给出了相应的数据接口,只要实现该接口即可实现其数据的处理,如向数据库存储,对数据的判断,向客户端回复等,都可以在该接口中去实现,因此具有一定的灵活性。

           数据接口很简单:

    

    public interface IDataHandler
    {
        /// <summary>
        /// 数据处理接口
        /// </summary>
        /// <param name="buffer"></param>
        void ReceivedDataHandler(BufferData buffer);
    }

  

       对于该接口,朋友们可以自己根据需要进行扩展。

      

        指定客户端发送,广播,多播的实现:

       /// <summary>
        /// 向指定客户端发送数据
        /// </summary>
        /// <param name="ip">客户端IP</param>
        /// <param name="data">数据</param>
        public void SendToClient(IPAddress ip, string data)
        {
            if (this.serverStart)
            {
                this.server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                this.AsyncBeginSend(ip, data);
            }
        }
        /// <summary>
        /// 向指定客户端发送数据
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="data"></param>
        public void SendToClient(string ip, string data)
        {
            this.SendToClient(IPAddress.Parse(ip), data);
        }
        /// <summary>
        /// 向指定IP,指定端口的客户机发送数据
        /// </summary>
        /// <param name="ip">Ip地址</param>
        /// <param name="port">端口号</param>
        /// <param name="data">数据</param>
        public void SendToClient(string ip, int port, string data)
        {
            if (this.serverStart)
            {
                this.server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                this.AsyncBeginSend(ip, port, data);
            }
        }
        /// <summary>
        /// 广播
        /// </summary>
        /// <param name="data"></param>
        public void Broadcast(string data)
        {
            this.SendToClient(this.brodcastIp, data);
        }
        /// <summary>
        /// 在指定端口上广播数据
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="port">端口号</param>
        public void Broadcast(string data,int port)
        {
            this.SendToClient(this.brodcastIp, port, data);
        }
        /// <summary>
        /// 多路广播
        /// </summary>
        /// <param name="ipAddress"></param>
        public void Multicast(string data)
        {
            if (this.serverStart)
            {
                this.server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                this.AsyncBeginSend(IPAddress.Broadcast, data);
            }
        }
        /// <summary>
        /// 指定端口进行多路广播
        /// </summary>
        /// <param name="data"></param>
        /// <param name="port"></param>
        public void Multicast(string data, int port)
        {
            if (this.serverStart)
            {
                this.server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                this.AsyncBeginSend(IPAddress.Broadcast.ToString(), port, data);
            }
        }

 

        希望以上代码会对有需要的朋友有些帮助,也希望和大家一起共同进步,呵呵,加油!得意奋斗 源代码稍候会在我的下载资源http://download.youkuaiyun.com/detail/wdx888中给出。

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值