`
北极的。鱼
  • 浏览: 150720 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

【转】Http Handler 介绍

阅读更多

【转自     http://www.tracefact.net/Asp-Net-Architecture/Introduction-to-Http-Handler.aspx

引言

在 Part.1 Http请求处理流程 一文中,我们了解了Http请求的处理过程以及其它一些运作原理。我们知道Http管道中有两个可用接口,一个是IHttpHandler,一个是IHttpModule,但在Part.1中,我并没有详细讲述如何对它们进行编程,只是轻描淡写地一笔带过。所谓学以致用,前面已经介绍了不少概念和原理。在本文中,我们通过几个范例来了解 IHttpHandler,看看掌握这些原理的实际用途。

IHttpHandler 概述

可能和我一样,很多Asp.Net开发人员都有过Asp的背景,以至于我们在开发程序的时候,通常都是在“页面级”上思考,也就是说我们现在正在做的这个页面应该有什么样的功能,是进行一个问卷调查还是一个数据库查询等等。而很少在“请求级”思考,考虑有没有办法来通过编码的方式来操控一个Http请求。

实际上,Framework提供了一系列的接口和类,允许你对于Http请求进行编程,而实现这一操作的一个主要的接口,就是 IHttpHandler(另一个是IHttpModule)。

应该还记得第一节中我们提到过 ISAPI,它根据文件名后缀把不同的请求转交给不同的处理程序。但是仔细看看就会发现:几乎一大半的文件都交给 aspnet_isapi.dll 去处理了。很明显,aspnet_isapi.dll 不可能对每种文件采用同一种方式处理,那么 aspnet_isapi.dll 是如何更进一步处理不同的文件,交由谁去处理呢?为了搞清楚这个问题,我们需要打开机器上C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\ 目录下的web.config 文件。

NOTE:我查阅了很多资料,都说是在 machine.config 中,但实际上 v2.0.50727 下的machine.config中httpHandlers结点是这样的:<httpHandlers />,并没有给出详细的处理程序,在Web.config中才能看到。而v1.1.4322 下的machine.config中却有。

找到httpHandlers结点,应该可以看到如下这样的代码(做了省略):

<httpHandlers>
... ... //略
<add path="*.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True" /><add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True" />
   <add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="True" />
   <add path="*.asax" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
<add path="*.ascx" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
   <add path="*.config" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
   <add path="*.cs" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />
   <add path="*" verb="GET,HEAD,POST" type="System.Web.DefaultHttpHandler" validate="True" />
   ... ... //略 
</httpHandlers>

 

可以看到,在<httpHandlers>结点中将不同的文件类型映射给不同的Handler去处理,对于.aspx来说,是由System.Web.UI.PageHandlerFactory来处理。而对于.cs来说,是由System.Web.HttpForbiddenHandler 处理,从ForbiddenHandler名字中出现的Forbidden (翻译过来是“禁止”)可以看出,这个Handler可以避免我们的源码被看到。

NOTE:System.Web.UI.PageHandlerFactory 是一个IHttpHandlerFactory,而不是一个单一的HttpHandler,IHttpHandlerFactory用来做什么后面会说明。

上面列出的是.Net Framework在处理Http请求时的所采用的默认Handler。而如果我们要用编程的方式来操控一个Http请求,我们就需要实现IHttpHandler接口,来定制我们自己的需求。

IHttpHandler的定义是这样的:

public interface IHttpHandler
{
    void ProcessRequest(HttpContext context);
    bool IsReusable { get; }
}

 

由上面可以看出IHttpHandler要求实现一个方法和一个属性。其中 ProcessRequest,从名字(处理请求)看就知道这里应该放置我们处理请求的主要代码。

IsReusable属性,MSDN上是这样解释的:获取一个值,该值指示其他请求是否可以使用 IHttpHandler 实例。也就是说后继的Http请求是不是可以继续使用实现了该接口的类的实例,一般来说,我把它设置成true。

那么实现此接口的类形式应该是这样的:

public class CustomHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
       // 处理请求的代码
   }
    public bool IsReusable
    {
       get { return true; }
    }
}

 

 

而为了能使用这个自定义的HttpHandler,我们需要在应用程序目录下的Web.config中注册它。

<system.web>
    <httpHandlers>
      <add path="*.jpg" verb="*" type="MyNameSpace.MyClass, MyDllName" />
    </httpHandlers>
 </system.web>

 

应该发现这与之前在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\目录下web.config中看到的几乎完全一样。这里,path指的是请求的文件名称,可以使用通配符扩大范围,也可以明确指定这个handler仅用于处理某个特定的文件(比如说:filename.aspx)的请求。verb指的是请求此文件的方式,可以是post或get,用*代表所有访问方式。type属性由“,”分隔成两部分,第一部分是实现了接口的类名,第二部分是位于Bin目录下的编译过的程序集名称。

NOTE:如果你新建一个项目,并且在项目下创建HandlerTest.cs,然后让站点引用该项目,那么在生成解决方案的时候会自动将编译好的.dll文件添到Bin目录中。
    NOTE:MyDll只写程序集名,不要加后面的.dll。

使用HttpHandler实现图片防盗链

有了之前这么多的准备知识,实现现在的目标就容易得多了:

NOTE:这个例子,以及下面的一个例子均来自于《Maximizing ASP.NET Real World, Object-Oriented Development》一书:

Step.1:创建文件 CustomHandler.cs,代码如下:

using System;
using System.Web;

namespace CustomHandler{
    public class JpgHandler : IHttpHandler{
       public void ProcessRequest(HttpContext context){
           // 获取文件服务器端物理路径
           string FileName = context.Server.MapPath(context.Request.FilePath);
           // 如果UrlReferrer为空,则显示一张默认的禁止盗链的图片
           if (context.Request.UrlReferrer.Host == null){
              context.Response.ContentType = "image/JPEG";
              context.Response.WriteFile("/error.jpg");
           }else{
              // 如果 UrlReferrer中不包含自己站点主机域名,则显示一张默认的禁止盗链的图片
             if (context.Request.UrlReferrer.Host.IndexOf("yourdomain.com") > 0){
                  context.Response.ContentType = "image/JPEG";
                  context.Response.WriteFile(FileName);
              }else{
                  context.Response.ContentType = "image/JPEG";
                  context.Response.WriteFile("/error.jpg");
              }
           }
       }

       public bool IsReusable{
           get{ return true; }
       }
    }
}

Step.2 编译这个文件

csc /t:library /r:System.Web.dll CustomHandler.cs

Step.3 将编译好的 CustomHandler.dll 拷贝到站点的 Bin 目录下。

Step.4 在Web.Config 中注册这个Handler。

<system.web>
    <httpHandlers>
      <add path="*.jpg" verb="*" type="CustomHandler.JpgHandler, CustomHandler" />
    </httpHandlers>
 </system.web>

 

OK,诸位可以按步骤自行测试一下,这里就不赘述了。

通过IhttpHandler实现图片验证码

也可以在一个.ashx文件中实现IHttpHandler,而不是采用这种提前编译的方式。

Step.1 打开Vs2005,“添加新项”,“一般处理程序”。新建文件后,VS会自动在文件中添加如下的代码:

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;

public class Handler : IHttpHandler
{
    public void ProcessRequest (HttpContext context)
   {
        context.Response.ContentType = "text/plain";
        context.Response.Write("Hello World");
    }
 
    public bool IsReusable 
   {
        get 
       {
            return false;
        }
    }
}

Step.2 将代码改写成如下所示:

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Web;
using System.Web.SessionState;

public class Handler : IHttpHandler, IRequiresSessionState {

    public void ProcessRequest(HttpContext context) {
       context.Response.ContentType = "image/gif";
       //建立Bitmap对象,绘图
       Bitmap basemap = new Bitmap(200, 60);
       Graphics graph = Graphics.FromImage(basemap);
       graph.FillRectangle(new SolidBrush(Color.White), 0, 0, 200, 60);
       Font font = new Font(FontFamily.GenericSerif, 48, FontStyle.Bold, GraphicsUnit.Pixel);
       Random r = new Random();
       string letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ";
       string letter;
       StringBuilder s = new StringBuilder();
       
       //添加随机的五个字母
       for (int x = 0; x < 5; x++) {
           letter = letters.Substring(r.Next(0, letters.Length - 1), 1);
           s.Append(letter);
           graph.DrawString(letter, font, new SolidBrush(Color.Black), x * 38, r.Next(0, 15));
       }
       
       //混淆背景
       Pen linePen = new Pen(new SolidBrush(Color.Black), 2);
       for (int x = 0; x < 6; x++)
           graph.DrawLine(linePen, new Point(r.Next(0, 199), r.Next(0, 59)), new Point(r.Next(0, 199), r.Next(0, 59)));
              
       //将图片保存到输出流中       
       basemap.Save(context.Response.OutputStream, ImageFormat.Gif);
       context.Session["CheckCode"] = s.ToString();   //如果没有实现IRequiresSessionState,则这里会出错,也无法生成图片
       context.Response.End();      
    }

    public bool IsReusable {
       get { return true; }
    }
}

需要特别注意的是,Handler类不仅需要实现 IHttpHandler接口(这个显然),为了在这个Handler类中使用SessionState,还需要实现IRequiresSessionState接口,对于这个接口,MSDN的解释是这样的:Specifies that the target HTTP handler requires read and write access to session-state values. This is a marker interface and has no methods.(翻译过来是:指定当前Http Handler需要对SessionState值的读写访问权。这是一个标记接口,没有任何方法)。

而实际上,IRequiresSessionState的接口定义是这样的:

public interface IRequiresSessionState{}

 

可见,这个接口没有任何需要实现的方法或属性,大家只要记得:如果想在HttpHandler中使用SessionState,必须实现这个接口,实际上也就是在类的标头将这个接口加进去。

Step.3 新建一个ImageCode.aspx页面,在HTML代码中写下:

<img src="Handler.ashx" alt="图片验证码" />

 

OK,在浏览器中打开ImageCode.aspx,应该可以看到如下所示:

利用HttpHandler创建自定义后缀Rss源

RSS如今已经可以说是随处可见,而RSS的实现方式,通常是在一个.aspx的CodeBehind文件中写一个XML文件,然后加载到Response的OutputStream中, Rss源通常是Rss.aspx这种形式的。通过第一章学到的ISAPI的知识,再结合本章学到的关于HttpHandler的知识,很容易想到:我们可以自定一个以 .rss 作为后缀名的文件来实现 Rss 源,比如说Article.rss。现在我们就一步步来实现它:

NOTE:关于RSS的更多内容,可以参阅我编译的 在Web站点中创建和使用RSS源。本文不再解释Rss是什么,如何创建Rss源,为了文章的独立性,仅给出创建过程。

Step.1 创建范例数据库

Create Table RssSample
(
    SampleId      Int Identity(1,1)    Not Null,
    Title         Varchar(100)          Not Null Constraint uq_Title Unique,
    Author        Varchar(50)              Not Null,
    PubDate       DateTime              Not Null Default GetDate(),
    [Description] Varchar(500)          Not Null,
    Link          Varchar(150)          Not Null

    Constraint pk_RssSample Primary Key(SampleId)
)
-- 插入范例数据
Insert Into RssSample(Title, Author, [Description], Link)
Values('标题1', '作者1', '文章摘要1', 'http://127.0.0.1/#' )

-- 省略 ....

Step.2 建立站点,在App_Code目录下建立RssFeedsLib.cs文件。

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Web;
using System.Xml;
using System.Text;

namespace RssFeadsLib
{
    public class RssGenerator
    {
        public static string GetRSS()
        {
            MemoryStream ms = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(ms, null);
            SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=Sample;User ID=sa;Password=sa");       //修改这里成你的数据库连接
            SqlCommand cmd = new SqlCommand("select * from RssSample order by pubdate desc", conn);

            conn.Open();
            SqlDataReader reader = cmd.ExecuteReader();
            writer.WriteStartElement("rss");
            writer.WriteAttributeString("version", "2.0");
            writer.WriteStartElement("channel");
            // Channel 下的结点静态写入
            writer.WriteElementString("title", "TraceFact.Net 技术文章");
            writer.WriteElementString("link", "http://www.tracefact.net");
            writer.WriteElementString("description", "Dedicated to asp.net...");
            writer.WriteElementString("copyright", "Copyright (C) 2007");
            writer.WriteElementString("generator", "My RSS Generator");
            // Item 结点从数据库读取
            while (reader.Read())
            {
                writer.WriteStartElement("item");
                writer.WriteElementString("author", reader.GetString(reader.GetOrdinal("Author")));
                writer.WriteElementString("title", reader.GetString(reader.GetOrdinal("title")));
                writer.WriteElementString("link", reader.GetString(reader.GetOrdinal("Link")));
                writer.WriteElementString("description", reader.GetString(reader.GetOrdinal("Description")));
                writer.WriteElementString("pubDate", reader.GetDateTime(reader.GetOrdinal("PubDate")).ToString(@"ddd, dd MMM yyyy 12:00:00 tt "));
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
            writer.WriteEndElement();
            reader.Close();
            conn.Close();

            writer.BaseStream.Flush();
            writer.Flush();
            ms.Flush();

            // 将流转换成String并返回
            byte[] data = new byte[ms.Length];
            ms.Seek(0, SeekOrigin.Begin);
            ms.Read(data, 0, data.Length);
            ms.Close();
            return UTF8Encoding.UTF8.GetString(data);
        }
    }
}

Step.3 创建可以处理 .rss 后缀名的 RssHandler

我们在这个 RssFeedsLib命名空间下,再添加一个类,这个类用于处理对 .rss 后缀名文件的Http请求。

public class RSSHandler:IHttpHandler
{
    public bool IsReusable
    {
       get {return false;}
    }
    public void ProcessRequest(HttpContext context)
   {
       context.Response.ContentType = "text/xml";
       string str = RssGenerator.GetRSS();
       context.Response.Write(str);
    }
}

Step.4 在Web.config中进行配置

<httpHandlers>
    <add path="*.rss" type="RssFeadsLib.RSSHandler" verb="GET" />
</httpHandlers>

 

 

NOTE:因为这个类和命名空间位于App_Code中,这里就不需要再手动编译RssFeadsLib.cs然后将编译好的.dll应用程序集放到Bin目录中了。至于为什么可以这样,将会在 《Asp.Net 构架与安全机制 Part.5 – 页面生存周期与编译模型》中解释。

Step.5 在IIS 对ISAPI进行设置。

应该还记得在Part.1中如何在IIS中设置ISAPI来进行文件与处理程序映射:

  1. 打开IIS,选择本范例所用的站点,右键,选择“属性”。
  2. 选择“主目录”选项卡,点击“配置...”按钮。
  3. 点击“添加”,设置“可执行文件”为“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll”,设置“扩展名”为“.rss”,点“确定”。
  4. 注意,不要勾选“检查文件是否存在”复选框,这样不用创建文件,只要在地址栏输入任意以.rss后缀结尾的文件名,均会交由上面创建的Handler去处理,而不管这个文件是否存在,也不管请求的是Article.rss还是Sample.rss。

进行了这些设置以后,现在IIS就知道如何去处理对.rss后缀名文件的请求了。

Step.6 测试范例

这个时候,随便打开一个页面,比如空白的Default.aspx,然后我们在地址栏将文件改为:Article.rss(改成abc.rss也是一样),敲回车,应该可以看到如下的画面。

IHttpHandlerFactory 概述

现在假设我们有这样的需求,我们不仅想要处理 .rss 后缀名,还想要能够处理 .atom后缀名,假设处理atom的类命名为AtomHandler,那么我们的Web.config该如何设置呢?我想应该是这样的:

<httpHandlers>
<add path="*.rss" type="RssFeadsLib.RSSHandler" verb="GET" />
<add path="*.atom" type="RssFeadsLib.AtomHandler" verb="GET" />
</httpHandlers>

 

如果我们有很多个HttpHandler分别映射不同后缀名的请求,这样我们的Web.config会变得很冗长,或者,我们只有在程序运行时才能确切地知道使用哪个Handler,这个时候,可以考虑实现 IHttpHandlerFactory来完成这一过程。

IHttpHandlerFactory的定义是这样的:

public interface IHttpHandlerFactory
{
    IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);
    void ReleaseHandler(IHttpHandler handler);
}

 

可见,需要实现两个方法,分别是 GetHandler() 和 ReleaseHandler()。

  • GetHandler(),返回实现了IHttpHandler接口的类的实例。
  • ReleaseHandler(),使得Factory可以重复使用一个已经存在的Handler实例。

对于上面 .atom 和 .rss 的问题,我们可以这样来实现 IHttpHandlerFactory接口:

class HandlerFactory : IHttpHandlerFactory
{
    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
    {
        string path = context.Request.PhysicalPath;
        if (Path.GetExtension(path) == ".rss")
        {
            return new RSSHandler();
        }

        if (Path.GetExtension(path) == ".atom")
        {
            return new ATOMHandler();
        }
        return null;
    }

    public void ReleaseHandler(IHttpHandler handler)
    {
    }
}

 

这时,在Web.Config 中<system.web>节点下进行如下设置即可:

<httpHandlers>
<add path="*.rss,*.atom" type=" RssFeadsLib.HandlerFactory" verb="GET" />
</httpHandlers>

 

但是,这不能简化IIS中ISAPI的设置,还是需要手动去对.rss和.atom分别设置。

总结

在本文中,我们首先讨论了aspnet_isapi.dll 如何将对不同后缀名文件的请求分发给相应的处理程序,如何查看Framework默认的处理程序Handler。

然后,我们通过三个实例,图片防盗链、图片验证码、处理自定义后缀名请求,详细讲解了IHttpHandler的实现方法和使用过程。

最后,我向大家概要地介绍了IHttpHandlerFactory接口。

分享到:
评论

相关推荐

    Mybatis类型转换介绍 TypeHandler

    NULL 博文链接:https://zw7534313.iteye.com/blog/2221078

    MyBatis使用自定义TypeHandler转换类型的实现方法

    主要介绍了MyBatis使用自定义TypeHandler转换类型的实现方法,本文介绍使用TypeHandler 实现日期类型的转换,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    Mybatis自定义TypeHandler解决特殊类型转换问题详解

    主要介绍了Mybatis自定义TypeHandler解决特殊类型转换问题详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    老罗android视频开发源码和ppt经典

    4.1 http协议的介绍 4.2 http协议GET方式获取图片 4.3 http使用Post方式进行提交数据 4.4 http使用apache工具类提交数据 五、 Android解析json数据 5.1 android解析json数据(一) 5.2 android解析json数据(二) ...

    exception_handler::bomb:自定义错误页面:bomb:Ruby on Rails→将RubyRails异常转换为品牌的4xx5xx HTTP错误页面

    :memo: 介绍 ExceptionHandler用动态视图替换Rails的默认错误页面。 它通过向控制器注入实现此目的-允许我们使用自己HTML填充错误的响应。 要了解其工作原理,您需要了解Rails如何处理错误: Rails使用 (上面)...

    LuckyDial:幸运抽奖转盘

    void setHandler(Handler handler) boolean isRunning() boolean isStoped() void start() int get() stop()方法是用来结束抽奖了,为了更加方便我们的实际的应用,重载了stop()方法来指定抽奖结果,stopIndex就是...

    VMProtect的逆向分析和静态还原

    VMP的Handler VMP指令分类 逻辑运算指令 寄存器轮转 字节码加密和随机效验 阶段总结 二、VMProtect静态跟踪 (一)虚拟执行特点 (二)执行引擎的虚拟执行 (三)分析条件跳转的两条出边 三、字节码反编译 (一)...

    微软企业库5.0学习之路

    第九步、使用PolicyInjection模块进行AOP—PART3——内置Call Handler介绍 第九步、使用PolicyInjection模块进行AOP—PART4——建立自定义Call Handler实现用户操作日志记录 第十步、使用Unity解耦你的系统—PART1...

    《深入理解Android》卷Ⅰ

    1.3 工具介绍 1.3.1 Source Insight介绍 1.3.3 Busybox的使用 1.4 本章小结 第2章 深入理解JNI 2.1 JNI概述 2.2 学习JNI的实例:MediaScanner 2.3 Java层的MediaScanner分析 2.3.1 加载JNI库 2.3.2 Java的native...

    ember-rsvp-global-error-handler:展示如何全局测试 Ember.RSVP 错误

    这个应用程序的简短介绍可以很容易地转到这里。 先决条件 您将需要在您的计算机上正确安装以下东西。 (带有 NPM) 安装 git clone 这个仓库 切换到新目录 npm install bower install 运行/开发 ember server 在...

    深入理解Android卷1全

    1.3.1 Source Insight介绍 / 8 1.3.3 Busybox的使用 / 11 1.4 本章小结 / 12 第2章 深入理解JNI / 13 2.1 JNI概述 / 14 2.2 学习JNI的实例:MediaScanner / 15 2.3 Java层的MediaScanner分析 / 16 2.3.1 加载JNI库 /...

    Herm(一套快速开发高性能的网络应用的C++库)

    1.1 什么是Herm Herm是一套快速开发高性能的网络应用的C++库。比如开发网络游戏、即时通信、流媒体、文件下载、P2P等基于TCP/IP网络应用。 Herm包括三个组件: ...http://hi.csdn.net/herm_lib

    标准MFC WinSock ActiveX控件开发实例(II)高级篇

    回顾:在上一篇文章《标准MFC WinSock ActiveX控件开发实例》中我们详细介绍了控件的开发过程,以及接口和事件的 添加和响应方法。现在我们将继续上次没有写完的控件继续进行开发,并完善作为一个WinSock控件应该具备...

    深入理解Android:卷I--详细书签版

    http://blog.csdn.net/fksec/article/details/7888251 深入理解Android:卷I(51CTO网站“2011年度最受读者喜爱的原创IT技术图书”) 基本信息 作者: 邓凡平 出版社:机械工业出版社 ISBN:9787111357629 上架时间...

    java俄罗斯方块源码-TetrisMina:俄罗斯方块项目的一个分支

    MINA官方文档介绍非常详细,从历史介绍--&gt;框架鸟瞰--&gt;核心概念等关键部分都做了中重点标注和解释.堪称教科书式的文档说明. 预备知识 :看完这个系列之后,最好看一下它的实现 背后原理 I/O Peocessor,创建服务和...

    Android开发应用实战详解源代码

    4.15 progressbar和handler进度条 4.16 动态排版处理 4.17 listactivity布局- 4.17.1 listactivity介绍 4.17.2 使用listactivity 4.18 matrix实现图片缩放 4.19 bitmap和matrix图片旋转 4.20 加载手机存储卡中的文件...

    WEB前端实现裁剪上传图片功能

    这里面有几个功能,第一个是支持拖拽,第二个压缩,第三个是裁剪编辑,第四个是上传和上传进度显示,下面依次介绍每个功能的实现: 1. 拖拽显示图片 拖拽读取的功能主要是要兼听html5的drag事件,这个没什么好说的,...

    DWR.xml配置文件说明书(含源码)

    Array的配置项目没有上面介绍的那么直观,默认情况下DWR装载所有的基本类型和可装载的对象,这些包括String,Date等先前介绍的类型.java高级程序员可能会理解为什么match的这行有点奇怪. [Z"/&gt; [B"/&gt; [S"/&gt; [I"/&gt; [J"/&gt;...

    圈圈教你玩USB(全书)

    2.11.3 KEIL工具栏及仿真介绍 15 2.11.4 按键驱动的编写 18 2.11.5 串口驱动的编写 24 2.11.6 PDIUSBD12读写函数及读ID的实现 28 2.12 本章小结 33 第三章 USB鼠标的实现 1 3.1 USB鼠标工程的建立 1 3.2 USB...

Global site tag (gtag.js) - Google Analytics