上一篇文章中,我们提到使用IHttpAsyncHandler来进行UDP的收发操作。由于UDP模型比较简单,所以运行没什么问题。这一篇我主要是使用IHttpAsyncHandler来进行TCP的收发操作。由于TCP的模型比较复杂,所以在设计的时候,稍微麻烦了一些。
核心讲解
首先,我贴上代码,然后来逐一讲解:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System; using System.Web; using System.Net.Sockets; using System.Net; using System.Text; using System.IO; namespace SocketViaWeb { #region 写日志 public static class Logger { //写日志 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 #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.Output.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 #region 异步执行对象 public static class myAsyncFunction { private static string resultResponse = string.Empty; //获取客户端数据 private static TcpListener tcpListener; private static MyAsyncResult asyncResult; //通知回传对象 // 把一个异步的请求对象传入以便于供操作 public static void Init(MyAsyncResult result,int port) { asyncResult = result; if (tcpListener == null) { try { IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("192.168.0.100"),port); tcpListener = new TcpListener(endPoint); tcpListener.Start(); } catch (System.Net.Sockets.SocketException ex) { tcpListener = null; throw new Exception(ex.Message); } } } //接收客户端数据并将数据打印到前台 public static void AcceptClients() { TcpClient client = tcpListener.AcceptTcpClient(); byte[] bufferResult = new byte[1024]; NetworkStream readStream = client.GetStream(); readStream.Read(bufferResult, 0, bufferResult.Length); resultResponse = System.Text.Encoding.Default.GetString(bufferResult); Logger.WriteLog("Receiving TCP Client Message: " + resultResponse); asyncResult.send(resultResponse); } } #endregion #region 异步执行请求 public class AsyncSocketHandler : IHttpAsyncHandler { private static object obj = 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) { MyAsyncResult asyncResult = result as MyAsyncResult; if (asyncResult != null) { asyncResult.send(string.Empty); } } public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { } } #endregion }
对于WriteLog函数,我就不讲了,主要是用来写日志的。
对于MyAsyncResult类,主要是用来接收处理状态并返回,在EndProcessRequest(IAsyncResult result)中可以还原对象,然后做一些善后的工作,比如关闭数据库,清空缓存等等。
对于myAsyncFunction类,这是我们执行的主体,里面的AcceptClients函数是我们执行的核心。
对于AsyncSocketHandler类,它继承自异步handler:IHttpAsyncHandler,主要是用来进行异步处理的。
在下面的代码中:
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; } }
首先实例化MyAsyncResult对象,以便创建返回参数;然后初始化调用对象;初始化之后,开始调用我们的核心对象,也就是利用TCP接收Client的连接。最后返回执行对象。
public void EndProcessRequest(IAsyncResult result) { MyAsyncResult asyncResult = result as MyAsyncResult; if (asyncResult != null) { asyncResult.send(string.Empty); } }
当异步执行完毕之后,就会进入EndProcessRequest函数,这个函数主要通过还原MyAsyncResult对象,实现清理等操作,这里我就不再赘述。
那么如何调用呢?
调用其实很简单,通过Ajax请求这个异步Handler,然后利用其Success方法打印出获取的对象就可以了。 需要注意的是,在Success方法中,我们可以再调用Ajax请求来实现循环获取Client Sockets。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<%@ 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>TCP服务器端</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 array = new Array(); 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(); var message = myDate.toLocaleString() + " : " + result; if (!array.contains(result)) { $("#msg").append(message).append("<br>"); } } TriggerAjax(); //循环调用 }, error: function (result) { TriggerAjax(); //循环调用 } }); } Array.prototype.contains = function (elem) { for (var i = 0; i < this.length; i++) { if (this[i] == elem) { return true; } } return false; } </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;" /> <input id="btnStop" type="button" value="停止" style="border:none;background:url('images/btn.gif') no-repeat;50px;height:20px;color:White;" onclick="return btnStop_onclick()" /> </div> </div> </body> </html>
实现效果
最后看看我们实现的效果吧:
源码下载
由于源码中有SignalR的示例,附件过大,我已经删除了相关的Signr附件,编译的时候,请首先运行 Install-Package Microsoft.AspNet.SignalR -version 1.0
来获取相关的版本。我百度网盘放了全版本的:http://pan.baidu.com/s/15SDGG
解决方案中的SocketViaWeb项目下的Server.aspx 为服务端,Client.aspx为客户端。