《C#网络应用编程(第3版)(工业和信息化部“十二五”规划教材) (普通高等学校计算机教育“十二五”规划教材)[精品]》-想法

2016-08-02
原文:对于需要侦听网络并发送请求的网络应用程序而言,System.Net.Sockets 命名空间提供了TcpClient类、TcpListener类和UdpClient类。这些类用传统技术封装了不同传输协议建立连接的实现细节,提供了多种传输数据的操作方法。另外,当需要在套接字级别进行控制时,还可以直接用该命名空间下的Socket类来实现。




2016-08-02
原文:套接字有3种不同的类型:流式套接字、数据报套接字和原始套接字。流式套接字用来实现面向连接的TCP通信,数据报套接字实现无连接的UDP通信,原始套接字实现IP数据包通信。这3种类型的套接字均可以用System.Net.Sockets命名空间下的Socket类来实现。 编写基于TCP和UDP的应用程序时,既可以使用对套接字进一步封装后的TcpListener类、TcpClient类或UdpClient类,也可以直接用Socket类来实现。而编写自定义的新网络协议程序时,则只能用Socket类来实现,而且这个实现工作量是巨大的。




2016-08-02
原文:IP连接领域有两种通信类型:面向连接的(Connection-Oriented)和无连接的(Connectionless)。在面向连接的套接字中,使用TCP来建立两个IP地址端点之间的会话。一旦建立了这种连接,就可以在设备之间进行可靠的数据传输。 对于网络应用程序而言,在System.Net.Sockets命名空间下,除了套接字以外,.NET框架还提供了TcpClient类和TcpListener类。这些类封装了不同传输协议建立连接的细节,提供了多种传输数据的操作方法。




2016-08-02
原文:除了TCP、UDP以外,还有用传统技术实现的HTTP应用编程、FTP应用编程、SMTP与POP3应用编程以及P2P应用编程等




2016-08-02
原文:使用IP地址的点分十进制表示法,Internet地址空间又划分为5类,具体如下。 A类:0.x.x.x~127.x.x.x (32位二进制最高位为 0) B类:128.x.x.x~191.x.x.x (32位二进制最高2位为10) C类:192.x.x.x~223.x.x.x (32位二进制最高3位为110) D类:224.x.x.x~239.x.x.x (32位二进制最高4位为1110) E类:240.x.x.x~255.x.x.x (32位二进制最高5位为11110) 在这5类IP地址中,A类IP地址由1字节的网络地址和3字节的主机地址组成,主要用于网内主机数达1600多万台的大型网络。




2016-08-02
原文:注意,A类中有一个特殊的IP地址,即127.0.0.1,该地址专用于本机回路测试。 B类IP地址由2字节的网络地址和2字节的主机地址组成,适用于中等规模的网络,每个网络所能容纳的计算机数大约为6万多台。 C类IP地址由3字节的网络地址和1字节的主机地址组成,适用于小规模的局域网,每个网内最多只能包含254台计算机。 D类地址属于一种特殊类型的IP地址,TCP/IP规定,凡IP地址中的第一字节以“lll0”开始的地址都叫多点广播地址。因此,任何第一字节大于223小于240的IP地址都是多点广播地址。 E类地址作为特殊用途备用。




2016-08-02
原文:在这些网络分类中,每类网络又可以与后面的一个或多个字节组合,进一步分成不同的网络,称为子网。每个子网必须用一个公共的网址把它与该类网络中的其他子网分开。 为了识别IP地址的网络部分,又为特定的子网定义了子网掩码。子网掩码用于屏蔽IP地址的一部分以区别网络标识和主机标识,它是判断任意两台计算机的IP地址是否属于同一子网的依据,并说明该IP地址是在局域网上,还是在远程网上。 把所有的网络位(二进制)用1来标识,主机位用0来标识,就得到了子网掩码。 IP 地址与子网掩码的关系可以简单地理解为:两台计算机各自的 IP 地址与子网掩码进行二进制“与”运算后,如果得出的结果是相同的,则说明这两台计算机处于同一个子网,否则就是处于不同的子网上。 假设子网掩码为255.255.255.0,转化为二进制为11111111.11111111.11111111.00000000,则IP地址和子网掩码进行二进制“与”运算后,前3字节构成网络标示(子网号),第4字节为0。例如,对于IP地址192.168.1.X,可以将子网掩码设置为255.255.255.0,则该子网内所有的IPv4地址为 192.168.1.0、




2016-08-02
原文:192.168.1.1、192.168.1.2、…、192.168.1.254、192.168.1.255 (2)IPv6编址方案。第2种IP地址编址方案是IPv6编址方案。在这种编址方案中,每个IP地址有16字节(128位二进制数),其完整格式用8段十六进制表示,各段之间用冒号分隔。为了简化表示形式,每段中前面的0可以省略。另外,连续的0可省略为“::”,但只能出现一次。例如: 1080:0:0:0:8:800:200C:417A简写为 1080::8:800:200C:417A FF01:0:0:0:0:0:0:101简写为FF01::101




2016-08-02
原文:0:0:0:0:0:0:0:1简写为::1 0:0:0:0:0:0:0:0简写为:: 本机回环地址:IPv4为127.0.0.1,IPv6为::1 另外,IPv6没有定义广播地址,其功能由多播地址替代。




2016-08-02
原文:从表面上看,好像知道了远程主机的IP地址,本机就能够和远程主机相互通信。其实真正相互完成通信功能的不是两台计算机,而是两台计算机上的进程。或者说,IP地址仅仅能够识别到某台主机,而不能识别该主机上的进程。如果要进一步识别是哪个进程,还需要引入新的地址空间,这就是端口(Port)。 在网络通信技术中,端口有两种含义:一是指物理意义上的端口,比如,ADSL Modem、集线器、交换机、路由器上连接其他网络设备的接口,如RJ-45端口、SC端口等;二是指逻辑意义上的端口,即进程标识,端口号的范围从0到65535,比如用于HTTP的80端口,用于FTP的21端口等。




2016-08-02
原文:由于端口地址用两字节二进制数来表示,因此,可用端口地址的范围是十进制的0~65535。另外,由于1000以内的端口号大多被标准协议所占用,所以程序中可以自由使用的端口号一般都使用大于1000的值。




2016-08-05
原文:在编写各种复杂的网络应用程序之前,需要首先掌握几个最基本的类:提供网际协议IP地址的IPAddress类,包含IP地址和端口号的IPEndPoint类和为Internet或Intranet主机提供信息容器的IPHostEntry类。




2016-08-05
原文:1.IPAddress类 System.Net命名空间下的IPAddress类提供了对IP地址的转换和处理功能。一般用IPAddress类提供的静态Parse方法将IP地址字符串转换为IPAddress的实例。例如: try { IPAddress ip = IPAddress.Parse("143.24.20.36"); } catch { MessageBox.Show("请输入正确的IP地址!"); } 如果提供的IP地址字符串格式不正确,调用Parse方法时会出现异常。 另外,利用该实例的AddressFamily属性可判断该IP地址是IPv6还是IPv4。例如: IPAddress ip = IPAddress.Parse("::1");




2016-08-05
原文:if (ip.AddressFamily == AddressFamily.InterNetworkV6) { MessageBox.Show("这是IPv6地址"); } IPAddress类还提供了7个只读字段,分别代表程序中使用的特殊IP地址。




2016-10-29
原文:NetworkInterface类位于System.Net.NetworkInformation命名空间下,利用它可以方便地检测本机有多少个网络适配器、哪些网络连接可用,并可获取某个网络适配器的型号、MAC(Media Access Control, 介质访问控制)地址和速度等信息。




2016-10-29
原文:NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces( );




2016-10-29
原文:IPInterfaceProperties类提供了检测IPv4和IPv6的网络适配器地址信息,利用该类可检测本机所有网络适配器支持的各种地址,如DNS服务器的IP地址、网关地址以及多路广播地址等。




2016-10-29
原文:byte[] macBytes = adapter.GetPhysicalAddress().GetAddressBytes(); sb.AppendLine("MAC地址:" + BitConverter.ToString(macBytes));




2016-10-29
原文:如果希望统计本机接收和发送数据的情况,可利用 System.Net.NetworkInformation 命名空间下的IPGlobalProperties类来实现。该类提供了本地计算机网络连接和通信统计数据的信息。例如,接收到的数据包个数、丢弃的数据包个数等。




2016-10-29
原文:Ribbon控件属于高级控件,默认没有放到工具箱中,因此,使用该控件前,首先需要通过添加引用的办法将其添加到项目中




2016-10-29
原文:Ribbon 控件主要包括以下子项:QuickAccessToolBar(快速访问工具栏)、Application Menu (应用程序菜单)、RibbonTab(选项卡)。另外,在QuickAccessToolBar和RibbonTab子项内,除了可以使用一般的WPF控件外,最常见的做法是在该控件的子项中包含Ribbon专用的子控件。




2016-10-29
原文:修改Ribbon的ShowQuickAccessToolBarOnTop属性,可控制快速访问工具栏显示的位置(控制显示在功能区的上部还是下部)。修改RibbonApplicationMenu的Visibility属性,可控制菜单的可见形式(显示、隐藏、折叠)。 如果希望将快速访问工具栏作为窗口的标题栏,只需要将根元素(默认是Window元素)改为RibbonWindow即可。但这并不是必需的,开发人员可根据需要决定是否这样做。




2016-10-29
原文:或触笔。 WPF在System.Windows.Ink命名空间下提供了与数字墨迹相关的类,利用这些类,可方便地实现动态绘图和编辑功能。




2016-10-29
原文:在 System.Windows.Controls 命名空间下,有一个 InkCanvas 控件(或者叫 InkCanvas 类、InkCanvas元素),该控件实现了墨迹的收集、复制、选择、显示和输入等功能。利用InkCanvas,可以让用户修改或删除现有的Stroke对象,同时还可以将其他控件添加到InkCanvas中。




2016-10-29
原文:InkCanvas类的常用属性如下。 1.DefaultDrawingAttributes属性 DefaultDrawingAttributes 属性用于获取或设置 InkCanvas 中新笔画的绘制特性(DrawingAttributes 对象)。该属性的可选值有:Color(笔画颜色)、Width(触笔宽度)、Height (触笔高度)、StylusTip(触笔形状,圆形或者矩形)、IsHighlighter(触笔是否像荧光笔)、FitToCurve (是否用贝塞尔曲线平滑法来呈现笔画)、IgnorePressure(笔画粗细是否随压力自动改变)。 2.EditingMode属性 EditingMode 属性指定了触笔、手指、鼠标等设备与 InkCanvas 交互的模式,属性的值用InkCanvasEditingMode枚举来表示,默认值为Ink。可选的枚举值有:Ink(接收墨迹)、GestureOnly (只响应笔势或手势但不接收墨迹)、None(不执行任何操作)、Select(用套索方式以及用触笔与墨迹相交的方式选择墨迹)、EraseByPoint(当触笔与墨迹相交时清除相交处的墨迹)、EraseByStroke (当触笔与墨迹相交时清除整个笔画)。




2016-10-29
原文:触点用System.Windows.Ink命名空间下的StylusPoint结构来表示,该结构的属性如下。 ● X、Y:获取或设置StylusPoint的X坐标值或者Y坐标值。 ● PressureFactor属性:获取或设置触笔施加于图面设备的压力大小。 ● Description属性:指定StylusPoint中的StylusPointDescription包含哪些属性。默认情况下,所有StylusPoint对象均包含(x, y)坐标以及触点压力属性。 实现高级的墨迹绘制功能时,可考虑使用PressureFactor属性和Description属性。




2016-10-29
原文:4.触点压力(PressureFactor) System.Windows.Ink命名空间下的PressureFactor类表示当触笔或手指按压在图面设备上时按压力量的大小,简称触点压力。用鼠标模拟时,可通过鼠标移动的快慢来表示,移动越快,表示触点压力越小;移动越慢,表示触点压力越大。 触点压力最小为0.0,最大为1.0,默认值为0.5。




2016-10-31
原文:Guid结构有多种构造函数,下面是其中的一种构造函数语法。 public Guid( int a,  //4字节 short b, //2字节 short c, //2字节 byte[] d //8字节 ) 该构造函数中参数a、b、c、d所占的字节数分别为:4、2、2、8。




2016-10-31
原文:一般用Guid的静态NewGuid方法自动获取新的GUID。例如: Guid id = Guid.NewGuid(); 这种方式能确保每次创建的id都不相同。这样一来,绘图时我们就不需要自己去创建和维护每个对象的ID号了。




2016-10-31
原文:自定义墨迹画板最简洁的办法就是让其继承自InkCanvas类。




2016-10-31
原文:一个 InkCanvas 可以具有一个或多个动态呈现的对象(DynamicRenderer)。在自定义的墨迹画板中,我们只需要将多个DynamicRenderer对象分别添加到StylusPlugIns属性中,再将其赋值给DynamicRenderer属性,即可将其添加到自定义的InkCanvas中。 DynamicRenderer对象是一个特殊的StylusPlugIns对象,在WPF应用程序中,使用InkCanvas或者使用继承自 InkCanvas 类的自定义墨迹控件,不需要显式声明 StylusPlugIns,只需要设计从DynamicRenderer继承的类,即可实现动态的即时呈现。




2016-10-31
原文:有了自定义的墨迹画板后,再利用InkCanvas的DynamicRenderer属性,就可以立即呈现出用户在图面上绘制的各种墨迹形状。 制作自定义墨迹画板的主要设计步骤如下。 ● 创建一个从InkCanvas派生的类。 ● 将自定义的DynamicRenderer分配给InkCanvas.DynamicRenderer属性。 ● 重写 OnStrokeCollected 方法。在此方法中,移除已添加到 InkCanvas 中的原始笔画,然后创建一个自定义笔画,将其添加到 Strokes 属性中,最后再使用包含该自定义笔画的新InkCanvasStrokeCollectedEventArgs调用基类相应的方法。




2016-10-31
原文:在操作系统级别的管理中,利用 Process 类可启动、停止本机或远程进程。进程所执行的程序(.exe文件、.dll文件、桌面快捷方式)可以用各种语言(如C#、Java、C++等)来编写。




2016-10-31
原文:硬件实现的角度来说,对于早期的单核处理器,可将线程看作是操作系统分配处理器时间片的基本执行单元;对于目前的多核处理器,可将线程看作是在每个内核上独立执行的代码段。




2016-10-31
原文:System.Diagnostics命名空间下的Process类提供了在操作系统级别对进程进行管理的各种属性和方法。利用 Process 类,可以启动和停止本机进程,获取或设置进程优先级,确定进程是否响应,是否已经退出,以及获取系统正在运行的所有进程列表和各进程的资源占用情况等。同时也可以利用它查询远程计算机上进程的相关信息,包括进程内的线程集合、加载的模块(.dll 文件和.exe文件)和性能信息(如进程当前使用的内存量等)。




2016-10-31
原文:1.启动进程 如果希望启动某个进程,首先需要创建 Process类的一个实例,并通过 StartInfo 属性指定要运行的应用程序名称以及传递的参数,然后调用该实例的Start方法启动该进程。另外,如果进程带有图形用户界面,还可以用ProcessWindowStyle枚举指定启动进程时如何显示窗口。可选的枚举值有:Normal(正常窗口)、Hidden(隐藏窗口)、Minimized(最小化窗口)和Maximized(最大化窗口)。例如: Process myProcess = new Process(); myProcess.StartInfo.FileName = "Notepad.exe"; //准备执行记事本Notepad.exe myProcess.StartInfo.Arguments = "Test1.txt"; //创建或打开的文档为Test1.txt myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal; myProcess.Start();




2016-10-31
原文:2.停止进程 有两种停止进程实例的方法:Kill方法和CloseMainWindow方法。前者用于强行终止进程,后者只是“请求”终止进程。




2016-10-31
原文:(1)Kill方法和CloseMainWindow方法。 Process实例的Kill方法是终止没有图形化界面进程的唯一方法。由于该方法使进程非正常终止,因此有可能会丢失没有保存的数据,所以一般只在必要时才使用该方法。另外,由于Kill方法是异步执行的,因此在调用 Kill 方法后,还要调用 WaitForExit 方法等待进程退出,或者检查HasExited属性以确定进程是否已经退出。 Process 实例的 CloseMainWindow 方法通过向进程的主窗口发送关闭消息来关闭进程,其行为与用户在界面中单击【关闭】按钮命令的效果相同。这样可使目标程序有机会在清除操作中提示用户保存任何没有保存的数据。如果成功发送了关闭消息,则返回 true;如果关联进程没有主窗口或禁用了主窗口(例如,当前正在显示模式对话框),则返回false。




2016-10-31
原文:只能对本机进程实例调用Kill方法或CloseMainWindow方法,无法用这些方法控制远程计算机上的进程。对于远程计算机上的进程,只能查看进程的信息。




2016-10-31
原文:Process实例的HasExited属性用于判断启动的进程是否已停止运行。如果与Process关联的进程已关闭则返回true,否则返回false。当进程退出时,操作系统将释放进程占用的内存,但仍会保留有关进程的管理信息,例如句柄、退出代码(ExitCode属性)和退出时间(ExitTime属性)。这些属性都是自动填充的,即使用户通过选择界面中的【关闭】命令关闭进程,系统也会自动更新HasExited属性以及ExitTime属性的值。如果希望确保退出应用程序时让Process实例启动的所有进程都关闭,这两个属性是很有用的。




2016-10-31
原文:WaitForInputIdle方法。 Process实例的WaitForInputIdle方法仅适用于具有用户界面的进程,它可以使Process等待关联进程进入空闲状态。如果关联进程已经达到空闲状态,则返回true;否则返回false。例如: Process myProcess = Process.Start("Notepad"); myProcess.WaitForInputIdle(); 此状态很有用,比如某个应用程序需要等待启动的进程完成创建其主窗口,然后才能与该窗口通信。这种情况下,就需要调用WaitForInputIdle方法。




2016-10-31
原文:WaitForExit方法。 Process实例的WaitForExit方法用于设置等待关联进程退出的时间,并在该段时间结束前或该进程退出前,阻止当前线程执行。




2016-10-31
原文:Process 实例的 ExitCode 属性用于获取关联进程终止时指定的值,该属性值为零表示成功退出,非零表示错误编号。ExitTime属性用于获取关联进程退出的时间。 这两个属性只能在HasExited属性为True时才能检测。




2016-10-31
原文:EnableRaisingEvents属性。 Process实例的 EnableRaisingEvents 属性用于获取或设置在进程终止时是否应引发 Exited 事件,如果关联的进程终止(通过退出或者调用Kill)时引发Exited事件则为true,否则为false,默认为false。 如果希望在进程退出后获得通知,可将与该进程关联的组件的EnableRaisingEvents属性设置为true,然后在Exited事件中处理进程退出后的操作。另外,一般在异步操作中将该属性设置为true。如果强制同步等待Exited事件发生,应该用WaitForExit方法。




2016-10-31
原文:/获取本机所有进程 Process[] myProcesses = Process.GetProcesses( ); //获取网络上远程计算机的所有进程。参数可以是远程计算机名,也可以是远程计算机的IP地址 Process[] myProcesses = Process.GetProcesses("192.168.0.1");




2016-10-31
原文:这里需要注意一点,使用GetProcesses方法时,如果所获取的进程不是用Start方法启动的,则该进程的 StartInfo属性将不包含该进程启动时使用的参数,此时应该用数组中每个 Process对象的MainModule属性获取相关信息。




2016-10-31
原文://获取本机指定名称的进程 Process[] myProcesses1 = Process.GetProcessesByName("MyExeFile"); //不要带扩展名 //获取远程计算机上指定名称的进程,参数1是进程名,参数2是远程计算机的名称或IP地址 Process[] myProcesses2 = Process.GetProcessesByName("Notpad", "Server1");




2016-11-01
原文:只有当属于某个进程的所有前台线程都终止后,公共语言运行库才会结束该进程,而且所有属于该进程的后台线程也都会立即停止,而不管其后台工作是否完成。




2016-11-01
原文:具体来说,用Thread对象创建的线程默认都是前台线程,在托管线程池中执行的线程默认都是后台线程。另外,从非托管代码进入托管执行环境的所有线程也都被自动标记为后台线程。




2016-11-01
原文:● IsBackground属性:获取或设置一个值,该值指示某个线程是否在后台执行。如果在后台执行则为true;否则为false。 ● IsThreadPoolThread属性:获取一个值,该值指示线程是否在托管线程池中执行。如果此线程在托管线程池中执行则为true,否则为false。 另外,为了使主线程能及时对用户操作的界面进行响应,也可以将辅助线程作为“后台”任务来执行,即将其设置为后台线程。比如用一个单独的线程监视某些活动,最好将其IsBackground属性设置为true使其成为后台线程,这样做的目的是为了不让该线程影响UI操作和进程的正常终止。




2016-11-01
原文:通过Thread对象可创建一个单独的线程,常用形式为 Thread t = new Thread(<方法名>); 该语句的意思是创建一个线程t,并自动通过相应的委托执行用“<方法名>”指定的方法。




2016-11-01
原文:线程是通过委托来实现的,至于使用哪种委托,要看定义的方法是否带参数。如果定义的方法不带参数,就自动用 ThreadStart 类型的委托调用该方法;如果带参数,则自动用ParameterizedThreadStart类型的委托调用该方法。




2016-11-01
原文:面的代码创建了2个线程。 Thread t1 = new Thread(Method1); Thread t2 = new Thread(Method2); ...... public void Method1(){……} public void Method2(object obj){……}




2016-11-01
原文:上面这段代码和下面的代码是等价的: Thread t1 = new Thread(new ThreadStart(Method1)); Thread t2 = new Thread(new ParameterizedThreadStart(Method2)); ...... public void Method1(){......} public void Method2(object obj){......}




2016-11-01
原文:用 Thread 创建的线程默认为前台线程,如果希望将其作为后台线程,可将线程对象的IsBackground属性设置为true。例如: Thread myThread = new Thread(Method1); myThread.IsBackground = true; 创建线程并设置让其在前台运行还是后台运行后,即可对线程进行操作,包括启动、停止、休眠、合并等。




2016-11-01
原文:启动线程 用Thread创建线程的实例后,即可调用该实例的Start方法启动该线程。例如: t1.Start(); //调用不带参数的方法 t2.Start("MyString"); //调用带参数的方法 在当前线程中调用Start方法启动另一个线程后,当前线程会继续执行其后面的代码。 当将方法作为一个单独的线程执行时,如果方法带有参数,只能在启动线程时传递实参,而且定义该方法的参数只能是一个Object类型。如果希望传递多个参数,可以先将这些参数封装到一个类中,然后传递该类的实例,再在线程中通过该类的实例访问相应的数据。




2016-11-01
原文:线程启动后,当不需要某个线程继续执行的时候,有两种终止线程的方法。 第1种方法是先设置一个修饰符为volatile的布尔型的字段表示是否需要正常结束该线程,称为终止线程。例如: public volatile bool shouldStop; 线程中可循环判断该布尔值,以确定是否退出当前的线程。在其他线程中,可通过修改该布尔值通知是否希望终止该线程。这是正常结束线程比较好的方法,实际应用中一般使用这种方法。




2016-11-01
原文:第2种方法是在其他线程中调用Thread实例的Abort方法终止当前线程,该方法的最终效果是强行终止该线程的执行,属于非正常终止的情况,称为取消线程的执行(不是指销毁线程)。但这种方式可能会导致某个工作执行到一半就结束了。




2016-11-01
原文:Thread.Sleep(1000); //当前线程暂停1s




2016-11-01
原文:创建线程时,默认优先级为Normal。如果想让一些重要的线程优先执行,可以使用下面的方法为其赋予较高的优先级。 Thread t1 = new Thread(MethodName); t1.priority = ThreadPriority.AboveNormal




2016-11-01
原文:注意,当把某线程的优先级设置为最高时,在该线程结束前,其他所有线程都将无法获得执行的机会,这会导致界面看起来像“死机”一样,所以使用最高优先级时要特别小心。除非遇到必须马上处理的任务,否则不要使用最高优先级。




2016-11-01
原文:线程池(ThreadPool类) 线程池是在后台执行任务的线程集合,它与Thread的主要区别是线程池中的线程是有关联的(如当某个线程无法进入线程池执行时先将其放入等待队列,自动决定用哪个处理器执行线程池中的某个线程,自动调节这些线程执行时的负载平衡问题等)。另外,线程池总是在后台异步处理请求的任务,而不会占用主线程,也不会延迟主线程中后续请求的处理。




2016-11-01
原文:ThreadPool类提供了对线程池的操作,如向线程池中发送工作项、处理异步I/O、利用委托自动调度等待的线程、处理专用的计时行为等。 1.线程池的基本特征 托管线程池有如下基本特征。 ● 托管线程池中的线程都是后台线程。 ● 添加到线程池中的任务不一定会立即执行。如果所有线程都繁忙,则新添加到线程池的任务将放入等待队列中,直到有线程可用时才能够得到处理。 ● 线程池可自动重用已创建过的线程。一旦池中的某个线程完成任务,它将返回到等待线程队列中,等待被再次使用,而不是直接销毁它。这种重用技术使应用程序可避免为每个任务创建新线程引起的资源和时间消耗。 ● 开发人员可设置线程池的最大线程数。从.NET 框架 4.0 开始,线程池的默认大小由虚拟地址空间的大小等多个因素决定。而早期版本的.NET框架则是直接规定一个默认的最大线程数,无法充分利用线程池的执行效率。 ● 从.NET框架4.0开始,线程池中的线程都是利用多核处理技术来实现的。




2016-11-01
原文:向线程池中添加工作项 在传统的编程模型中,开发人员一般是直接用ThreadPool.QueueUserWorkItem方法向线程池中添加工作项。例如: ThreadPool.QueueUserWorkItem(new WaitCallback(Method1)); ThreadPool.QueueUserWorkItem(new WaitCallback(Method2)); ThreadPool只提供了一些静态方法,不能通过创建该类的实例来使用线程池。




2016-11-02
原文:(1)用volatile修饰符锁定公共或私有字段。 为了适应单处理器或者多处理器对共享字段的高效访问,C#提供了一个 volatile 修饰符,利用该修饰符可直接访问内存中的字段,而不是将字段缓存在某个处理器的寄存器中。这样做的好处是所有处理器都可以访问该字段最新的值。例如: private static volatile bool isStop = false; public static bool IsStop { get { return isStop; } set { isStop = value; } }




2016-11-02
原文:(2)用Interlocked类提供的静态方法锁定局部变量。 System.Threading.Interlocked类通过加锁和解锁提供了原子级别的静态操作方法,对并行执行过程中的某个局部变量进行操作时,可采用这种办法实现同步。例如: int num = 0; Interlocked.Increment(ref num); //将num的值加1 Interlocked.Decrement(ref num); //将num的值减1 锁定局部变量的另一种实现方式是直接用C#提供的lock语句将包含局部变量的代码块锁定,退出被锁定的代码块后会自动解锁。




2016-11-02
原文:(3)用lock语句锁定代码块。 为了在多线程应用程序中实现不同线程同时执行某个代码块的功能,C#提供了一个 lock 语句,该语句能确保当一个线程完成执行代码块之前,不会被其他线程中断。被锁定的代码块称为临界区。 lock语句的实现原理是进入临界区之前先锁定某个私有对象(声明为private的对象),然后再执行临界区中的代码,当代码块中的语句执行完毕后,再自动解除该锁。例如: private List list = new List( ); ...... lock(list) { ...... //对list进行操作 } 如果锁定的代码段中包含多个需要同步的字段或者多个局部变量,可先定义一个私有字段lockedObj,通过一次性锁定该私有字段实现多个变量的同步操作。例如: private Object lockedObj = new Object ( );




2016-11-02
原文:lock(lockedObj) { ...... } 提供给 lock 的对象可以是任意类型的实例,但不允许锁定类型本身,也不允许锁定声明为public的对象,否则将会使lock语句无法控制,从而引发一系列问题。




2016-11-02
原文:如果开发人员使用的不是C#语言(比如C++等),则只能用.NET框架提供的类来完成各种不同情况下的同步功能(例如Monitor类、Mutex类、SpinLock类、ReaderWriterLockSlim类、Semaphore类、WaitHandle类、EventWaitHandle类等),只是这些类分别适用于不同的情况,用起来没有C#提供的volatile修饰符和lock语句简单而已。如果读者希望深入理解相关概念和原理(类似操作系统课程中介绍的生产者消费者问题),以及在不同情况下应该如何分别处理死锁和争用情况的细节,可参考这些类提供的实现方式。




2016-11-03
原文:1.WPF调度器(Dispatcher) 不论是WPF应用程序还是WinForm应用程序,默认情况下,.NET框架都不允许在一个线程中直接访问另一个线程中的控件,这是因为如果有两个或多个线程同时访问某一控件,可能会使该控件进入一种不确定的状态,甚至可能出现死锁。 为了解决死锁以及异步执行过程中的同步问题,WPF中的每个元素(包括根元素)都有一个Dispatcher属性,Dispatcher会自动在线程池中按优先级对工作项进行排队和调度。通过该属性执行指定的委托,即可实现不同线程之间的交互而不会出现死锁问题。 要在后台线程中与用户界面交互,可以通过向WPF控件的Dispatcher注册工作项来完成。注册工作项的常用方法有两种:Invoke方法和InvokeAsync方法。这两个方法均通过调度器执行指定的委托来实现。例如: TextBlock1.Dispatcher.Invoke(...); TextBlock1.Dispatcher.InvokeAsync(...);




2016-11-03
原文:Invoke 方法是同步调用,即直到在线程池中实际执行完该委托它才返回。InvokeAsync 方法是异步调用,调用该方法后将立即返回到调用的语句,然后继续执行该语句后面的代码。 Dispatcher.Invoke方法的重载形式非常多,常用的重载形式有: Invoke(Action) Invoke(Action, DispatcherPriority) Invoke(Action, DispatcherPriority, CancellationToken) Invoke(Action, DispatcherPriority, CancellationToken, TimeSpan)




2016-11-03
原文:Invoke(Func) Invoke(Func, DispatcherPriority) Invoke(Func, DispatcherPriority, CancellationToken) Invoke(Func, DispatcherPriority, CancellationToken, TimeSpan) 重载形式中的TResult表示任何类型(如void、string、Task等)。




2016-11-03
原文:另外,Dispatcher按优先级对其队列中的元素进行排序。向 Dispatcher队列中添加元素时可指定10个优先级别。这些优先级用DispatcherPriority枚举来表示。学习WPF调度器的基本用法时,使用默认的调度优先级即可,只有当默认优先级不能满足需求时,才需要通过它指定优先级。




2016-11-13
原文:如果希望在不停止当前应用程序进程的情况下动态加载或卸载一个或多个组件(.dll 文件或者.exe文件),最好用应用程序域来实现而不是用多进程来实现。




2016-11-13
原文:有两种获取程序集中指定类型的办法。 ①使用C#提供的typeof关键字获取指定类型的Type对象,例如: Type t = typeof(System.double); ②调用Type类的GetType静态方法获取指定类型的Type对象,例如: Type t = Type.GetType("System.Double")。




2016-11-13
原文:Assembly类是在System.Reflection命名空间中定义的,利用它可访问给定程序集的元数据,并包含可以执行一个程序集(.dll或者.exe)的方法。 元数据是一种二进制信息,用以对存储在公共语言运行库中可移植的可执行文件或存储在内存中的程序进行描述。 Assembly类提供有一个静态的Load方法,利用该方法可加载程序集(.dll文件或者.exe文件)。




2016-11-13
原文:下面的代码将名为Example.exe或者Example.dll的程序集加载到当前应用程序域中,然后从该程序集获取名为MyClass的类型,再通过该类型获取名为MethodA的无参数方法,最后通过委托执行该方法。 System.Reflection.Assembly a = System.Reflection.Assembly.Load("Example"); Type myType = a.GetType("MyClass"); System.Reflection.MethodInfo myMethod = myType.GetMethod("MethodA"); object obj = Activator.CreateInstance(myType);




2016-11-13
原文:myMethod.Invoke(obj, null);




2016-11-13
原文:AppDomain类是为应用程序域提供的编程接口,该类提供了多种属性和方法。这一节我们只学习最基本的用法。 1.常用属性 AppDomain类的常用属性如下。 (1)CurrentDomain属性(静态属性)。该属性用于获取当前线程所在的应用程序域。例如: var currentDomain = AppDomain.CurrentDomain; 如果当前线程是主线程,该属性获取的就是主线程所在的应用程序域;如果当前线程是辅助线程,该属性获取的就是该辅助线程所在的应用程序域。 (2)BaseDirectory属性。该属性用于获取域所在的应用程序的基目录,即该应用程序的根目录。例如: var currentDomain = AppDomain.CurrentDomain; string exePath = currentDomain.BaseDirectory; 如果当前线程是主线程,所得到的 exePath 的值与使用 Environment.CurrentDirectory 属性得到的结果相同。




2016-11-13
原文:常用方法 AppDomain类提供了多种方法,利用这些方法可以创建和卸载域、创建域中各类型的实例以及注册各种通知(如卸载应用程序域)。 下面我们学习最常用的方法。 (1)CreateDomain方法。该方法用于创建新的应用程序域。通过AppDomainSetup对象可设置新域加载的应用程序根目录。例如: //设置domain加载的根路径 AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationBase = @"E:\ls"; //创建domain AppDomain domain = AppDomain.CreateDomain("Domain1", null, setup); 代码中的null表示使用当前应用程序域的访问权限。 (2)ExecuteAssembly方法。ExecuteAssembly方法用于执行应用程序域中的程序集(从入口点开始执行),参数中可直接指定可执行的文件名。例如: domain.ExecuteAssembly("MyConsoleApp.exe");




2016-11-13
原文:3)Unload方法。该方法用于卸载应用程序域,这是一个静态方法。例如: AppDomain.Unload(domain); 默认情况下,只有应用程序域中正在运行的所有线程都已停止或域中不再有运行的线程之后,才卸载该应用程序域。




2016-11-13
原文:下面的代码演示了将以上几种方法综合在一起的用法。 //设置domain中要执行的应用程序根路径 AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; //创建domain AppDomain domain = AppDomain.CreateDomain("Domain1", null, setup); //在domain中执行应用程序 domain.ExecuteAssembly("MyConsoleApp.exe"); //卸载domain AppDomain.Unload(domain);




2016-11-13
原文:(4)CreateInstanceAndUnwrap方法。该方法用于在应用程序域中创建指定类的实例,并返回一个代理(proxy)。使用此方法可避免将包含创建的程序集加载到调用程序集中。例如: var c = AppDomain.CurrentDomain; string exePath = @"E:\ls\ch01.exe"; string typeName = "ch01.MainWindow"; var mainWindow = (Window)c.CreateInstanceFromAndUnwrap(exePath, typeName); frame1.Content = mainWindow.Content;




2016-11-13
原文:用于COM互操作的Load方法 除了以上介绍的常用方法外,AppDomain类也提供了一个通过实例访问的Load方法,使用它也可以加载程序集,但该方法主要用于为实现 COM 互操作性而提供的(通过它在应用程序域中调用COM对象)。或者说,该方法用于为COM对象提供对AppDomain.CreateInstance方法的版本无关的访问。 如果不是为了实现COM互操作,不要用该方法加载应用程序域。另外,也不应该使用Load方法将程序集加载到除从其调用该方法的应用程序域以外的其他应用程序域。




2016-11-13
原文:演示AppDoman的基本用法。利用AppDoman将上一章介绍的InkCanvasExample项目生成的EXE文件的主界面嵌入到当前应用程序的主界面中,使其看起来好像是正在运行当前项目中的一部分功能一样。程序运行效果如图3-4所示。  图3-4 例3-4的运行效果  主要设计步骤如下。




2016-11-13
原文:(1)将InkCanvasExample项目bin\Debug文件夹下的InkCanvasExample.exe复制到当前项目的bin\Debug文件夹下。 (2)将InkCanvasExample项目bin\Debug文件夹下的InkCanvasExample.exe复制到当前项目文件夹下。 (3)修改MainWindow.xaml.cs,相关代码如下。




2016-11-13
原文:private void button_Click(object sender, RoutedEventArgs e) { Button btn = e.Source as Button; oldButton.Foreground = Brushes.Black; btn.Foreground = Brushes.Red; oldButton = btn; if (btn.Tag.ToString() == "例4") { var c = AppDomain.CurrentDomain; string exePath = c.BaseDirectory + @"\InkCanvasExample.exe"; string typeName = "InkCanvasExample.MainWindow"; var mainWindow = (Window)c.CreateInstanceFromAndUnwrap(exePath, typeName); frame1.Content = mainWindow.Content; }




2016-11-13
原文:else { frame1.Source = new Uri(btn.Tag.ToString(), UriKind.Relative); } }




2016-11-13
原文:将字符序列转换为字节序列的过程称为编码。当这些字节传送到网络的接收方时,接收方再将字节序列转换为字符序列,这种过程称为解码。




2016-11-13
原文:1.ASCII ASCII字符集由128个字符组成,包括大小写字母、数字0~9、标点符号、非打印字符(换行符、制表符等4个)以及控制字符(退格、响铃等)。 2.Unicode Unicode 是国际通用的编码方式,可以表示地球上绝大部分地区的文字。这种编码每个字符都占2字节,例如,一个英文字符占2字节,一个汉字也是2字节。 C#中的字符和字符串默认采用的都是Unicode编码。 3.UTF-8 UTF-8是在因特网上使用最广泛的一种编码格式。它是Unicode的一种变长字符编码,用1~4字节表示一个Unicode字符。例如,每个英文字母都占1字节,每个汉字都占4字节。 4.GB2312和GB18030 对于简体中文来说,国家规定的编码标准(国标)有两种,一种是GB2312(1980年公布),另一种是GB18030(2000年公布)。 GB2312 每个汉字的编码长度都占 2 字节,这种编码方式最多支持 6 千多个汉字的编码;




2016-11-13
原文:GB18030编码长度为1~4字节,可支持两万多个汉字的编码。




2016-11-13
原文:Encoding 类位于 System.Text 命名空间下,该类主要用于对字符集进行编码和解码以及将一种编码格式转换为另一种编码格式




2016-11-13
原文:利用Encoding类的Convert方法可将字节数组从一种编码转换为另一种编码,转换结果为一个byte类型的数组。语法为 public static byte[] Convert( Encoding srcEncoding, //源编码 Encoding dstEncoding, //目标编码 byte[] bytes //待转换的字节数组 ) 下面的代码演示了如何将Unicode字符串转换为UTF8字符串。 string s = "abcd"; Encoding unicode = Encoding.Unicode; Encoding utf8 = Encoding.UTF8; byte[] b = Encoding.Convert(unicode, utf8, unicode.GetBytes(s)); string s1 = utf8.GetString(b);




2016-11-13
原文:利用Encoding类实现字符串的编码和解码 可以直接用Encoding类实现字符串的编码和解码,例如: Encoding en = Encoding.GetEncoding("GB2312"); //编码 byte[] bytes = en.GetBytes("abcd123"); //按字节显示编码后的数据 textBlock1.Text = BitConverter.ToString(bytes); //解码 textBlock2.Text = en.GetString(bytes);




2016-11-13
原文:数据流(Stream)是对串行传输数据的一种抽象表示,当希望通过网络逐字节串行传输数据,或者对文件逐字节进行操作时,首先需要将数据转化为数据流。 System.IO命名空间下的Stream类是所有数据流的基类。




2016-11-13
原文:数据流一般和某个外部数据源相关,数据源可以是硬盘上的文件、外部设备(如I/O卡的端口)、内存、网络套接字等。根据不同的数据源,可分别使用从Stream类派生的类对数据流进行操作,包括FileStream类、MemoryStream类、NetworkStream类、CryptoStream类,以及用于文本读/写的StreamReader和StreamWriter类、用于二进制读/写的BinaryReader和BinaryWriter类等。




2016-11-13
原文:对数据流的操作有3种:逐字节顺序写入(将数据从内存缓冲区传输到外部源)、逐字节顺序读取(将数据从外部源传输到内存缓冲区)和随机读/写(从某个位置开始逐字节顺序读/写)。




2016-11-14
原文:System.IO命名空间下的FileStream类继承于Stream类,利用FileStream类可以对各种类型的文件进行读/写,例如文本文件、可执行文件、图像文件、视频文件等。 1.创建FileStream对象 常用的创建FileStream对象的办法有两种。 (1)利用构造函数创建FileStream对象。 第1种办法是利用FileStream类的构造函数创建FileStream对象,语法为 FileStream(string path,FileMode mode,FileAccess access) 参数中的path指定文件路径,mode指定文件操作方式,access控制文件访问权限。 表4-2列出了FileMode枚举的可选值。 表4-2 FileMode枚举   FileAccess枚举的可选值有:Read(打开文件用于只读)、Write(打开文件用于只写)、ReadWrite (打开文件用于读和写)。




2016-11-14
原文:(2)利用File类创建FileStream对象。 第2种办法是利用System.IO命名空间下的File类创建FileStream对象。如利用OpenRead方法创建仅读取的文件流,利用OpenWrite方法创建仅写入的文件流。下面的代码演示了如何以仅读取的方式打开File1.txt文件。 FileStream fs= File.OpenRead(@"D:\ls\File1.txt");




2016-11-14
原文:读/写文件 得到 FileStream 对象后,即可以利用该对象的 Read 方法读取文件数据到字节数组中,利用Write方法将字节数组中的数据写入文件。 (1)Read方法。 FileStream对象的Read方法用于将文件中的数据读到字节数组中,语法如下: public override int Read( byte[] array, //保存从文件流中实际读取的数据 int offset,  // 向array数组中写入数据的起始位置,一般为0 int count  //希望从文件流中读取的字节数 ) 该方法返回从FileStream中实际读取的字节数。




2016-11-14
原文:(2)Write方法。 FileStream对象的Write方法用于将字节数组写入到文件中,语法如下: public override void Write( byte[] buffer, //要写入到文件流中的数据 int offset,  //从buffer中读取的起始位置 int size  //写入到流中的字节数 )




2016-11-14
原文:内存流(MemoryStream) 利用 System.IO 命名空间下的 MemoryStream 类,可以按内存流的方式对保存在内存中的字节数组进行操作。即利用MemoryStream类的Write方法将字节数组写入到内存流中,利用Read方法将内存流中的数据读取到字节数组中。 MemoryStream 的用法与文件流的用法相似,支持对数据流的查找和随机访问,该对象的CanSeek 属性值默认为 true ,程序中可通过Position属性获取内存流的当前位置。 由于内存流的容量可自动增长,因此在数据加密以及对长度不定的数据进行缓存等场合,使用内存流比较方便。




2016-11-14
原文:System.Net.Sockets命名空间下的NetworkStream类也是从Stream类继承而来的,利用它可以通过网络发送或接收数据。 可以将NetworkStream看作在数据源和接收端之间架设了一个数据通道,这样一来,读取和写入数据就可以针对这个通道来进行。注意, NetworkStream 类仅支持面向连接的套接字。 对于NetworkStream流,写入操作是指从来源端内存缓冲区到网络上的数据传输;读取操作是从网络上到接收端内存缓冲区(如字节数组)的数据传输,




2016-11-14
原文:使用NetworkStream对象时,需要注意以下几点。 ● 通过DataAvailable属性,可查看缓冲区中是否有数据等待读出。 ● 网络流没有当前位置的概念,不支持对数据流的查找和随机访问,NetworkStream对象的CanSeek 属性始终返回 false ,读取 Position 属性和调用 Seek 方法时,都会引发NotSupportedException异常。




2016-11-14
原文:1.获取NetworkStream对象 有两种获取NetworkStream对象的办法。 (1)利用TcpClient对象的GetStream方法得到网络流对象。例如: TcpClient tcpClient=new TcpClient( ); tcpClient.Connect("www.abcd.com", 51888); NetworkStream networkStream = client.GetStream( ); (2)利用Socket得到网络流对象。例如: NetworkStream myNetworkStream = new NetworkStream(mySocket);




2016-11-14
原文:下面的代码检查NetworkStream是否可写。如果可写,则使用Write写入一条消息。 if (myNetworkStream.CanWrite) { } { byte[] writeBuffer = Encoding.UTF8.GetBytes("Hello"); myNetworkStream.Write(writeBuffer, 0, writeBuffer.Length); else ...... }




2016-11-14
原文:下面的代码使用 DataAvailable 来确定是否有数据可供读取。当有可用数据时,该示例将从NetworkStream读取数据。 if(myNetworkStream.CanRead) { byte[] readBuffer = new byte[1024]; //设置缓冲区大小 int numberOfBytesRead = 0; // 准备接收的信息有可能会大于1024,所以要用循环 do{ numberOfBytesRead = myNetworkStream.Read(readBuffer, 0, readBuffer.Length); ...... //处理接收到数据 }while(myNetworkStream.DataAvailable); } else { ...... }




2016-11-14
原文:调用构造函数创建CryptoStream对象时,需用目标数据流、要使用的转换和流的模式初始化CryptoStream类的新实例。加密时为写访问模式,解密时为读访问模式。 CryptoStream类的构造函数语法如下。 public CryptoStream( Stream stream,    //对其执行加密转换的流 ICryptoTransform transform, //要对流执行的加密转换 CryptoStreamMode mode  //CryptoStreamMode枚举,有Read和Write两种 )




2016-11-14
原文:使用CryptoStream对象时,一般还要借助其他流进行处理。比如使用FileStream作为目标数据流,再根据创建的CryptoStream对象生成StreamWriter对象,然后调用WriteLine方法,通过CryptoStream将加密后的数据写入FileStream,写入完成后,关闭创建的对象。此时在文件中保存的就是加密后的数据。 解密时,使用和加密时相同的密钥创建CryptoStream实例,并在创建该实例时将构造函数的mode参数改为读模式,再将StreamWriter替换成StreamReader,即可将解密后的数据读取出来。




2016-11-14
原文:StreamReader和StreamWriter类 NetworkStream、MemoryStream和FileStream类都提供了以字节为基本单位的读/写方法,其实现思路都是先将待写入的数据转化为字节序列,然后再进行读/写,这对文本数据来说用起来很不方便。因此,操作文本数据时,一般用StreamReader和StreamWriter类来实现。




2016-11-14
原文:BinaryReader和BinaryWriter类 为了更方便地对图像文件、压缩文件等二进制数据进行操作,System.IO 命名空间还提供了BinaryReader 和 BinaryWriter 类以二进制模式读/写流。对于 BinaryReader 中的每个读方法,在BinaryWriter中都有一个与之对应的写方法,比如BinaryReader提供了ReadByte、ReadBoolean、ReadInt、ReadInt16、ReadDouble、ReadString等方法,与之对应BinaryWriter则提供了多个重载的Write方法分别与之对应。 例如,当Write方法传递的参数为Int32类型时,利用BinaryWriter类的Write方法可以将Int32类型数据转化为长度为4的字节数组,并将字节流传递给一个Stream对象。




2016-11-14
原文:私钥算法以块为单位加密数据,一次加密一个数据块,所以也称为块密码。私钥加密算法与公钥算法相比速度非常快,当加密数据流时,私钥加密是最理想的方式。




2016-11-14
原文:(1)DES和TripleDES加密算法。 DES是美国1977年公布的一种数据加密标准,DES算法当时在各超市零售业、银行自动取款机、磁卡及 IC 卡、加油站、高速公路收费站等领域被广泛应用。例如,信用卡持卡人的 PIN的加密传输,IC 卡的认证、金融交易数据包的 MAC 校验等,那个时候大部分使用的都是 DES算法。但是,该算法目前已经有多种破解方法,因此已被淘汰。 TripleDES算法(也叫3DES算法)是美国国家标准技术协会(NIST)1999年提出的数据加密标准。该算法是DES的一个变形,使用DES算法的3次连续迭代,支持128位和192位的密钥长度,其安全性比DES算法高。




2016-11-14
原文:(2)RC2加密算法。 RC2 算法是Ron Rivest 在1987 年设计的一个块密码算法。该算法密钥长度为40~128 位,以8位递增。




2016-11-14
原文:3)SHA-1加密算法。 SHA-1(安全哈希算法,也称为安全哈希标准)是由美国政府发布的一种加密哈希算法。它可以根据任意长度的字符串生成160位的哈希值。HMACSHA1接受任何大小的密钥,并产生长度为160位的哈希序列。




2016-11-14
原文:(4)AES加密算法。 1997年,美国国家标准技术协会(NIST)开始向全世界公开征集新的高级加密标准(Advanced Encryption Standard,AES)。经过反复筛选后,在 2000 年召开的 AES 评选会议上公布了 5 个候选算法,并从中挑选出Rijndael算法作为新的对称加密算法标准。 Rijndael算法是由Vincent Rijmen和Joan Daemen两人提出的加密算法。该算法作为新一代的数据加密标准,汇聚了强安全性、高性能、高效率、易用和灵活等优点。算法支持128位(16字节)、192位(24字节)和256位(32字节)的密钥长度,与DES算法相比,Rijndael的128位密钥比DES的56位密钥强1021倍。 由于Rijndael加密算法是AES选中的唯一算法,因此将其简称为AES算法。




2016-11-15
原文:对称加密的实现原理 所有对称加密(私钥加密)算法都是通过加密将n字节的输入块转换为加密字节的输出块。或者说,加密和解密字节序列都必须逐块进行,而且读入的数据块必须符合私钥算




2016-11-15
原文:法要求的块的大小,如果不符合应该填充至使其符合要求。例如,RC2、DES和TripleDES每块均为8字节, AES为16字节(默认)、24字节或32字节。如果被加密的数据块大于n,则逐块加密,即一次加密一个块。如果被加密的数据块小于n,则先将其扩展为n字节后再进行加密处理。 (1)用于加密的块密码模式。 块密码加密模式可以根据需要通过CipherMode枚举来选择,例如: using (Aes aes = Aes.Create()) { aes.Mode = CipherMode.CBC; ...... } CipherMode枚举提供的可选值有CBC、CFB、CTS、ECB、OFB,如果不设置,默认为CBC模式。这些枚举值的含义如下。




2016-11-15
原文:CBC:密码块链模式。在该模式中,每个纯文本块在加密前,都和前一个块进行按位“异或”操作。这样可确保即使纯文本包含许多相同的块,这些块中的每一个也会加密为不同的密码文本块。在加密块之前,初始化向量(IV)通过按位“异或”操作与第一个纯文本块结合。如果密码文本块中有一个位出错,相应的纯文本块也将出错。此外,后面的块中与原出错位的位置相同的位也将出错。 CFB:密码反馈模式。该模式将少量递增的纯文本处理成密码文本,而不是一次处理整个块。这种模式将一个块分为几部分,每部分都用移位寄存器对其进行处理。例如,如果块大小为8字节,移位寄存器每次处理一字节,则该块将被分为8个部分。如果密码文本中有一个位出错,则将导致接下来若干次递增的纯文本也出错,直到出错位从移位寄存器中移出为止。默认反馈大小可以根据算法而变,但通常是8位或块大小的位数。支持CFB的算法可使用FeedbackSize属性设置反馈位数。 CTS:密码文本窃用模式。该模式可处理任何长度的纯文本并产生长度与纯文本长度相匹配的密码文本。除了最后两个纯文本块外,对于所有其他块,此模式与CBC模式的行为相同。




2016-11-15
原文:ECB:电子密码本模式。该模式分别加密每个块。任何纯文本块只要相同并且在同一消息中,或者在使用相同的密钥加密的不同消息中,都将被转换成同样的密码文本块。需要特别注意的是,由于该模式存在多个安全隐患,不建议使用此模式。这是因为如果要加密的纯文本包含大量重复的块,则逐块破解密码文本是可行的。另外,攻击者还可以对块进行分析来确定加密密钥。此外,随时准备攻击的对手甚至可能会在密文发送过程中悄悄插入、替代或交换个别的块,从而导致结果与预想的情况大相径庭。 OFB:输出反馈模式。该模式将少量递增的纯文本处理成密码文本,而不是一次处理整个块。此模式与CFB相似,这两种模式的唯一差别是移位寄存器的填充方式不同。如果密码文本中有一个位出错,纯文本中相应的位也将出错。但是,如果密码文本中有多余或者缺少的位,则那个位之后的纯文本都将出错。




2016-11-15
原文:既然有了密钥Key,为什么还要再使用一个初始化向量IV呢?这是因为初始化向量默认是一个随机生成的字符集,使用它可以确保任何两个原始数据块都不会生成相同的加密后的数据块,从而可以尽可能防范穷举搜索而进行的攻击。例如,对于给定的私钥Key,如果不用初始化向量IV,那么相同的明文输入块就会加密为同样的密文输出块。显然,如果在明文流中有重复的块,那么在密文流中也会存在重复的块。对于攻击者来说,就可以对这些重复的块进行分析或者通过穷举来发现密钥。为了解决这个问题,加密时先使用初始化向量 IV 加密第一个纯文本块,然后每个后续纯文本块都会在加密前与前一个密码文本块进行按位“异或”(XOR)运算。因此,每个密码文本块都依赖于它前面的块。这样一来,两个相同的明文块的输出就会不同,从而使数据的安全系数大大提高。




2016-11-15
原文:加密和解密数据时,有两种实现思路,一种是程序根据用户提供的密码,用某种对称加密算法实现加密和解密,这种方式适用于让用户去记忆密码的情况。另一种是随机生成对称加密的密钥,然后用它加密和解密数据,这种方式适用于对本机中的重要数据或者对通过网络传输的数据进行加密和解密的场合。




2016-11-15
原文:/// 使用AES算法加密字符串 public static byte[] EncryptString(string str, byte[] key, byte[] iv) { byte[] encrypted; using (Aes aesAlg = Aes.Create())




2016-11-15
原文:{ ICryptoTransform encryptor = aesAlg.CreateEncryptor(key, iv); MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write); using (StreamWriter sw = new StreamWriter(cs)) { sw.Write(str); } encrypted = ms.ToArray(); cs.Close(); ms.Close(); } return encrypted; }




2016-11-15
原文:/// 使用AES算法解密字符串 public static string DescrptString(byte[] data, byte[] 




2016-11-15
原文:ey, byte[] iv) { string str = null; using (Aes aesAlg = Aes.Create()) { ICryptoTransform decryptor = aesAlg.CreateDecryptor(key, iv); MemoryStream ms = new MemoryStream(data); CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read); using (StreamReader sr = new StreamReader(cs)) { str = sr.ReadToEnd(); } cs.Close(); ms.Close(); }




2016-11-15
原文:/// 根据提供的密码生成Key和IV public static void GenKeyIV(string password, out byte[] key, out byte[] iv) { using (Aes aes = Aes.Create()) { key = new byte[aes.Key.Length]; byte[] pwdBytes = Encoding.UTF8.GetBytes(password); for (int i = 0; i < pwdBytes.Length; i++) { key[i] = pwdBytes[i]; } iv = new byte[aes.IV.Length]; for (int i = 0; i < pwdBytes.Length; i++) {




2016-11-15
原文:iv[i] = pwdBytes[i]; } } }




2016-11-15
原文:/// 随机生成Key和IV public static void GenKeyIV(out byte[] key, out byte[] iv) { using (Aes aes = Aes.Create()) { key = aes.Key; iv = aes.IV; } } }




2016-11-15
原文:byte[] key, iv; AesHelp.GenKeyIV(out key, out iv); textBlock1.Text = "原始字符串:" + textBox1.Text; //加密 byte[] data1 = AesHelp.EncryptString(textBox1.Text, key, iv); string encryptedString = Convert.ToBase64String(data1); textBlock1.Text += "\n加密后的字符串:" + encryptedString;




2016-11-15
原文://解密 byte[] data2 = Convert.FromBase64String(encryptedString); string s = AesHelp.DescrptString(data2, key, iv); textBlock1.Text += "\n解密后的字符串:" + s; }




2016-11-15
原文:不对称加密也叫公钥加密,这种技术使用不同的加密密钥与解密密钥,是一种“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制。不对称加密产生的主要原因有两个,一是为了解决对称加密的密钥管理问题,二是数字签名需要用不对称加密来实现。




2016-11-15
原文:不对称加密使用一个需要保密的私钥和一个可以对外公开的公钥,即使用“公钥/私钥”对来加密和解密数据。公钥和私钥都在数学上相关联,用公钥加密的数据只能用私钥解密,反之,用私钥加密的数据只能用公钥解密。两个密钥对于通信会话都是唯一的。公钥加密算法也称为不对称算法,原因是需要用一个密钥加密数据而需要用另一个密钥来解密数据。 对称加密算法使用长度可变的缓冲区,而不对称加密算法使用固定大小的缓冲区,无法像私钥算法那样将数据链接起来成为流,这是编写程序时必须注意的问题。 不对称加密之所以不容易被攻击,关键在于对私钥的管理上。在对称加密中,发送方必须先将解密密钥传递给接收方,接收方才能解密。如果能通过某种处理,避免通过网络传递私钥,就可以解决这个问题,不对称加密的关键就在于此。 使用不对称加密算法加密数据后,私钥不是发送方传递给接收方的,而是接收方先生成一个公钥/私钥对,在接收被加密的数据前,先将该公钥传递给发送方;由于从公钥推导出私钥是不可能的,所以不怕通过网络传递时被攻击者截获公钥从而推导出私钥(但是无法避免攻击者假冒,后面我们




2016-11-15
原文:还要介绍解决这个问题的办法)。发送方得到此公钥后,使用此公钥加密数据,再将加密后的数据通过网络传递给接收方;接收方收到加密后的数据后,再用私钥进行解密。由于没有直接传递私钥,从而保证了数据的安全性。




2016-11-16
原文:截止到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其密钥的长度足够长,用RSA加密的信息就被认为是不能被破解的。但在分布式计算和量子计算机理论日趋成熟的今天,RSA加密安全性受到了挑战。 在.NET框架中,System.Security.Cryptography命名空间下的RSACryptoServiceProvider类提供了RSA算法的实现,利用它可实现不对称加密、解密以及数字签名。




2016-11-16
原文:ECC(Elliptic Curves Cryptography,椭圆曲线算法)是一种公钥加密算法,与RSA算法相比, ECC 算法可以使较短的密钥达到相同的安全程度。近年来,人们对 ECC 的认识已经不再处于研究阶段,而是开始进入实际应用,如国家密




2016-11-16
原文:码管理局2010年12月14日颁布的SM2算法标准就是基于ECC算法的,对该算法有兴趣的读者可参考下面的网站: http://www.oscca.gov.cn/News/201012/News_1197.htm 与ECC相关的算法包括ECDSA算法、ECDH算法等。该网站还详细介绍了ECC基本的算法描述、数字签名、密钥交换协议以及公钥加密算法等。 在.NET框架中,System.Security.Cryptography命名空间下的ECDiffieHellmanCng类提供了椭圆曲线 ECDSA 算法(也叫 Diffie-Hellman 算法)的下一代加密技术(CNG)的加密实现,利用该类可直接用ECDSA算法实现不对称加密和解密。




2016-11-16
原文:不论是对称加密还是非对称加密,都有如何保存密钥的问题。比如我们常见的让用户自己记忆密码的办法,如果攻击者也知道了该密码,那么攻击者也一样可以用它来进入系统或者利用它来解密数据,这样一来,加密也就失去了意义。所以,网络传输中一般不使用让用户记忆密码的办法,




2016-11-16
原文:而是自动产生密钥,同时还必须有一种办法,来确保密钥存储的安全性,这就是密钥容器的用途。




2016-11-16
原文:1.用密钥容器保存不对称加密的密钥 密钥容器最直接的用途是保存不对称加密的密钥,为了区分是哪一个密钥容器,还需要给每个密钥容器起一个名称。在System.Security.Cryptography命名空间中,有一个CspParameters类,可以通过该类提供的属性设置获取密钥容器的名称。 下面的方法演示了如何保存RSA不对称加密的密钥到密钥容器中,以及如何从密钥容器中获取密钥信息。注意,保存密钥信息和获取密钥信息使用的是同一段代码。 public static RSACryptoServiceProvider GenRSAFromContainer (string ContainerName) { // 创建CspParameters对象,指定密钥容器的名称,用于保存公钥/私钥对 CspParameters cp = new CspParameters( ); //如果不存在名为containerName的密钥容器,则创




2016-11-16
原文:建它,并初始化cp //如果存在,则直接根据它原来保存的内容初始化cp cp.KeyContainerName = ContainerName; //使用CspParameters对象创建RSACryptoServiceProvider的实例 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp); return rsa; } 使用密钥容器保存密钥信息的优点是安全性高,这是因为一旦将密钥信息保存在密钥容器中,它就会永久保存,而且攻击者很难获取该信息。当然,不需要该密钥信息时,也可以通过程序将其删除。 采用密钥容器保存密钥信息时,需要注意一个问题,即当操作系统被破坏时,比如硬盘损坏或者被病毒感染,密钥信息也会随之丢失。为了防止丢失密钥信息而导致原来被加密的数据无法解密,生成密钥信息后,最好先将其导出为 XML 文件保存到某个安全的地方,或者保存到某个数据库中,作为备用。当出现密钥信息丢失的情况时,再将 XML 




2016-11-16
原文:文件保存的密钥信息导入到密钥容器中即可。这种保护措施和要求必须提供数据库数据备份功能的保护措施类似。 可以用以下方法之一导出密钥信息。 (1)ToXMLString方法:返回密钥信息的XML表示形式。 (2)ExportParameters方法:返回保存密钥信息的RSAParameters结构。 (3)ExportCspBlob方法:返回密钥信息的字节数组。 这3个方法都有一个布尔类型的参数,表示导出时是否包含私钥信息。 当需要导入密钥信息时,可以使用FromXMLString方法或者ImportParameters方法初始化不对称加密类的实例。 但是要注意,用单独的文件保存密钥信息,优点是方便灵活;缺点是没有密钥容器安全,因为一旦攻击者获取了该信息,被加密的数据也就无任何安全保障了。




2016-11-16
原文:用密钥容器保存对称加密的密钥




2016-11-16
原文:解决了不对称加密中密钥的保存问题,对称密钥的处理就容易了,只需要用不对称加密的密钥加密对称加密的密钥,然后将不对称加密的密钥保存到密钥容器中。解密时,先从密钥容器中得到不对称加密的密钥,然后利用它解密的结果得到对称加密的密钥,最后再利用对称加密的密钥解密数据。




2016-11-16
原文:数字签名 通过 Internet 下载文件后,怎样知道下载的文件是否和原始文件完全相同呢?或者说,发送方通过 Internet 发送数据后,接收方如何验证接收的数据在网络传输过程中是否被修改过?这就是数字签名的用途。换言之,如果通信双方希望确保信息是来自对方而不是来自第三方,需要使用数字签名进行身份验证。另外,数字签名还可以防止特定一方否认曾发送过的信息。 1.常用的数字签名算法 .NET框架包含的类提供了以下实现数字签名的算法。 (1)RSA算法




2016-11-16
原文:(2)DSA算法 DSA(Digital Signature Algorithm)是Schnorr和ElGamal签名算法的变种,被美国NIST作为数字签名算法标准。 在.NET框架中,System.Security.Cryptography命名空间下的DSACryptoServiceProvider类提供了DSA算法的数字签名实现。注意该算法只能用于数字签名,即只能利用它验证发送方发送的数据完整性以及发送者的身份。 DSA算法的安全性与RSA相比差不多,但DSA的算法执行速度要比RSA快很多。




2016-11-16
原文:(3)ECDSA算法




2016-11-16
原文:在.NET框架中,System.Security.Cryptography命名空




2016-11-16
原文:间下的ECDsaCng类提供了基于ECDSA (也叫Diffie-Hellman算法)的下一代加密技术(CNG)实现,利用该类可直接实现数字签名。




2016-11-16
原文:2.数字签名的实现原理 数字签名是利用私钥加密必须用公钥解密这个原理来实现的。 在应用程序中,可以利用数字签名实现数据身份验证和数据完整性验证。数据身份验证是为了验证数据是不是持有私钥的人发送的,数据完整性验证则用于验证数据在传输过程中是否被修改过。 实现数字签名的基本思路是:发送方先将发送的消息使用数字签名算法创建消息摘要,然后用私钥对消息摘要进行加密,以创建发送方的个人签名。接收方收到消息和签名后,再使用发送方的提供的公钥解密该签名,以恢复消息摘要,并使用发送方所用的同一算法对该消息进行运算。如果接收方计算的消息摘要与收到的消息摘要完全匹配,则接收方可以确保消息在传输过程中没有修改。注意,因为公钥不是保密的,所以任何人都可以用它来验证数字签名。 验证数据完整性的实现思路是:发送方先使用某个数字




2016-11-16
原文:签名算法对数据进行运算得到验证值,然后将数据和验证值一起发送给接收方;接收方接收到数据和验证值后,对接收的数据进行和发送方相同的运算,然后将计算得到的值和接收的验证值进行比较,如果二者一致,说明收到的数据肯定与发送方发送的原始数据相同,从而说明数据是完整的。 总之,设计一个实际的网络应用软件时,应该根据安全第一、性能第二的原则来选择实现的技术,而不要仅仅片面最求性能而忽略了安全性要求。




2016-11-16
原文:第5章 异步编程 异步编程是基于任务的网络应用编程必备的技术,也是学习本书后续章节的基础,希望读者能很好地理解和掌握。  5.1 并行和异步编程预备知识 使用传统的技术实现并行和异步功能时,如果程序员开发经验不足,编写的多线程并行和异步程序往往效率不高,特别是处理异步或并行执行过程中的同步问题时,可能还会带来很多漏洞。为了降低多线程编程的复杂度,在.NET框架4.0及更高版本中开发新的多线程应用程序时,不建议使用早期版本提供的传统技术,而是建议使用新的并行和异步编程模型。新的编程模型涉及的主要内容有: ● System.Threading.Tasks命名空间下的Task类和Task类。




2016-11-16
原文:● System.Threading.Tasks命名空间下的Parallel类。 ● System.Collections.Concurrent命名空间下的并发集合类。 ● System.Linq.ParallelEnumerable类。 ● C#5.0提供的async和await关键字以及.NET框架4.5提供的Task.Run方法。 在多核处理器以及GPU快速发展的今天,使用这些新技术可极大地简化并行和异步编程的难度。 在基于任务的并行和异步编程模型中,会大量使用Lambda表达式、Action委托、Func委托以及元组等。因此,学习这些技术之前,我们需要先学习与此相关的一些基本知识。




2016-11-16
原文:任务(Task类、Task类) 在基于任务的编程模型中,并行和异步都是通过任务来实现的。在多任务编程中,要么异步执行一个或多个任务,要么并行执行多个任务。 从技术实现的角度来看,并行(Parallelism)是利用多




2016-11-16
原文:线程来实现的,异步是利用委托来实现的,并发数据(Concurrency Data)是用专门的并发集合来实现的。 任务用System.Threading.Tasks命名空间下的Task类或者Task类来描述。 1.Task类 Task类表示没有返回值的任务。 2.Task类 Task类表示有返回值的任务。 3.Task.Delay方法 任务都是异步的,Task.Delay方法用于延时执行任务。该方法的重载形式有: Delay(Int32) //延时指定的毫秒数 Delay(TimeSpan) //延时指定的时间(年、月、日、时、分、秒、毫秒等) Delay(Int32, CancellationToken) //延时指定的毫秒数后取消任务操作




2016-11-16
原文:Delay(TimeSpan, CancellationToken) //延时指定的时间后取消任务操作 当某个任务启动后,利用该方法也可以延迟一段时间后再执行。例如: Task.Delay(TimeSpan.FromMilliseconds(1500)); Task.Delay(1500); 这两条语句的功能完全相同,但前者更容易阅读和理解。 Task.Delay方法方法和Thread.Sleep方法的主要区别是:Task.Delay方法只能用于异步等待任务,等待过程中不会影响UI操作,仍能保持界面操作流畅;而Thread.Sleep方法如果不通过异步方式来执行,会影响UI操作,休眠期间界面有停顿现象。




2017-01-12
原文:Lambda 表达式主要用于简化委托的代码编写形式。当函数语句较少,而又需要用委托来调用时,使用Lambda表达式比较方便




2017-01-12
原文:Lambda表达式是一个可用于创建委托或表达式树类型的匿名函数。语法为 (输入参数列表)=>{表达式或语句块} 所有Lambda表达式都使用Lambda运算符(=>)来描述,该运算符读作“goes to”。 运算符左侧的输入参数可以是零个,也可以是多个。如果有多个参数,各参数间用逗号分隔;如果输入参数只有一个,可省略小括号,其他情况都必须用小括号括起来。 右侧可以是表达式,也可以是语句块。如果是表达式或者只有一条语句,可不加大括号,否则必须用大括号括起来。 下面表达式的含义为:输入参数是x,返回的结果为x*x的值。 x => x * x




2017-01-12
原文:在LINQ to Objects中使用Lambda表达式 也可以用Lambda表达式实现类似于LINQ的功能,下面的代码演示了如何在LINQ to Objects中使用Lambda表达式查询List泛型列表。 List numberList = new List { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; var q1 = numberList.Where(i => i < 4); 这段代码和下面的代码是等价的。




2017-01-12
原文:List numberList = new List { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; var q2 = from i in numberList where i < 4 select i; 实际上,凡是能使用匿名委托的地方,都可以用Lambda表达式来实现。




2017-01-12
原文:为了简化委托的用法,System命名空间下提供了Action委托和Func委托




2017-01-12
原文:Action相当于 public delegate void Action(T1 arg1, T2 arg2) Func相当于 public delegate TResult Func(T1 arg1, T2 arg2) 注意,在Action和Func的参数中不能使用out和ref,如果需要返回多个类型,将其包含在TResult中即可(即先创建一个类包含多个参数,然后传递该类的实例)。




2017-01-12
原文:基本用法 下面的代码演示了Action的基本用法。 class Program { static void Main(string[] args)




2017-01-12
原文:{ Action a = ShowMessage; a("OK"); Console.ReadKey(); } private static void ShowMessage(string message) { Console.WriteLine(message); } } 如果方法中的代码较少,更常见的做法是用匿名方法来实现,即将其与 Lambda 表达式一起使用。例如: class Program { static void Main(string[] args) { Action a = () => Console.WriteLine("OK"); //或者




2017-01-12
原文:Action b = (s) => Console.WriteLine(s); b("OK"); Console.ReadKey(); } }




2017-01-13
原文:假如用3元组分别保存学生的姓名、年龄和成绩,并将元组中每个元素在控制台显示出来,可用下面的代码来实现。 var t = Tuple.Create("张三", 20, 90); Console.WriteLine(t.ToString()); Tuple[] students = { Tuple.Create("李四", 22, 80), Tuple.Create("王五", 21, 85) }; foreach (var v in students) { Console.WriteLine("{0,-10},{1,10},{2,10}", v.Item1, v.Item2, v.Item3);




2017-01-13
原文:} 元组通常有4种不同的使用方式: ● 表示一组数据。如表示一条数据库记录时,用每个元素表示该记录的一个字段。 ● 提供对数据集的轻松访问和操作。 ● 在方法的参数传递中不使用out修饰符就可以返回多个值。例如Task的TResult如果包含3个需要返回的值,就可以用Tuple对象来实现。 ● 用1个参数就可以将多个值传递给某个方法。例如Thread.Start(Object)方法只能传递一个参数,如果需要传递3个参数,可以用Tuple对象来实现。




2017-01-13
原文:任务是通过方法(命名方法、匿名方法)来定义的,定义后再通过Task类或者Task类用委托(Action、Func)去调用。另外,定义方法时,以及在调用任务的线程中,都可以利用C#内置的async和await关键字实现异步执行。




2017-01-13
原文:1.异步编程的实现方式 对于初学者而言,在掌握如何编写异步程序之前,首先应该了解异步编程模型有哪些实现模式,这样可避免将过时的技术作为新技术来学习。 (1)传统的异步编程模型(APM) 传统的异步编程模型(APM)是微软推出.NET 框架 1.0 时提供的模型,该模型也称为IAsyncResult模式。在这种设




2017-01-13
原文:计模式中,异步操作要求通过以Begin为前缀的方法和以End为前缀的方法来“配对”实现异步(例如FileStream实例的BeginWrite方法和EndWrite方法)。调用Begin 为前缀的方法后,应用程序可以继续执行其后面的代码,同时异步操作在另一个线程上执行。当异步操作完成后,它会自动调用End为前缀的方法处理异步操作的结果。 该模式已经是过时的技术,对于新的开发工作不再建议采用此模式。 (2)基于事件的异步编程设计模式(EAP) 基于事件的异步编程设计模式(EAP)是微软推出.NET框架2.0时引入的异步编程模式。在EAP模式中,异步操作至少需要一个以Async为后缀的方法和一个以Completed为后缀的事件来“配对”共同表示(例如WebClient类提供的DownloadStringAsync方法和DownloadStringCompleted事件)。当以Async为后缀的异步方法完成工作后,它会自动引发以Completed为后缀的事件,开发人员可在异步方法中定义异步操作,在该事件中处理异步操作的结果。 该模式已经是过时的技术,对于新的开发工作不再建议采用此模式。




2017-01-13
原文:(3)基于任务的异步模式(TAP) 基于任务的异步模式(Task-based Asynchronous Pattern,TAP)是.NET框架4.0引入的设计模式,这种模式是基于任务驱动(Task 和 Task类)来实现的,该模式仅用一个方法就可以表示异步操作的启动和完成,而且可用它表示任意的异步操作任务。 刚推出.NET框架4.0时,异步编程建议的首选方式是TAP。 (4)改进的基于任务的异步模式(async、await、Task.Run和TAP) 从.NET框架4.5开始,C#语言提供了async、await关键字以及Task.Run方法。无论是在性能提升还是在编程的易用性方面,C#本身提供的 async、await 关键字都比.NET 框架 4.0 提供的TAP更优秀,而且这种内置的实现方式适用于用C#编写的各种应用程序(如WPF应用程序、WCF应用程序、ASP.NET应用程序、控制台应用程序以及WinForm应用程序等)。 将.NET框架4.5提供的async和await关键字以及




2017-01-13
原文:Task.Run方法与TAP结合使用,就是在VS2012中建议的基于任务的异步编程模型。




2017-01-13
原文:2.异步操作关键字 async和await是C#5.0和.NET框架4.5提供的功能。async和await关键字进一步简化了TAP的使用,在VS2012中用C#语言和.NET框架4.5开发异步程序时,建议尽量用async和await来实现,而不是用.NET框架4.0提供的TAP来实现。 (1)异步方法和异步事件处理程序 为方便介绍和区分,本书将带有 async 修饰符的普通方法称为异步方法,将带有 async 修饰符的事件处理程序称为异步事件处理程序。实际上,两者除了签名的形式不同以外,其他用法都相同。另外,由于事件本身就是一种特殊的方法(特殊之处在于它是通过委托来调用的),因此有时候(不需要区分两者区别的时候)也将普通方法和事件处理程序统称为异步方法。 (2)async修饰符 async是一个修饰符,只能用在方法或者事件处理程序




2017-01-13
原文:的签名中。 对于普通的方法,有两种情况。 ● 如果方法没有返回值,则用async和Task共同签名。 ● 如果方法有返回值,则用async和Task共同签名。 例如: private void Method1(){......} //普通方法 private async Task Method1Async(){......} //异步方法 private int Method2(){......} //普通方法 private async Task Method2Async (){......} //异步方法 对于事件处理程序,则用async和void共同签名。例如: private void BtnOK_Click(…){......} //普通的事件处理程序




2017-01-13
原文:private async void ButtonOK_Click(…){......} //异步事件处理程序




2017-01-13
原文:(3)await运算符 await是一个运算符,可将其用在表达式中,该运算符表示等待异步执行的结果。换言之,await运算符实际上是对方法的返回值(Task实例的Result属性)进行操作,而不是对方法本身进行操作。另外,包含await运算符的代码必须放在异步方法的内部。例如: private async void ButtonOK_Click(…) { Task a = Method1Async(); //创建任务a ......//此处可继续执行其他的代码 await a; //等待任务a完成,任务完成前不会执行该语句后面的代码,但也不会影响界面操作 Task b = Method2Async(); //创建任务b ......//此处可继续执行其他的代码 int x = await b; //等待任务b完成,任务完成前不会执行该语句后面的代码,也不会影响界面操作




2017-01-13
原文:} private Task Method1Async(){......} private Task Method2Async{......} await运算符和同步编程的最大区别是:异步等待任务完成时,既不会继续执行其后面的代码,也不会影响用户对 UI 界面的操作,这一点非常重要,也是开发人员采用早期的异步编程模型一直都无法用最简单的方式顺利解决而倍感头痛的问题。




2017-01-22
原文:异步方法的命名约定 在.NET框架4.5提供的类中,异步方法名全部约定用Async作为后缀(异步事件处理程序除外),这样做的目的是为了让开发人员能明确看出该方法是一个同步方法还是一个异步方法。例如System.IO.Stream类提供的CopyTo方法、Read方法和Write方法都是同步方法,而CopyToAsync方法、ReadAsync方法和WriteAsync方法都是异步方法。 自定义异步方法时,建议也按照这种约定指定方法名。 还有一种特殊情况,如果自定义的类继承自传统的异步编程模型(APM),或者继承自基于事件的异步编程设计模式(EAP),由于APM和EAP异步方法的后缀可能也是Async,为了和这些旧模型本身提供的方法区分,可将基于任务的异步方法名后缀改为 TaskAsync。但是,由于需要这样继承来实现的情况非常少,所以读者只需要对其了解即可。




2017-01-22
原文:1.定义任务执行的方法 编写任务将要执行的某个方法时,既可以用普通方法实现,也可以用异步方法实现。或者说,不论是普通方法还是异步方法,都可以将其作为任务来执行。 (1)用普通方法定义任务 下面的代码演示了如何在异步按钮事件中将普通方法作为任务来执行: private async void btnStart_Click(...) { await Task.Run(() => Method1()); int result = await Task.Run(() => Method2()); }




2017-01-22
原文:(2)用异步方法定义任务 如果用异步方法来实现,必须用 async 和 Task 共同表示没有返回值的任务,用 async 和Task共同表示返回值为TResult类型的任务,其中,TResult可以是任何类型。 下面的代码演示了如何在异步按钮事件中将异步方法作为任务来执行。 private async void btnStart_Click(...) { await Method1Async()); int result = await Method2Async()); } public async Task Method1Async(){......}




2017-01-22
原文:(3)用匿名方法定义任务 不论是普通方法还是异步方法,当方法内的语句较少时,都可以改为直接用匿名方法通过Lambda表达式来实现。例如: private async void btnStart_Click(...) { await Task.Run(() =>{ ...... }); await Task.Run(async ()=>{ ...... }); }




2017-01-22
原文:2.利用Task.Run方法隐式创建和执行任务 Task类的静态Run方法表示使用默认的任务调度程序在线程池中通过后台执行指定的任务。如果不需要自定义调度程序,使用该方法最方便。




2017-01-22
原文:Task.Run方法常用的重载形式有: Run(Func) //用默认调度程序在线程池中执行不带返回值的任务 Run(Func>) //用默认调度程序在线程池中执行带返回值的任务 Run(Func, CancellationToken) //执行任务过程中可侦听取消通知 Run(Func>, CancellationToken) //执行任务过程中可侦听取消通知




2017-03-15
原文:.利用async和await关键字隐式创建异步任务 隐式调用Task或Task构造函数的方式有多种,其中最常用的方式是用async和await来实现。另外,不管用那种方式创建任务,当需要任务与界面交互时,都可以利用async和await关键字、通过异步执行任务的办法来避免出现界面停顿的情况。




2017-03-15
原文:利用WPF控件的调度器隐式创建和执行任务 由于WPF的每个控件(包括Page、Window等根元素)都有一个对应的调度器(UI线程),因此也可以通过调度器的InvokeAsync方法在当前UI线程中通过委托异步执行指定的任务。




2017-03-15
原文:private void btnStart_Click(......) { this.Dispatcher.InvokeAsync(() => MyMethodAsync("a")); } private async Task MyMethodAsync(string s){......} 这段代码和下面的代码实现的功能相同。 private void btnStart_Click(......) { Func t = () => Method1Async(); this.Dispatcher.InvokeAsync(t); }




2017-03-15
原文:这种方式的缺点是只能在 WPF 应用程序中使用它,而且实现异步过程中的同步比较困难,在WPF应用程序中同时独立执行多个没有关联的异步任务时,可以采用这种方式。




2017-03-15
原文:通过显式调用Task或Task的构造函数创建任务 当需要用单独的线程执行任务时,可通过显式调用Task或者Task的构造函数创建任务对象,再通过Start方法启动该对象。这种方式和直接用Thread创建线程的用法相似。例如: var taskA = new Task(() => Console.WriteLine("Hello from taskA.")); taskA.Start(); Console.WriteLine("Hello from the calling thread.");




2017-03-15
原文:Task类和Task类都有8个重载的构造函数,两者的用法非常相似,区别仅是用Task创建的任务没有返回值,用Task创建的任务有返回值。




2017-03-15
原文:(1)Task类的构造函数 Task类的构造函数主要的重载形式有两个。 一个是不传递数据对象的任务。 public Task( Action action, //要执行的不带返回值的任务 CancellationToken cancellationToken, //新任务将观察的取消操作 TaskCreationOptions creationOptions //自定义任务行为的可选项 ) 另一个是传递数据对象的任务。 public Task( Action action, //要执行的不带返回值的任务,任务包含一个输入参数 Object state, //该操作使用的数据的对象,类似于为线程池传递的state对象




2017-03-15
原文:CancellationToken cancellationToken, //新任务将观察的取消操作 TaskCreationOptions creationOptions //自定义任务行为的可选项 ) 其他重载都是这两个重载的简化形式,包括: Task(Action) Task(Action, CancellationToken) Task(Action, TaskCreationOptions) Task(Action, Object) Task(Action, Object, CancellationToken) Task(Action, Object, TaskCreationOptions) 在这些重载的构造函数中,如果不指定 CancellationToken,表示创建的是不可取消的任务;如果不指定TaskCreationOptions,表示使用默认行为。 (2)Task类的构造函数 Task类也有8个重载的构造函数,其形式与Task类的构造函数相似,重载形式有: Task(Func)




2017-03-15
原文:Task(Func, CancellationToken) Task(Func, TaskCreationOptions) Task(Func, CancellationToken, TaskCreationOptions) Task(Func, Object) Task(Func, Object, CancellationToken) Task(Func, Object, TaskCreationOptions) Task(Func, Object, CancellationToken, TaskCreationOptions) 关于TaskCreationOptions枚举和CancellationToken的含义及用法,后面还会有详细的介绍,这里只需要读者了解通过构造函数可设置这些参数即可。




2017-03-15
原文:.NET 框架引入的 CancellationTokenSource 类和 CancellationToken 结构用于协同实现多个线程、线程池工作项或 Task 对象的取消操作,其处理模式与派多人出差这个问题相似。对于 Task或者Task任务对象来说,创建的这个“短信群”就是CancellationTokenSource对象,对于出差人员来说,手机接收到的实际是发送者群发的通知,即CancellationToken对象。




2017-03-15
原文:2.CancellationTokenSource类和CancellationToken结构 System.Threading.CancellationTokenSource用于创建取消通知,称为取消源。 System.Threading.CancellationToken结构用于传播应取消操作的通知,称为取消令牌。 调用任务的代码在分配任务前,可先用CancellationTokenSource对象创建一个取消标记。例如: CancellationTokenSource cts = new CancellationTokenSource(); 如果希望在30秒后自动发出取消通知,可以用下面的代码实现。 var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); var ct = cts.Token; 在调用任务的代码中,可通过cts对象的Cancel方法发出取消通知,该方法会将取消标记ct的每个副本上的IsCancellationRequested属性都设置为true。 执行任务的方法接收到取消通知后,可以用以下方式之一终止操作。




2017-03-15
原文:(1)在任务代码中,简单地从委托中返回。这种实现方式类似于在调用任务的代码中用一个布尔型的字段表示取消通知,任务接收到通知后直接返回的情况。大多数情况下,这样做就可以了。但是,采用这种方式取消的任务状态返回值为TaskStatus.RanToCompletion枚举值(表示任务正常完成),而不是TaskStatus.Canceled枚举值。 在调用任务的代码中,可通过任务的Status属性获取任务状态。例如: if(taskA.Status == TaskStatus.RanToCompletion){......}




2017-03-15
原文:(2)在任务代码中,引发OperationCanceledException异常,并将其传递到在其上请求了取消的标记。采用这种方式取消的任务状态会转换为用Canceled枚举值表示的状态。调用任务的代码可使用该状态来验证任务是否响应了取消请求。 完成此操作的首选方式是在任务代码的内部调用ThrowIfCancellationRequested方法。例如: ct.ThrowIfCancellationRequested();




2017-03-15
原文:这种实现方式类似于Thread类提供的Abort方法,但是,在早期的实现中,调用Abort方法时可能又会出现异常,虽然利用try-catch可以捕获该异常,由于在异常处理的代码中又可能会导致新的异常,因此实际上真正在项目中编写代码的时候非常棘手,如果处理不好就可能会出现“假死机”现象。 CancellationTokenSource和CancellationToken就是为了解决这些问题而引入的。




2017-03-15
原文:在任务的生命周期内,可通过任务的Status属性获取任务执行的状态,当任务完成后,还可以通过任务属性获取任务完成的情况。 1.冷任务和热状态 用Task类或者Task类的构造函数显式创建的任务称为冷任务(ColdTask),冷任务必须通过Start方法来启动。例如: Task t1 = new Task(......); t1.Start(); 任务启动后(显式创建是在调用Start方法后启动,隐式创建是创建后默认启动),才开始其生命周期,此周期一直持续到释放任务占用的资源为止。 任务在生命周期内的执行情况称为热状态。




2017-03-15
原文:2.Status属性和TaskStatus枚举 在任务的生命周期内,可利用任务实例的Status属性获取任务执行的状态。任务执行的状态用TaskStatus枚举表示。TaskStatus的枚举值有: ● Created:该任务已初始化,但尚未进入调度计划。 ● WaitingForActivation:该任务已进入调度计划,正在等待被调度程序激活。 ● WaitingToRun:该任务已被调度程序激活,但尚未开始执行。 ● Running:该任务正在运行,但尚未完成。 ● RanToCompletion:该任务已成功完成。




2017-03-15
原文:● Canceled:该任务由于被取消而完成(任务自身引发 OperationCanceledException 异常,或者在该任务执行之前调用方已向该任务的CancellationToken发出了信号)。 ● Faulted:该任务因为出现未经处理的异常而完成。 ● WaitingForChildrenToComplete:该任务本身已完成,正等待附加的子任务完成。




2017-03-15
原文:任务完成情况相关的属性 当一个任务完成后,可直接通过任务实例获取任务完成的情况。 (1)IsCompleted属性 该属性表示任务是否完成。如果完成则为true,否则为false。注意该属性仅表示这个任务完成了,但是,该任务可能是正常完成,可能是因为被取消而完成,也可能是因为出现异常而完成。只有当任务的Status属性为TaskStatus.RanToCompletion枚举值时,才表示任务成功完成。 (2)IsCanceled属性 该属性表示任务是否因为被取消而完成。如果是则为true,否则为false。例如: Task t1 = Method1Async(); while (t1.IsCompleted == false) { await Task.Delay(100);




2017-03-15
原文:textBlock1.Text += "."; } await t1; (3)IsFaulted属性 任务是否因为出现未经处理的异常而完成,如果是则为true,否则为false。 当IsFaulted属性为true时,程序中可进一步通过Exception属性获取是什么异常导致任务完成。例如: if (t1.IsFaulted == true) { MessageBox.Show("任务因出现异常而完成,异常为:" + t1.Exception.Message); } (4)取消和完成之间的关系 取消(也叫终止)和完成之间关系是:取消是向任务传递一种信号,表示希望任务尽快结束。或者说,它表示的是希望任务立即停止,而不管该任务执行到什么情况。在任务代码中,可通过判断该信号决定是否结束正在执行的工作。但是,是否能正常结束任务,执行过程中是否出现异常,还要由代码的执行情况来决定。 完成是指该任务执行结束了,但是,因为什么情况而结束的,还要由任务执行的状态来决定。




2017-03-15
原文:报告任务执行的进度 有时候我们可能希望让某些异步操作提供进度通知,以便在界面中显示异步操作执行的进度,这种情况下,可以用Progress类报告任务执行的进度。 Progress类是通过IProgress接口来实现的,该类的声明方式如下: public class Progress : IProgress { public Progress(); public Progress(Action handler); protected virtual void OnReport(T value); public event EventHandler ProgressChanged; }




2017-03-15
原文:可以看出,Progress的实例公开了一个 ProgressChanged 事件,利用该事件即可获取异步操作报告的进度。




2017-03-15
原文:编写C/S应用程序时,有两种情况需要定时执行,一种是在服务器端定时执行某些任务,另一种是在客户端定时执行某些任务。 1.System.Timers.Timer类




2017-03-15
原文:在服务器端定时执行这些任务时,最简单的方式是使用System.Timers.Timer类来实现。




2017-03-15
原文:System.Timers.Timer类的常用属性和方法如下。 ● AutoReset属性:获取或设置一个布尔型的值,该值为true表示每次间隔结束时都引发一次Elapsed事件,false表示仅在首次间隔结束时引发一次该事件。 ● Interval属性:获取或设置两次Elapsed事件的间隔时间(以毫秒为单位)。该值必须大于零并小于或等于Int32.MaxValue,默认值为100毫秒。 ● Start方法:启动定时器。 ● Stop方法:停止计时器。




2017-03-15
原文:2.System.Windows.Threading.DispatcherTimer类 在客户端 WPF 应用程序中,对于需要与用户界面交互的任务,最简单的方式是使用System.Windows.Threading命名空间下的DispatcherTimer类来实现。这种定时器也是用时间模型来实现的,但它是在与当前线程关联的线程中定时执行任务,因此利用这种定时器可以直接获取或修改界面控件的属性。另外,该对象的定时效果没有System.Timers.Timer的定时精确,所以最好不要将其用于针对服务器的定时操作。




2017-03-15
原文:3.System.Threading.Timer类 System.Threading.Timer类也是在线程池中定时执行任务,它与其他两种计时器的区别是该类不使用事件模型,而是直接通过调用TimerCallback类型的委托来实现。 该类的构造函数语法为 public Timer( TimerCallback callback, //一个TimerCallback类型的委托,表示要执行的方法 Object state, //一个包含回调方法要使用的信息的对象,可以为null TimeSpan dueTime, //首次调用回调方法之前延迟的时间




2017-03-15
原文:量 TimeSpan period //每次调用回调方法的时间间隔。-1表示禁用定期终止 ) 利用该构造函数创建对象后,首次到达dueTime 延时时间时会自动调用一次callback 委托,以后每隔 period 时间间隔,都会自动调用一次callback委托。




2017-03-30
原文:任务并行库(TPL)的核心是Parallel类和PLINQ,这是编写并行程序的首选方案。 1.TPL的分类 任务并行库(TPL)主要包括两种类别:一种是数据并行,另一种是任务并行。另外,不论是数据并行还是任务并行,都可以用并行查询(PLINQ)提高数据查询的效率。 (1)数据并行 数据并行是指对源集合或数组中的元素同时执行相同操作的情况。 数据并行主要靠System.Threading.Tasks命名空间下的Parallel类提供的静态For方法和静态ForEach方法来实现。通过这两个方法,可以并行处理Func和Action委托。 (2)任务并行 任务并行主要靠Parallel类提供的静态Invoke方法来实现。




2017-03-30
原文:(3)并行查询 并行查询是指并行实现LINQ toObjects查询,简称PLINQ。 PLINQ与LINQ的主要区别是PLINQ尝试充分利用系统中的所有处理器(多核)来实现查询,其具体做法是:先将数据源分成多个片段,然后同时在多个处理器上对单独工作线程上的每个片段并行执行查询。在很多情况下,通过这种方式可显著提高查询的速度。 由于PLINQ超出了本书介绍的范围,因此不再阐述其用法。




2017-03-30
原文:2.TPL与传统多线程编程模型相比的优势 利用任务并行库(TPL)实现并行编程有以下优点。 (1)TPL编程模型使用CLR线程池执行多个任务,并能自动处理工作分区、线程调度和取消、状态管理以及其他低级别的细节操作。 (2)TPL还会动态地按比例调节并发程度,从而最有效地使用所有可用的处理器。 (3)TPL比Thread更具智能性,当它通过试探法来预判任务集不会从并行运行中获得性能优势时,还会自动选择按顺序运行。 总之,利用TPL,程序员不必考虑线程或线程池中执行的细节,开发人员只需要重点处理“做什么”即可。而在传统的多线程编程模型中,不但让开发人员处理“做什么”,而且还需要让开发人员自己去处理“如何做”时所涉及到的各种资源冲突和负载平衡等细节,此时如果开发人员经验不足,用传统的多线程模型编写出来的并行程序往往效率不高,而利用TPL来实现并行编程,既可以快速完成项目任务,又可以确保执行的效率。




2017-03-30
原文:PLINQ 和任务并行库(TPL)都提供了默认的分区程序,当用 PLINQ 编写并行查询或者用ForEach方法执行数据并行时,默认的分区程序将以透明方式工作,即对开发人员不可见。 对于开发人员来说,一般情况下,使用默认的分区程序就足够满足要求了。如果希望自己控制分区,还可以自定义分区程序,但前提是开发人员要能确保自定义分区比默认的分区程序执行效率高。 有以下几种分区方式。 1.按范围分区 按范围分区适用于已经知道长度的数据源。比如数组和其他已建立索引的源(如预先知道长度的IList集合)等,此时多个线程将协同并行处理原始源序列的不同片段,即每个线程都接收唯一的开始和结束索引,并行处理时不会覆盖其他线程或被其他线程覆盖。 按范围分区中涉及的唯一内存开销是创建范围的初始工作,之后不再需要其他同步。因此,只要平均划分工作负载,它就能够提供很好的性能。 按范围分区的缺点是:如果一个线程提前完成,而其他线程尚未完成,该线程也无法帮助这些未完




2017-03-30
原文:成的线程加快完成它们的工作。 2.按区块分区 对于长度未知的链接列表或其他集合,可以使用按区块分区。 在按区块分区中,并行循环或查询中的每个线程或任务都对一个区块中一定数量的源元素进行处理,然后返回检索其他元素。分区程序可确保分发所有元素,并且没有重复项。区块可为任意大小。只要区块不是太大,这种分区在本质上是负载平衡的,原因是为线程分配元素的操作不是预先确定的。但是,每次线程需要获取另一个区块时,分区程序都会产生同步开销。在这些情况下产生的同步量与区块的大小成反比。 一般情况下,按区块分区的速度在大多数情况下都比按范围分区速度快。只有当委托的执行时间较短或者中等,处理的数据源具有大量的元素,并且每个分区的总工作量大致相等时,按范围分区的速度才会较快。对于元素数量很少或者委托执行时间较长的源,则按区块分区和按范围分区的性能大致相等。 3.动态分区 TPL分区程序还支持动态数量的分区,即可以随时创建分区。比如当ForEach循环生成新任务时的情况即是如此。此功能使分区程序能够随循环本身一起缩放。 动态分区程序在本质上也是负载平衡的。当创建自定义分区程序时,必须支持可从 ForEach循环




2017-03-30
原文:中使用动态分区。 4.自定义分区程序 在某些情况下,可能值得或者甚至要求实现自定义的分区程序。例如有这样一个自定义集合类,根据对该类的内部结构的了解,确认能够采用比默认分区程序更有效的方式对其进行分区。或者,根据对在源集合中的不同位置处理元素所花费时间的了解,可能需要创建大小不同的范围分区。 若要创建自定义分区程序,可从 System.Collections.Concurrent.Partitioner派生一个类并重写虚方法。




2017-03-30
原文:Web Service Web Service也叫Web服务,根据数据交换格式的不同,Web Service又进一步分为XML Web Service和JSON Web Service等。 XML Web Service是一种以XML作为数据交换格式、部署在Web服务器上的一种应用程序服务。由于这种Web服务用XML描述通信消息,而XML是基于文本的,因此,不论什么操作系统,也不论什么设备,只要能连接到Internet,就能调用服务商(有偿或无偿)提供的服务。 以JSON 作为数据交换格式的Web 服务称为JSON Web Service,一般在Web 应用程序通过HTML和JavaScript调用这种服务。




2017-03-31
原文:(1)SOAP 由于Web服务是通过Web服务器发布的,因此客户端应用程序必须先通过某种协议(HTTP或SOAP)生成请求消息,然后将请求消息通过网络传递到服务器。Web服务对接收的信息进行处理后,再通过网络将结果返回到客户端应用程序。 SOAP(Simple Object Access Protocol,简单对象访问协议)是一种基于XML的、以HTTP作为基础传输协议的消息交换协议。SOAP实际上是一套规范,该规范定义了客户端与Web服务交换数据的格式,以及如何通过HTTP交换数据。 SOAP 包括 4 个主要部分:SOAP 消息、数据编码规则和统一模型、RPC(远程过程调用)消息交换模式、SOAP与HTTP之间的绑定。其中,SOAP消息是必需提供的部分,其他部分都是可选的。




2017-03-31
原文:(2)Web服务描述语言(WSDL) WSDL(Web Service Description Language,Web服务描述语言)用于描述Web服务提供的方法以及调用这些方法的各种方式,这是用XML文档来描述Web服务的标准。 WSDL为查找Web服务以及如何使用Web服务提供了有效的辅助手段,通过WSDL,可描述Web服务的3个基本属性。 (1)服务完成什么功能。即指出Web服务提供了哪些方法。 (2)如何访问服务。指出客户端和Web服务交互的数据格式以及必要的协议。 (3)服务位于何处。指出与 Web 服务所用协议相关的地址,如 URL、UDDI(Universal Description,Discovery and Integration)等。




2017-03-31
原文:2.客户端与Web服务通信的过程 客户端调用服务端提供的Web服务时,系统执行了一系列的操作,主要过程如下。 (1)客户端应用程序创建Web服务代理类的一个实例。 (2)客户端应用程序调用代理类的方法。 (3)客户端基础架构将Web服务所需要的参数序列化为SOAP消息,并通过网络将其发送给Web服务器。 (4)Web服务器接收SOAP消息并反序列化该XML,同时创建实现Web服务的实例,再调用Web服务提供的方法,并将反序列化后的XML作为参数传递给该方法。 (5)Web服务器执行Web服务提供的方法,得到返回值和各种输出参数。 (6)Web服务器将返回值和




2017-03-31
原文:输出参数序列化为SOAP消息,并通过网络将其返回给客户端基础架构。 (7)客户端基础架构接收返回的SOAP消息,将XML反序列化为返回值和输出参数,并将其传递给代理类的实例。 (8)客户端应用程序接收返回值和输出参数。




2017-03-31
原文:消息队列是发送和接收消息的公用存储空间,可以存在于内存中或者文件中。消息可以用两种方式发送:快速模式(Express)和可恢复模式(Recoverable),其区别在于,快速模式把消息保存在内存中,而不是保存在磁盘中;可恢复模式在传送过程的每个步骤中,都把消息写入磁盘中,以得到较好的故障恢复能力。消息队列可以放置在发送方或者接收方所在的计算机上,也可以单独放置在另外一台计算机上。由于消息队列在放置方式上的灵活性,形成了消息传送机制的可靠性。当保存消息队列的计算机发生故障而重新启动以后,以可恢复模式发送的消息仍然可以恢复到故障发生之前的状态,而以快速模式发送的消息则丢失了。另一方面,采用消息传递机制,发送方不必再担心接收方是否启动、是否发生故障等因素,只要消息成功发送出去,就可以认为处理完成。 MSMQ是微软实现的MQ。 采用 MSMQ 带来的好处是:由于是异步通信,无论是发送方还是接收方都不用等待对方返回成功消息,就可以继续执行其后面的代码,因此极大地提高了事务处理的能力。




2017-03-31
原文:在早期的网络应用编程模型中,不同应用程序之间的通信往往需要根据需求分别用不同的技术来实现,例如前面我们介绍的Web Service、远程处理、消息队列等都属于传统的技术解决方案。这些传统技术解决方案的问题在于:由于不同的软件公司对这些技术的实现各不相同,开发人员只好先选择一种开发工具和语言(例如.NET开发人员选择Visual Studio开发工具和C#语言,Java开发人员选择Eclipse开发工具和Java语言),然后再分别学习不同的实现技术,而这些不同的实现技术又相互抵触或互不兼容,从而给网络应用程序的开发带来了很大的负担。 SOA就是在此大环境下迅速发展起来的一种新架构,其基本思想就是希望用一种统一的、以“服务”为中心的模型来整合各种不同的技术,而不是仅仅限于Web服务。另外,早期的技术都是以“对象”为中心的,即都是面向对象技术的实现模式,而SOA则强调以“服务”为中心,而不是以“对象”为中心,这是它与面向对象技术的本质区别。 但是,SOA和其他标准或规范相似,它只是一种新的分布式框架设计规范,而不是具体实现,而符合规范的具体实现则仍然由各大软件公司来完成。 对于.NET开发人员来说,这个基于SOA的具体实现就是WCF。




2017-03-31
原文:WCF是微软公司推出的符合SOA思想的分布式应用程序技术框架和编程模型,是建立在消息通信这一概念基础之上的一个运行时服务系统。 WCF编程模型的目标是实现以下两个实体之间的通信:WCF服务端和WCF客户端。该编程模型封装在.NET框架的System.ServiceModel命名空间中。




2017-03-31
原文:1.WCF的特点 WCF的主要特点如下。 (1)以服务为中心 由于WCF是面向服务的体系结构(SOA)的一种具体实现,因此,在WCF中,所有服务都具有松耦合的特点。松耦合意味着只要符合基本协定,则在任何平台上创建的任何客户端应用程序都可以调用WCF服务,而不是仅仅限于.NET平台和Windows系列操作系统,比如用C#编写的WCF服务程序,在Linux平台上用C#、Java或者C++编写的客户端程序一样可以访问。 (2)支持多种消息交换模式 WCF 支持多种消息交换模式,其中最常用的模式是“请求-应答”模式(客户端发出服务请求,服务端回复服务执行情况)。除此之外,WCF还支持单向模式(客户端只发送消息而不期望服务端应答)、双工模式(客户端和服务端建立连接后,都可以单独向对方发送数据,类似于即时消息传递程序)以及自定义消息交换模式等。




2017-03-31
原文:(3)支持多种传输协议和编码方式 WCF可通过任意一种内置的网络传输协议和编码发送消息,其中最常用的协议和编码是使用HTTP发送文本编码的消息。此外,利用WCF还可以通过TCP、UDP、命名管道(实现本机进程间通信)或者消息队列(MSMQ)发送消息。这些消息可以编码为文本,也可以使用优化的二进制数据格式(传输图像、视频等)。 如果所提供的网络传输协议或编码方式都不符合设计要求,开发人员还可以自定义网络传输协议或编码。 (4)支持工作流、事务以及持久性的消息处理 WCF还支持对业务工作流进行处理(先将业务分为一个一个的活动任务,再按照业务流程依次对其处理),以及对事务进行处理(事务是指要么全部完成,要么回滚到原始点)。另外,WCF还支持持久性消息传输(不会由于通信中断而丢失消息,当通信中断恢复后,还可以继续发送未完成的消息)。




2017-03-31
原文:(5)统一性、安全性和可扩展性 统一性是指WCF整合了包括Web Service、.NET远程处理、.NET企业服务、消息队列(MSMQ)等微软早期就已经很优秀的.NET分布式编程技术,并以一种统一的协定为其他终结点提供服务。 在传统的编程模型中,开发人员只能根据不同的需求分别选择不同的模型或架构,由于这些模型之间的格式并不统一,当需要同时用多种技术来实现时,开发人员就只能自己去处理这些不同模型之间的整合问题。而用 WCF 来实现,只需要用一种模型就可以满足各种要求,而且格式统一,使用方便。 安全性是指WCF可对消息进行各种级别的加密处理,而且还可以对请求方进行身份验证(验证成功后才允许进行消息传递)。




2017-03-31
原文:2.终结点(EndPoint) 终结点(EndPoint)用于确定网络通信的目标,内部用EndPoint类来实现,在配置文件中用配置节来指定。 对于WCF来说,终结点由地址、协定和绑定组成,三者缺一不可。其中,地址(Address)用于公开服务的位置,协定(Contract)用于公开提供的是哪种具体服务。 将地址和协定绑定在一起,就构成WCF服务端的一个终结点。 例如,A 计算机是服务端,B 计算机是客户端,则在服务端配置文件(Web.config 或者App.config)和客户端配置文件(App.config)中都可以通过下面的方式指定 WCF 服务端的一个终结点。




2017-03-31
原文:在这段配置代码中,配置节中的localhost(即IPv4的127.0.0.1或者IPv6的::1)主要用于开发过程中的本机调试,部署应用程序时,还需要将其替换为实际的 IP 地址(例如218.205.75.191)或域名(例如www.cctv.com)。这样一来,客户端B知道WCF服务端A的终结点以后,就可以通过它访问服务端A提供的Service1服务了。 在服务端配置文件(Web.config)的终结点配置中,也可以不指定 address,此时服务端的终结点地址由系统根据其他配置节点的设置自动推断。




2017-03-31
原文:在一个WCF服务端项目中,可以通过配置文件(或者通过编写代码)同时公开多个终结点,这样一来,客户端就可以访问同一个服务端提供的不同服务。例如,银行可以为内部工作人员提供一个终结点(例如提供Service1服务),为外部客户提供另一个终结点(例如提供Service2服务),由于每个终结点都使用不同的地址、绑定和协定,因此服务端就可以在不同的类(Service1、Service2)中分别实现相应的功能。 对于客户端来说,由于上面这段代码中终结点配置的已经指定了采用的协议是HTTP,因此服务端只需要知道客户端的IP地址和端口号(IPEndPoint对象),就可以将信息返回给客户端。




2017-03-31
原文:(1)地址(Address) WCF中的地址用于确定终结点的位置。地址可以是URL、FTP、网络路径或本地路径。WCF规定的地址格式为: [传输协议]://[位置][:端口][/服务路径] 例如: http://www.mytest.com:50001/MyService http://localhost:8733/Design_Time_Addresses/MyService http://localhost:8080/MyService 第1行地址的含义为:域名为www.mytest.com的主机在端口50001正在等待客户端以HTTP方式进行通信,客户端访问的服务的路径为“MyService”。 第2行地址的含义为:域名为localhost的主机(即本地主机,IPv4为127.0.0.1,IPv6为::1)在端口 8733 正在等待客户端以 HTTP 方式进行通信,客户端访问的服务的路径为“Design_Time_Addresses/MathService”。注意路径中的Design_Time_Addresses表示自




2017-03-31
原文:动获取本机应用程序中提供服务的位置,这样一来,不论是否以管理员身份运行 VS2012,都可以正常访问本机提供的WCF服务。但是,该地址仅仅是为了方便调试程序,部署时应该将其替换为实际的地址。 第3行地址的含义为:域名为localhost的本地主机在端口8080正在等待客户端以HTTP方式进行通信调用,访问服务的路径为“MathService”。 WCF和传统的WebService相比,两者最大的区别是:WCF服务可以在各种不同的基础网络协议(例如TCP、UDP、HTTP等)之间传输,而传统的WebService仅能通过HTTP传输,无法使用其他基础网络协议。 下面的代码演示了客户端通过TCP访问WCF服务的地址格式: net.tcp://localhost:50001/MyService 这行代码中的net.tcp表示使用TCP传输协议,端口号为50001(如果不指明端口号,默认使用808端口)。 WCF客户端利用服务端的终结点地址确定WCF服务所在位置后,就可以自动生成客户端配置和客户端代理类,开发人员利用它生成的代理类,即可轻松调用WCF服务。




2017-03-31
原文:(2)绑定(Bingding) WCF通过绑定来定义WCF客户端与WCF服务通信的方式。 WCF 提供了多种绑定方式,包括标准绑定、系统提供的绑定以及自定义绑定。BasicHttpBinding类(在配置文件中用basicHttpBinding元素指定)只是系统提供的绑定之一,除此之外,系统提供的绑定还有 WSHttpBingding、NetTcpBinding、NetNamedPipeBinding、NetMsmqBinding等。本书后面的章节还会分别介绍这些绑定的含义及用法,这一章我们主要通过最基本的BasicHttpBinding学习WCF的基本用法。




2017-03-31
原文:不论是服务端还是客户端,一般都是在单独的配置文件(Web.config、App.config)中配置绑定。在后面的内容中,我们还会专门学习具体的绑定办法。




2017-03-31
原文:(3)协定(Contract) 协定表示客户端和服务端之间的信息交换规则,例如服务协定、数据协定、消息协定等。如果不指定协定,就无法在客户端和服务端之间进行通信。 协定在接口中用 Contract 特性来声明,内部用 ContractAttribute 类来实现,在配置文件中用Contract指定。 编写 WCF 服务端代码的主要工作就是用接口设计和实现服务,并利用协定公开服务操作,供客户端代码访问。




2017-03-31
原文:1.协定层 协定层用于在相互传递消息之前制定服务规则、数据交换规则、消息格式、安全策略、绑定的网络协议,以及消息采用的数据编码方式等。 2.服务运行时层 该层仅处理在服务实际运行期间发生的行为,包括限制消息处理的个数、服务出现内部错误时应采取的操作、控制是否提供元数据以及如何向外部提供元数据,指定可运行的服务实例的数目以及事务处理和调度行为等。 3.消息传递层 消息传递层描述数据的各种格式和交换模式。 消息传递层由通道组成。 通道是以某种方式对消息进行处理(例如通过对消息进行身份验证)的组件。一组通道也称为“通道堆栈”。通道对消息和消息头进行操作,这与服务运行时层不同,服务运行时层主要涉及对消息正文内容的处理。




2017-03-31
原文:4.激活和承载层 服务的最终形式为程序。该层负责激活或者承载WCF服务。




2017-03-31
原文:WCF 本身不能运行,只能将其“宿主”在某个可执行程序中(.dll 或者.exe)才能运行,因此,在学习WCF的具体设计方法之前,我们需要先了解有哪些承载WCF的方式。 1.利用IIS或者WAS承载WCF服务 承载WCF服务时,最常用的方式就是利用IIS(Internet Information Services,互联网信息服务)或者IIS(7.0以上版本)自带的WAS来承载。 (1)IIS和IISExpress IIS是微软公司推出的Web应用服务器。利用IIS提供的图形化管理界面,网络管理员无需记忆繁琐的服务器配置指令,就能够轻松搭建Web服务器。由于IIS宿主选项默认与ASP.NET集成在一起,因此能自动实现进程回收、空闲关闭、进程状况监视和基于消息的激活等功能。另外, IIS还提供了企业级服务器产品具有的集成可管理性。利用IIS,可以在服务器上发布多种服务,包括Web服务、FTP服务(文件传输)、NNTP服务(新闻服务)、SMTP服务(邮件收发)以及WCF服务等,并且还支持服务器集群等功能。




2017-03-31
原文:为了方便应用程序开发,随VS2012 开发工具一起提供的还有一个免费的IIS Express 8.0 版(Express版对并发用户数有限制),安装VS2012时,会自动安装IIS Express8.0。 利用IIS Express,可以在Windows7、Windows8等操作系统上搭建各种服务,例如部署WCF服务和搭建文件下载服务等。应用程序开发完成后,再将其部署在服务器操作系统(例如Windows Server2008、Windows Server2012)的IIS中即可。




2017-03-31
原文:(2)WAS WAS(Windows Process Activation Service,Windows进程激活服务)是从IIS7.0版开始提供的一个组件(IIS 7.0 以前的版本没有此组件)。利用 WAS,不需要将程序部署到 IIS 中,就可以承载并自动激活WCF服务,而且不需要开发人员编写任何承载代码。 无论是胖客户端桌面应用程序(如WPF应用程序、WinForm应用程序等)还是Web应用程序(如ASP.NET Web 应用程序、Silverlight应用程序等),都可以通过WAS承载WCF服务,并通过应用程序中的代码与WCF进行通信。




2017-03-31
原文:编写WCF服务程序时,可以利用【WCF服务库】模板将WCF服务制作成单独的DLL文件,调试程序时系统会自动将其宿主到WCF服务主机中来承载WCF服务,并利用Windows进程去自动激活它。或者说,WCF服务主机(WcfSvcHost.exe)的用途就是将WCF服务作为宿主环境,然后以Windows服务的方式运行它。




2017-03-31
原文:3.自承载 自承载WCF是指开发人员自己编写代码实现承载WCF的工作。比如在WPF应用程序中,启动程序时自动加载WCF服务,关闭时自动停止WCF服务;或者设计一个操作界面,让用户通过界面操作,根据需要随时启动或者停止WCF服务。 自承载这种方式本质上是利用Windows进程激活服务来承载WCF的,但是开发人员不是直接用WCF模板来实现承载工作,而是利用.NET框架公开的相关类去实现承载WCF的工作。




2017-03-31
原文:(1)WCF服务应用程序 在VS2012中,【WCF服务应用程序】模板生成的是一个扩展名为.dll的文件,该模板利用IIS自带的WAS承载和激活WCF服务。这种服务模式有点类似于传统的Web Service的服务模式,但是,虽然服务的形式看起来相似,而实际上其本质却是完全不同的。例如 Web Service 只能使用HTTP,而WCF除了可以使用HTTP以外,还可以使用TCP、UDP等传输协议。另外,WCF的编程模型和传统的Web Service也不相同。 用该模板创建WCF服务并编写实现代码后,就可以直接测试WCF服务,测试时系统会自动运行WCF 测试客户端(WcfTestClient.exe),开发人员利用它可直接测试服务端提供的每个方法。另外,当在同一个解决方案中既包括WCF服务应用程序项目又包括WCF客户端项目时,运行客户端程序时,该模板会利用WAS自动承载并激活WCF服务,而不需要开发人员编写任何承载代码。 该模板是使用HTTP作为传输通道时,创建WCF服务端应用程序的首选方式。




2017-03-31
原文:(2)WCF服务库 VS2012 中的【WCF 服务库】模板生成的也是一个扩展名为.dll 的文件。但是该模板是利用WCF服务主机(WcfSvcHost.exe)承载并激活WCF服务的。另外,用该模板创建WCF服务后,模板也会自动运行WCF测试客户端(WcfTestClient.exe),开发人员利用它也可以直接测试WCF服务。 但是,由于用该模板设计的WCF服务部署后无法自动读取App.config配置文件,因此它仅适用于需要长时间(开机运行)WCF服务的场合。换言之,当需要本机提供WCF服务而不是在服务器上部署WCF服务时,一般用自承载来实现比较方便,而不是用WCF服务库来实现。




2017-03-31
原文:(3)其他模板 除了前面介绍的模板外,VS2012还提供了其他创建和承载WCF的模板,例如【WCF工作流服务】模板、【WCF联合服务库】模板等




2017-03-31
原文:2.编写WCF服务端程序的主要步骤 编写WCF服务端程序时,有4个主要的步骤:选择承载方式、设计和实现协定、配置服务、承载服务。 (1)选择承载方式 编写HTTP应用程序时,一般选择【WCF服务应用程序】模板承载WCF服务,由于在这种方式中承载工作是自动完成的,因此不需要程序员编写任何承载代码,此时开发人员只需要做两件事:设计和实现协定、在Web.config文件中配置服务。 编写TCP应用程序时,既可以选择自承载方式,也可以选择【WCF服务应用程序】模板承载WCF服务。 编写UDP应用程序时,既可以用自承载方式来实现,也可以用标准绑定来实现。 (2)设计和实现协定 有两种设计和实现协定的方式。 第1种方式是用一个接口公开多个方法(每个方法都称为一个操作),再用一个类实现接口中声明的所有方法,这是建议的做法。这




2017-03-31
原文:种方式的优点是可以用接口实现多继承,另外,当修改接口的实现时,只要接口声明不变,就不需要客户端做任何改动。还有,如果升级了版本,而且希望保存原来的接口实现,只需要在接口中增加新的方法声明即可。 第 2 种方式是不使用接口,而是全部用类来实现,例如直接在类和方法的上面同时用ServiceContract 特性和 OperationContract 特性声明协定。这种办法的优点是简单、直观,缺点是由于托管类不支持多继承,因此每个类只能实现一个服务协定,或者说有多少个类就不得不公开多少个服务协定。另外,采用这种方式时,服务端修改了任何一行代码,客户端代码也必须做相应修改,否则调用就可能失败,在实际项目中用起来非常不方便。 因此,在实际项目中,强烈建议用第1种方式来实现。




2017-03-31
原文:(3)配置服务 配置服务也有两种方式,第1种方式通过修改配置文件(Web.config或者App.config)来实现;第2种方式开发人员自己编写代码来实现。




2017-03-31
原文:(4)承载服务 服务端设计完成后,运行(承载)服务即可,此时客户端就可以和服务端交互了。 自承载 WCF 时,在开发的初始阶段,服务端程序一般用控制台应用程序来实现,调试完成后,再将实现代码复制到实际的项目中。这是因为用控制台应用程序实现时,可以暂不考虑界面设计的工作,此时开发人员的主要精力是集中在服务代码的实现上。但是,在实际项目中,一般用其他应用程序来实现承载工作。




2017-03-31
原文:2.编写WCF客户端程序的主要步骤 编写WCF客户端程序的主要设计步骤如下。 (1)利用服务端配置生成客户端代理类和客户端配置 在客户端应用程序中,运行 WCF 服务后,客户端可通过【添加服务引用】的办法,让系统自动生成客户端代理类,此时它会根据服务端配置(Web.config 或者 App.config)自动修改客户端配置(App.config)。 开发客户端程序时,一般不需要手动修改App.config文件的内容,但是,如果理解这个文件中各节点的含义,对开发会很有帮助。




2017-03-31
原文:(3)更新客户端配置 如果服务端配置文件(Web.config 或者 App.config)发生了改变,或者接口发生了改变,此时需要在客户端更新服务引用,以便让系统重新生成新的客户端配置(App.config)。但是,也可能不需要这样做,这要看服务端配置是否修改了绑定方式。 当服务端的配置文件修改后,如果客户端更新服务引用后仍然无法正常访问,此时删除已经添加的服务引用,再重新添加服务引用即可,这种方式最简单,也不容易出错。




2017-03-31
原文:(2)创建服务端 在【解决方案资源管理器】中,鼠标右键单击解决方案名,选择【添加】→【新建项】命令,在弹出的窗口中选择【WCF服务应用程序】模板,将项目名改为WcfService,单击【确定】按钮。此时系统会自动添加对System.ServiceModel命名空间的引用,并自动在该项目中生成以下文件。 ● IService1.cs:用接口定义服务协定和数据协定。 ● Service1.svc和Service1.svc.cs:实现IService1接口中声明的服务操作。 ● Web.config:服务端配置文件,用于定义服务行为以及绑定的协议等。 在自动生成的这些文件中,还自动包含了简单的示例代码。




2017-03-31
原文:(3)添加新服务(可选) 如果不希望使用默认的WCF服务Service1,可以利用【重构】将其替换为其他名称,或者删除 IService1.cs、IService1.svc 以及 IService1.svc.cs,然后按照下面的办法创建或添加新服务。鼠标右键单击项目名,选择【添加】→【WCF服务】命令,在弹出的窗口中将【项名称】改为新的WCF 服务名称(例如 MyService),单击【确定】按钮。此时系统会自动添加 IMyService.cs、IMyService.svc以及IMyService.svc.cs文件。 在同一个服务端项目中,既可以只有一个服务,也可以同时包含多个服务。




2017-03-31
原文:2.编写服务端代码 编写服务端代码的主要工作就是设计和实现协定,包括服务协定、数据协定以及消息协定。后面我们还会详细介绍这些协定,这里只需要重点关注如何实现服务协定即可。 服务协定用ServiceContract特性来声明(ServiceContractAttribute类),用接口来定义。在接口内部,用方法及其参数声明服务提供的操作,在接口中声明的这些方法统称为“操作方法”。注意在服务协定中只能声明操作方法,不允许声明属性和字段。 (1)定义接口 将IService1.cs改为下面的内容。 [ServiceContract] public interface IService1 { [OperationContract] string SayHello(string name); [OperationContract] double Add(double d1, double d2); [OperationContract] double Divide(double d1, double d2); }




2017-03-31
原文:(2)实现接口中声明的操作方法 在Service1.svc. cs中添加下面的代码。 public class Service1 : IService1 { public string SayHello(string name) { return string.Format("Hello, {0}", name); } public double Add(double d1, double d2) { return d1 + d2; } public double Divide(double d1, double d2) { return d1 / d2; } } 代码编写完毕后,鼠标右键单击WcfService项目名,选择【生成】或者【重新生成】命令,确保没有语法错误。




2017-03-31
原文:(2)开通调试功能 在Web.config中找到includeExceptionDetailInFaults,将其改为true。 这一步是为了调试客户端程序时,当服务端出现任何运行错误时,都能将服务端程序的详细出错信息反馈到客户端。但是,一定要记住,部署时必须将其再改为false,以免泄漏内部代码的实现细节而受到黑客的青睐和恶意攻击。




2017-03-31
原文:4.测试服务(可选) 用【WCF应用程序】模板创建WCF服务后,如果需要,可以直接利用【WCF测试客户端】来测试每个服务操作。由于这个例子比较简单,所以可以直接进行测试。但是,随着功能实现越来越多,也可能会在修改服务端配置后再次通过这一步来测试。 测试服务的具体办法如下。 (1)选中要测试的文件名 鼠标右键单击 WcfService 项目,选择【设为启动项目】命令,并选中要测试的 Service1.svc文件名(测试哪个服务就选中哪个.svc文件),然后按键调试运行,此时系统会自动弹出WCF测试客户端




2017-04-01
原文:5.在客户端添加服务引用 创建并测试了 WCF 服务以后,首先需要在客户端应用程序中添加服务引用,以便让其自动生成WCF客户端代理类,以及让其自动更新客户端配置文件(App.config)。 添加服务引用的步骤如下。 (1)确保服务已经启动 鼠标右键单击WcfService项目,选择【生成】或者【重新生成】命令。此步骤是为了确保系统能自动运行成功生成的WCF服务。 如果WCF服务没有启动,添加服务引用会失败。 (2)查找引用的服务 鼠标右键单击Client项目中的【引用】→【添加服务引用】命令,在弹出的对话框中,单击【发现】按钮,此时它会自动找到与该项目在同一个解决方案中的WCF服务,如图7-5所示。注意地址中的端口号(1241)是系统自动生成的,添加服务引用时,端口号和该例子中的端口号不一定相同。




2017-04-01
原文:如果用单独的解决方案创建WCF服务,需要直接在地址栏中输入服务引用的地址,例如输入 http://localhost:1241/Service1.svc,然后单击【转到】按钮,即可将该服务引用添加到客户端项目中。 添加服务引用后,系统会自动在指定的命名空间中(本例子为 ServiceReference1)生成客户端代理类(Service1Client),以后就可以利用它在客户端应用程序中调用WCF提供的服务了。 另外,在客户端的App.config文件中,它还会自动根据服务端配置生成对应的客户端配置,读者可打开该文件查看添加了哪些内容。




2017-04-01
原文:编写 WCF 服务应用程序和客户端应用程序时,如果修改了服务端接口的声明,在客户端应用程序中引用的服务并不会自动更新,此时必须手动更新WCF服务。 更新 WCF 服务的办法是:在【解决方案资源管理器】中鼠标右键单击客户端项目中引用的服务(例如ServiceReference1),在弹出的快捷菜单中选择【更新服务引用】命令,此时系统会自动更新客户端的App.config和客户端代理类。 如果服务端提供的接口声明不变,或者接口中声明的协定没有发生变化,而只是修改了实现接口的代码,不需要此步骤。




2017-04-01
原文:服务协定 服务协定是指WCF对客户端公开哪些服务。WCF服务端通过服务协定向客户端公开以下内容:操作方法、消息交换模式、采用的通信协议以及序列化格式。 1.ServiceContract特性 ServiceContract特性用于在应用程序中定义服务协定,该特性的常用属性如下。 ● CallbackContract:获取或设置双工通信的回调协定类型,默认为null。 ● Name 和Namespace:获取或设置Web 服务描述语言(WSDL)中元素的名称和命名空间。 ● HasProtectionLevel:获取一个 bool 类型的值,该值指示是否对成员分配了保护级别。如果分配了保护级别(非None)则




2017-04-01
原文:为true,否则为false。 ● ProtectionLevel:设置绑定支持的保护级别,默认值为ProtectionLevel.None。可选择的值有:EncryptAndSign(对数据进行加密和签名确保所传输数据的保密性和完整性)、None(仅身份验证)、Sign(对数据签名确保所传输数据的完整性)。 ● SessionMode:获取或设置采用的会话模式。 默认情况下,Name和Namespace属性分别是协定类型的名称和http://tempuri.org。如果没有显式声明服务协定的名称和命名空间,由于重构代码时可能会破坏协定,这样一来客户端有时候可能就不得不删除已有的服务引用,然后再重新添加服务引用。因此,在实际项目中,强烈建议设置ProtectionLevel的值,但在学习时可暂时不管它。 由于ProtectionLevel的默认值为None,学习编写代码时不考虑该属性没有任何问题,但在实际项目中,如果没有保密性和安全性控制,可能会导致很严重的消息泄密问题。将其设置为服务协定所要求的级别的好处是可在运行时验证配置文件是否配置了相应级别的安全保密措施。




2017-04-01
原文:2.OperationContract特性 该特性类用于在应用程序中定义操作协定,常用属性如下。 ● IsOneWay:获取或设置是否不应答消息,默认为false(返回应答的消息)。 ● IsInitiating:获取或设置一个布尔值,该值指示接口中的方法是否在服务端启动会话时就可以实现操作,默认为true。 IsOneWay属性的用法在后面的例子中我们还会学习,这里先举一个IsInitiating属性用法的例子。假设接口中的很多方法都是根据订单ID号来操作的,此时可将GetOrderId方法的IsInitiating设置为true,并将所有其他的方法都设置为false。这样就可以确保执行其他方法之前,每个新的客户端都能获得一个正确的订单ID号。




2017-04-01
原文:此外,使用消息协定还可以对消息进行更多的控制。比如,可以决定哪些信息段包含在消息正文中,哪些信息段包含在消息头中。 1.MessageHeader特性和MessageBodyMember特性 在消息协定的内部,通过MessageHeader特性(MessageHeaderAttribute类)指定消息头,通过MessageBodyMember特性(MessageBodyMemberAttribute类)指定消息体。 可以对所有字段、属性和事件应用MessageHeader特性和MessageBodyMember特性,而与这些字段、属性和事件的访问修饰符无关,即不论是public、private、protected还是internal,都能使用这两个特性。 如果一个类型中既包含消息协定又包含数据协定,则只处理消息协定。 2.以Rpc样式对消息进行操作 默认情况下,WCF通过远程过程调用(RPC)对消息协定进行操作,这种操作称为 RPC样式的操作,这是建议的做法。 在RPC样式的操作中,消息协定中操作协定的声明办法和服务协定中操作协定的声明办法完全相同,也就是说,都可以使用多个参数,包括带ref和out的参数。这样做的好处是即使开发人员对




2017-04-01
原文:SOAP和SOAP消息不熟悉,一样能快速创建WCF服务应用程序。




2017-04-01
原文:用【WCF服务应用程序】模板创建WCF服务项目时,服务端配置信息保存在项目的Web.config文件中,客户端应用程序中的配置信息保存在项目的App.config文件中。 用自承载WCF创建WCF服务项目时,或者用【WCF服务库】模板创建WCF服务项目时,服务端和客户端的配置信息都保存在对应项目的App.config文件中。 对于终结点配置来说,通过配置文件(Web.config、App.config)配置终结点是建议的做法,这是因为部署前开发人员没有办法预先知道实际使用的服务地址(只有在实际部署应用程序时才会知道)。用配置文件的好处是更改配置信息后,不必重新编译和重新部署应用程序,就可以自动使




2017-04-01
原文:用新的终结点配置。或者说,无论是部署前还是部署后,都可以单独修改配置文件指定终结点,而不需要修改程序中的代码。 用 VS2012 开发 WCF 服务端和客户端应用程序时,在程序的开发阶段,只需要在服务端的Web.config文件中配置服务即可,这是因为服务端配置完成后,在客户端添加或更新服务引用时,系统会自动修改对应的客户端配置文件(App.config),不需要程序员再去手动配置客户端。但是,部署后仍然需要根据实际的服务地址修改客户端配置,该工作既可以通过直接修改配置文件来实现,也可以通过单独设计的配置界面来实现。




2017-04-01
原文:有两种修改服务端Web.config(WCF服务应用程序)或者App.config(WCF服务库或者自承载)配置文件的办法,一种是在编辑状态下直接修改配置,另一种是通过配置工具提供的选项来选择修改。 (1)直接修改配置 修改服务端配置文件最方便的办法是直接打开并修改它,办法是:双击 Web.config 或者App.config打开配置文件,当编辑配置信息时,系统还会自动显示智能提示。 另外,在VS2012开发环境下,服务端和客户端终结点的配置与VS2010相比进行了大幅度的简化,很多默认值不再需要开发人员逐个指定,只需要指定与默认值不同的配置即可。还有,利用系统提供的绑定,只需要修改很少的配置代码,就可以在同一个配置文件中实现多种绑定配置。 由于Web.config中的服务绑定配置完成后,在客户端添加服务引用时,系统会自动修改App.config文件中对应的客户端绑定配置,因此本书后续章节的例子主要介绍服务端Web.config文件的配置方法。 (2)利用配置工具修改配置




2017-04-01
原文:在开发过程中,除了直接修改配置文件外,还可以利用配置工具( Svcutil.exe ,或者叫ServiceModel元数据实用工具)配置和查看各个参数的具体值。例如,在【解决方案资源管理器】中右键单击Web.config文件,选择【编辑WCF配置】命令,此时即可以通过配置窗体查看或修改各个属性的值




2017-04-01
原文:绑定可指定终结点之间通话时所使用的通信机制,并指示如何从一个终结点连接到另一个终结点。绑定元素有:协议通道绑定元素、传输通道绑定元素和消息编码绑定元素。 WCF提供了两种类型的通道:协议通道和传输通道。 1.协议通道绑定元素(绑定消息处理协议) 协议通道绑定元素用于确定绑定到哪种消息处理协议(WS-Security 或者 WS-Reliability)来确保消息的安全性和可靠性。另外,该通道还处理自定义的消息处理协议。 WS是WebService的缩写。 (1)安全性(Security) 消息的安全性(security)是指消息的安全处理方式,即如何保护传输通道使其满足安全要求,也叫WS-Security。 WS-Security是一种在Web服务上满足安全要求的网络传输协议。2004年,OASIS组织发布了WS-Security标准的1.0版本,2006年发布了1.1版本。 安全处理方式是指采用哪种安全处理机制,例如,Windows身份验证、用户名密码身份验证、




2017-04-01
原文:SSL、SOAP等。 在下的节中,可使用mode特性配置消息的安全性(security),该特性的可选值包括以下几种。 ● None:指不保护SOAP消息且不验证客户端的身份。 ● Transport:在传输层上满足安全要求。 ● Message:在消息层上满足安全要求。 ● 混合(TransportWithMessageCredential或者TransportCredentialOnly):安全性声明包含在消息中,而完整性和保密性要求则由传输层来满足。 例如: ......




2017-04-01
原文:......
......
(2)可靠性(WS-Reliability) 消息的可靠性是指 WCF 通过传输通道传输消息的过程中,确保消息到达目的地而不会丢失消息,也叫WS-Reliability。比如,传输过程中网络中断了,当网络恢复后,该通道会自动再次发送该消息,不会出现丢失消息的情况。 WS-Reliability是一种基于SOAP消息的传输协议,2007年OASIS组织发布了WS-Reliability标准的1.0版本,2009年发布了1.2版本。 (3)事务 事务是指某个通信过程要么全部完成,要么回滚到未通信前的初始状态,但绝不会出现半途而废的情况。 (4)双工(Duplex) 可指定是否支持双工通信。 (5)传输方式(TransferMode) 传输方式指传入和传出消息时使用的数据流采用哪种机制。例如,发送消息时,如果直接发送,则称为流式的;如果是先保存到某个缓冲区中,等缓冲区满时再发送,则称为缓冲式的。 使用 TransferMode 可设置




2017-04-01
原文:传输方式,允许的值包括:Buffered(请求消息和响应消息都是缓冲式的)、Streamed(请求消息和响应消息都是流式的)、StreamedRequest(请求消息是流式的,而响应消息是缓冲式的)、StreamedResponse(请求消息是缓冲式的,而响应消息是流式的)。 传输方式对通信的影响比较复杂,一般情况下不要在配置中修改其默认值。




2017-04-01
原文:3.消息编码绑定元素 消息编码绑定元素指定对发送到终结点的消息使用的消息编码。 可指定的消息编码方式包括以下几种。 ● Text:指采用哪种文本编码方式,如utf-8、Unicode等。 ● Binary:二进制格式。 ● MTOM:消息传输优化机制,这是一种对SOAP信封上下文中二进制XML元素高效编码的方法。 Text(文本消息编码器)是所有基于 HTTP 绑定的默认编码器,这是最关注互操作性的所有自定义绑定的最佳选择。此编码器读取和编写标准SOAP 1.1/SOAP 1.2文本消息,而不会对二进制数据进行任何特殊处理。如果消息的MessageVersion设置为None,则SOAP信封包装会从输出中省略,只有消息正文内容会进行序列化。 Binary(二进制消息编码器,一般用于 TCP 绑定):当通信双方(服务端、客户端)都基于WCF时,二进制消息编码器始终是最佳的选择。这种编码器使




2017-04-01
原文:用.NET二进制 XML格式,该格式与等效的 XML 1.0 表示法相比产生的需求量通常较小,并将二进制数据编码为字节流。如果WCF客户端不存在互操作性要求,而且使用HTTP传输时,也可以采用这种消息编码绑定方式。 MTOM 消息编码器也是一个文本编码器,但这种编码器实现对二进制数据的特殊处理,默认情况下在任何标准绑定中都不会使用 MTOM,它仅用于严格按某种具体情况进行优化的情况。换言之,只有当二进制数据的量不超过某个阈值时,MTOM 编码才具有优势;如果消息包含的二进制数据超过了这个阈值,则这些数据会外部化到消息信封之后的MIME部分。具体来说,当使用HTTP传输并要求互操作性,并且还必须发送大型二进制数据时(比如超过 500MB 的二进制数据),可考虑在标准BasicHttpBinding或WSHttpBinding绑定上启用MTOM。启用办法是:将该绑定的MessageEncoding属性设置为Mtom。但是要注意,如果传输的数据量低于1KB,采用这种方式反而会大大降低传输性能。




2017-04-01
原文:HTTP应用编程的技术选择 编写基于C/S的HTTP应用程序时,有以下几种实现技术。 1.利用可插接式协议实现HTTP应用编程 可插接式协议用WebRequest类和WebResponse类来实现。这两个类是各种与具体的通信协议相关的类的基类,提供了上传、下载等基本方法。或者说,不论采用的是哪种通信协议,都可以用这两个对来实现。 2.利用HttpWebRequest和HttpWebResponse实现HTTP应用编程 HttpWebRequest 类和 HttpWebResponse 类是针对 HTTP 提供的,分别从 WebRequest 类和WebResponse类继承而来。




2017-04-01
原文:3.利用WCF实现HTTP应用编程 前两种方式都是传统的编程模型,在实际项目中,如果用传统的编程模型来实现,除了业务处理之外,很多细节也都需要程序员自己去完成,例如负载平衡、网络监视、安全管理、防范攻击等。而用WCF来实现,程序员只需要处理业务逻辑即可,其他工作让WCF内部去完成就行了。因此,用WCF和基于任务的编程模型实现HTTP应用编程是建议的做法。




2017-04-16
原文:客户端与服务端建立连接后,发送方需要先将要发送的数据转换为字节流发送到内存的发送缓冲区中,TCP会自动从发送缓冲区中取出一定数量的数据,将其组成TCP报文




2017-04-16
原文:段逐个发送到IP层,再通过网卡将其发送出去。 接收端从IP层接收到TCP报文段后,将其暂时保存在接收缓冲区中,这时程序员就可以通过程序依次读取接收缓冲区中的数据,从而达到相互通信的目的。




2017-04-16
原文:有以下几种编写TCP应用程序的技术。 1.用Socket类实现 如果程序员希望 TCP 通信过程中的所有细节全部通过自己编写的程序来控制,可以直接用System.Net.Sockets命名空间下的Socket类来实现,这种方式最灵活,无论是标准TCP协议,还是自定义的新协议,都可以用它去实现。但是,用Socket类实现时,需要程序员编写的代码也最多。这就像设计一座大楼,设计人员除了规划大楼和各楼层以及各房间的样式以外,水泥、钢铁、玻璃、砖块等所有材料也都需要自己去制造,或者说,这些材料的制造细节也都需要设计人员自己去考虑。 除非我们准备定义一些新的协议或者对底层的细节进行更灵活的控制,否则,一般不需要用Socket类去实现,而是使用对Socket进一步封装后的TcpListener类、TcpClient类以及UdpClient类来实现。这主要是因为用Socket编写程序比较复杂,容易出错。 2.用TcpClient和TcpListener以及多线程实现




2017-04-16
原文:System.Net.Sockets命名空间下的TcpClient和TcpListener类是.NET框架对Socket进一步封装后的类,这是一种粗粒度的封装,在一定程度上简化了用 Socket 编写 TCP 程序的难度,但灵活性也受到一定的限制(只能用这两个类编写标准TCP应用程序,不能用它编写其他协议和自定义的新协议程序)。另外,TCP 数据传输过程中的监听和通信细节(比如消息边界问题)仍然需要程序员自己通过代码去解决。这就像设计大楼时,盖楼用的砖块、玻璃、钢铁都可以直接用,不需要再考虑这些材料的制造细节,但是,砖块、玻璃、钢铁都只有标准大小,而不是各种大小各种形状的都可以提供。




2017-04-16
原文:3.用TcpClient和TcpListener以及多任务实现 用TcpClient和TcpListener以及基于任务的编程模型




2017-04-16
原文:编写TCP应用程序时,不需要开发人员考虑多线程创建、管理以及负载平衡等实现细节,只需要将多线程看作是多个任务来实现即可。 4.用WCF实现 用WCF编写TCP应用程序时,监听和无消息边界等问题均由WCF内部自动完成,程序员只需要考虑传输过程中的业务逻辑即可,而不需要再去考虑TCP通信过程中如何监听,也不需要考虑如何解决无消息边界等细节问题。另外,利用WCF还可以实现自定义的协议。 总之,从顶层的业务逻辑控制到底层的套接字实现,无论程序员希望对细节控制到什么程度,都可以用WCF提供的相关类去完成。




2017-04-16
原文:有以下几种解决TCP消息边界问题的办法。 (1)发送固定长度的消息 这种办法适用于消息长度固定的场合。具体实现时,可通过 System.IO 命名空间下的BinaryReader对象每次向网络流发送一个固定长度的数据。例如,每次发送一个int类型的32位整数。BinaryReader 和 BinaryWriter 对象提供了多种重载方法,利用它发送和接收具有固定长度类型的数据非常方便,而且可以发送任何类型的数据。例如: TcpClient client = new TcpClient("www.abcd.com", 51888); NetworkStream networkStream = client.GetStream( ); BinaryWriter bw = new BinaryWriter(networkStream, Encoding.UTF8); bw.Write(35); (2)将消息长度与消息一起发送




2017-04-16
原文:这种办法一般在每次发送的消息前面用4个字节表明本次消息的长度,然后将其和消息一起发送到对方;对方接收到消息后,首先从前4个字节获取实际的消息长度,再根据消息长度值依次接收发送方发送的数据。这种办法适用于任何场合,而且一样可以利用 BinaryReader 对象和BinaryWriter对象方便地实现。 BinaryWriter对象提供了很多重载的Write方法。例如,向网络流写入字符串时,该方法会自动计算出字符串占用的字节数,并使用4个字节作为前缀将其附加到字符串的前面;接收方使用BinaryReader对象的ReadString方法接收字符串时,会首先自动读取字符串前缀,并自动根据字符串前缀读取指定长度的字符串。 (3)使用特殊标记分隔消息 这种办法主要用于消息本身不包含特殊标记的场合。 对于字符串处理来说,用这种办法最方便的途径就是StreamWriter对象和StreamReader对象。发送方每次用 StreamWriter 对象的 WriteLine 方法将发送的字符串写入网络流,接收方每次用StreamReader对象的ReadLine方法将




2017-04-16
原文:以回车换行作为分隔符的字符串从网络流中读出。 总之,解决TCP消息边界问题的办法各有优缺点,编程时应该根据实际情况选择一种合适的解决方式。




2017-04-16
原文:WCF与TCP相关的绑定 利用WCF编写TCP应用程序时,只需要在服务端配置文件中设置相关的绑定,就可以轻松实现相应的功能,而且不容易出错。 1.NetTcpBinding NetTcpBinding类用于将WCF和TCP绑定在一起,并以服务的形式提供TCP服务端和客户端之间的通信。 在服务端配置文件中,用元素来配置,默认配置如下。 ● 安全模式:Transport(保证传输安全)。 ● 消息编码方式:Binary(采用二进制消息编码器)。 ● 传输协议:TCP。 ● 会话:提供传输会话(也可以配置为可靠对话)。 ● 事务:无事务处理功能(也可以配置为支持事务处




2017-04-16
原文:理)。 ● 双工:支持。 既然绑定的是TCP,为什么还要加一个Net前缀呢?这是因为WCF有一个约定:凡是具有Net 前缀的绑定默认都使用二进制编码器对消息进行编码,而不带 Net 前缀的绑定则默认使用文本消息编码。 由于这种绑定的默认配置比WSHttpBinding和WSDualHttpBinding提供的配置传输速度更快,因此特别适用于双方都是基于WCF的通信,比如企业内部网之间的通信。




2017-04-16
原文:1.队列 队列的用处是捕获和传送通信双方之间交换的消息,同时还可以通过延期处理来隔离出现故障的任意一方。 有两种类型的队列,一种是临时性的非事务队列(Nontransactional Volatile Queues),这种队列将消息保存在内存中,不使用事务来保护对消息的操作。使用这种队列时,一旦服务器发生问题,或者调用方出现异常,消息就会丢失。 另一种是永久性的事务队列(Transactional Queue),




2017-04-16
原文:这种队列将消息保存在磁盘中,当服务器关机、重启或崩溃时,消息仍可以在系统恢复后被读取出来。同时,消息发布、获取和删除都在环境事务范围内操作,从而确保了消息的可靠性。 实际上,队列是一个分布式概念。它可以是任意一方的本地队列(称为传输队列),也可以是通信目标的远程队列(称为目标队列)。 所有队列都是靠队列管理器来管理的。 客户端将消息发送至队列时,队列管理器会将该消息定址到由服务队列管理器管理的目标队列。客户端上的队列管理器将消息发送至传输队列,然后队列管理器查找一个拥有目标队列的队列管理器的路径并将消息可靠地传输给它。 目标队列管理器用于接收并存储到目标队列的消息。当 WCF 服务发出从目标队列读取的请求时,目标队列管理器就会自动将消息传送至目标应用程序




2017-04-16
原文:MSMQ利用队列将源和目标相分离。在MSMQ中,队列可以是事务性的(不会丢失消息),也可以是非事务性的(可能会丢失消息)。对于事务性的队列,由于MSMQ的队列管理器实现了可靠的消息传输协议,因此它能确保不会在传输过程中丢失消息。 由于 MSMQ 能自动将消息缓存起来,因此,利用消息队列实现不同计算机之间的通信时,先启动服务端还是先启动客户端都无所谓,既不要求对方一直处于监听状态,而且也不要求发送方发送消息的速率和接收方接收消息的速率相匹配。 MSMQ是作为可选组件随Windows操作系统一起提供的,并作为NT服务运行,使用MSMQ的前提是要确保MSMQ能正常运行。




2017-04-16
原文:WCF与MSMQ相关的绑定最常用的是NetMsmqBinding类,在配置文件中对应的绑定元素为。该绑定在传输层提供队列支持,并且为松耦合应用程序、故障隔离、负载均衡和断开连接的操作提供支持。 大部分情况下,使用NetMsmqBinding就足以满足各种业务要求,而且还能提高最佳的功能。当在不同计算机上通过消息队列实现WCF应用程序之间的通信时,使用这种绑定非常方便。 将WCF和NetMsmqBinding绑定后,客户端和服务端通过队列交换消息时,从开发人员的实现代码上来看,其用法和其他绑定的用法基本上没有什么大的区别,从而极大地简化了消息队列编程的复杂度。 但是,从内部实现来看,客户端实际上是先通过 WCF 将消息发送到本机的传输队列(不要求服务端保持运行),当服务端运行时,客户端的MSMQ再将消息从本机的传输队列发送到服务端的目标队列,服务端再通过 WCF 服务读




2017-04-16
原文:取目标队列的消息,由于此过程全部是自动完成的,因此不需要程序员去编写发送、接收以及维护队列的代码,只需要指定创建哪些队列即可。




2017-04-16

原文:消息队列(MSMQ 4.0版)是Windows 7自带的NT服务,利用WCF中的MSMQ队列管理器,不论目标接收方是否开机,发送方都可以向目标发送消息,而且不会出现消息丢失的情况。


《C#网络应用编程(第3版)(工业和信息化部“十二五”规划教材) (普通高等学校计算机教育“十二五”规划教材)[精品]》-想法_第1张图片

《C#网络应用编程(第3版)(工业和信息化部“十二五”规划教材) (普通高等学校计算机教育“十二五”规划教材)[精品]》-想法_第2张图片

《C#网络应用编程(第3版)(工业和信息化部“十二五”规划教材) (普通高等学校计算机教育“十二五”规划教材)[精品]》-想法_第3张图片

《C#网络应用编程(第3版)(工业和信息化部“十二五”规划教材) (普通高等学校计算机教育“十二五”规划教材)[精品]》-想法_第4张图片

《C#网络应用编程(第3版)(工业和信息化部“十二五”规划教材) (普通高等学校计算机教育“十二五”规划教材)[精品]》-想法_第5张图片




你可能感兴趣的:(读书笔记,C#)