• 基于IHttpAsyncHandler的UDP收发器


        很难把UDP和Asp.net扯到一起,但是由于最近项目中需要通过网页发送控制指令到中间件,再由中间件发送到下位机的需求。所以就研究了一下是否可以通过asp.net操控UDP Socket实现数据的收发,结果证明是可以的。

    服务端代码逻辑

        在这个实现过程中,其核心组件就是IHttpAsyncHandler接口,相信大家都明白IHttpHandler是来干什么用的,那么IHttpAsyncHandler就是其异步版本,可以实现操作的异步进行。并且由于这个接口提供了事件完成回调机制,所以能够非常方便的将执行结果打印到前台来。这个接口包含了两个比较有用的方法,一个是BeginProcessRequest,代表异步操作的开始,另外一个是EndProcessRequest,代表异步操作的结束。一般在使用的时候,我们需要结合IAsyncResult接口一起使用,以便能够非常方便的实现完成回调的通知。

    下面主要来讲解一下代码逻辑。

    首先,需要继承自IHttpAsyncHandler接口,这里暂时先不提供实现:

        #region 异步执行请求
        public class AsyncSocketHandler : IHttpAsyncHandler,IRequiresSessionState
        {
            public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
            {
                //开始请求
            }
     
            public void EndProcessRequest(IAsyncResult result)
            {
                //请求结束
            }
            public bool IsReusable
            {
                get { return false; }
            }
     
            public void ProcessRequest(HttpContext context)
            { }
        }
        #endregion

    然后,我们最好能够引用IAsyncResult接口并提供其实现,以便于回传通知:

    #region 返回异步执行结果
        public class myAsyncResult : IAsyncResult
        {
            public HttpContext contex;
            public AsyncCallback cb;
            public object extraData;
            public bool isCompleted = false;
            public myAsyncResult(HttpContext contex, AsyncCallback cb, object extraData)
            {
                this.contex = contex;
                this.cb = cb;
                this.extraData = extraData;
            }
     
            public void send(string resultStr)
            {
                this.contex.Response.Write(resultStr);
            }
            public object AsyncState
            {
                get { return null; }
            }
     
            public System.Threading.WaitHandle AsyncWaitHandle
            {
                get { return null; }
            }
            public bool CompletedSynchronously
            {
                //在网络连接或者流读取中,这里需要设置为True,否则前台是不能显示接收数据的。
                get { return true; }
            }
            public bool IsCompleted
            {
                get { return isCompleted; }
            }
        }
        #endregion

    上面的实现很简单,其实就是一个带参构造,然后利用Send方法向前台打印数据。

    最后就是我们真正要运行在异步进程中的方法:

     
        #region 异步执行对象
        public static class myAsyncFunction
        {
            private static string resultResponse = string.Empty;  //获取客户端数据
            private static UdpClient udpClient;  
            private static IPEndPoint remoteEndPoint;
     
            private static myAsyncResult asyncResult;  //通知回传对象
            
            // 把一个异步的请求对象传入以便于供操作
            public static void Init(myAsyncResult result,int port)
            {
                asyncResult = result;
                if (udpClient == null)
                {
                    try
                    {
                        udpClient = new UdpClient(port);
                    }
                    catch (System.Net.Sockets.SocketException ex)
                    {
                        udpClient = null;
                        throw new Exception(ex.Message);
                    }
                }
            }
     
            //接收客户端数据并将数据打印到前台
            public static void AcceptClients()
            {
                byte[] bufferResult = udpClient.Receive(ref remoteEndPoint);
                resultResponse = System.Text.Encoding.ASCII.GetString(bufferResult);
                WriteLog("ReceivingClient: " + resultResponse);
                asyncResult.send(resultResponse);
            }
            //数据接收完毕,服务器端回送消息给客户端
            public static void ResponseClients()
            {
                string message = "Server has received your message.";
                byte[] msgBytes = Encoding.Default.GetBytes(message);
                IPEndPoint sEndPoint = new IPEndPoint(remoteEndPoint.Address, 10003);
                UdpClient sClient = new UdpClient();
                sClient.Connect(sEndPoint);
                sClient.Send(msgBytes,msgBytes.Length);
            }
     
            //写日志
            public static void WriteLog(string content)
            {
                try
                {
                    string timeStamp = DateTime.Now.ToString("yyyyMMdd");
                    string filePath = "C:\TestLog." + timeStamp + ".txt";
                    string contentAll = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + ":" + content + Environment.NewLine;
                    byte[] b = Encoding.Default.GetBytes(contentAll);
                    using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
                    {
                        fs.Write(b, 0, b.Length);
                        fs.Flush();
                    }
                }
                catch (Exception ex) { }
            }
        }
        #endregion
    都写完以后,我们需要对IHttpAsyncHandler接口进行实现:
    #region 异步执行请求
        public class AsyncSocketHandler : IHttpAsyncHandler,IRequiresSessionState
        {
            private static object obj = new object();
            private static object objEx = new object();
            public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
            {
                lock (obj)
                {
                    int port = Int32.Parse(context.Request.QueryString["port"].ToString());
                    myAsyncResult asyncResult = new myAsyncResult(context, cb, extraData);  //实例
                    myAsyncFunction.Init(asyncResult, port); //接收所有传入的异步对象
                    myAsyncFunction.AcceptClients();   //处理所有传入的异步对象
                    asyncResult.isCompleted = true;
                    return asyncResult;
                }
            }
            public void EndProcessRequest(IAsyncResult result)
            {
                myAsyncFunction.ResponseClients();  //服务端发送客户端的应答
            }
            public bool IsReusable
            {
                get { return false; }
            }
            public void ProcessRequest(HttpContext context)
            { }
        }
        #endregion
    由于代码比较简单,我这里不做过多的讲解,逻辑顺序就是,通过BeginProcessRequest方法处理所有传入的异步执行对象,然后执行;执行完毕之后,通过IAsyncResult接口打印数据到前台去。
     
    上面的是服务端部分,接口写完后,如何调用呢?方法很简单,我直接贴出前台代码:
    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Server.aspx.cs" Inherits="SocketViaWeb.Server" %>
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
     
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>UDP服务器端</title>
        <style type="text/css">
        body
        {
            font-size:12px;
        }
        </style>
        <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
        <script type="text/javascript">
            $(document).ready(function () {
                $("#btnListen").bind("click", function () {
                    setInterval(TriggerAjax, 1000);
                    //TriggerAjax();
                });
            });
            var TriggerAjax = function () {
                var port = $("#txtPort").val();
                $.ajax({
                    type: "GET",
                    url: "AsyncSocketHandler.ashx?port=" + port,
                    dataType:"text",
                    success: function (result) {
                        //if (result != "") {
                        var myDate = new Date();
                        $("#msg").append(myDate.toLocaleString() + " : " + result).append("<br>");
                        //}
                    },
                    error: function (result) {
                        debugger;
                    }
                });
            }
        </script>
    </head>
    <body>
        <div style="height: 390px;  435px;float:left;border:1px solid #B7D6BF;">
            <div style="height:30px;100%;float:left;background:url('images/navEx.gif') repeat-x;line-height:30px;text-align:center;"><strong>服务端接收数据</strong></div>
            <div style="float:left;100%;height:310px;border-bottom:1px solid #B7D6BF; overflow-x:auto;" id="msg"></div>
            <div style="float:left;100%;height:50px;text-align:right;line-height:50px;">
                绑定的本机端口:<input id="txtPort" type="text" style="40px;border:none;border-bottom:1px solid black;" value="10004" />
                <input id="btnListen" type="button" value="监听" style="border:none;background:url('images/btn.gif') no-repeat;50px;height:20px;color:White;" /></div>
        </div>
    </body>
    </html>

    在前台,我是通过一个轮询来每隔1秒钟检测一次UDP Socket连接,如果当前有数据传来,那么就会显示到页面上。这里就是服务端的所有部分。

    客户端代码逻辑

    至于客户端,我就不细说了,和正常的UDP socket发送的写法一样:

    using System;
    using System.Net.Sockets;
    using System.Net;
    using System.Text;
     
    namespace SocketViaWeb
    {
        public partial class Client : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                    port = Int32.Parse(txtPort.Value);
                    endPoint = new IPEndPoint(IPAddress.Parse("192.168.0.100"), port);
                    if (client == null)
                    {
                        client = new UdpClient();
                    }
            }
            private int port;
            private static UdpClient client;
            private static IPEndPoint endPoint;
     
            protected void btnListen_Click(object sender, EventArgs e)
            {
                SendContentViaUDP(txtMsg.Text);
            }
            private void SendContentViaUDP(string content)
            {
                client.Connect(endPoint);
                byte[] byteToSend = Encoding.Default.GetBytes(content);
                client.Send(byteToSend, byteToSend.Length);
            }
        }
    }

    效果展示

    好了,我们开始运行程序,并且同时打开两个窗口,一个是客户端,一个是服务器端,看看效果吧:

    源码下载

    点击这里下载

  • 相关阅读:
    SharePoint 中AJAX请求报错
    SharePoint Online 站点启用内容编辑器部件
    SharePoint 表单开发常用脚本[不断更新ing]
    SharePoint Online 工作流添加历史记录
    BBC评出的100本最具影响力经典书籍
    描写人物的成语汇总,请为孩子收藏!
    失传已久,1917年的满分作文,惊现于世!
    MySQL用户及权限
    数据库SQL优化大总结之 百万级数据库优化方案(转载)
    3分钟弄懂中国金融体系
  • 原文地址:https://www.cnblogs.com/scy251147/p/3386688.html
Copyright © 2020-2023  润新知