-
easyui 工作流设计器设计 例子源码
easyui 工作流设计器设计 例子源码
- 2014-11-04下载
- 积分:1
-
windows hook 技术
对于HOOK函数的一点认识一、序言对大多数的Windows开发者来说,如何在Win32系统中对API函数的调用进行拦截一直是项极富挑战性的课题,因为这将是对你所掌握的计算机知识较为全面的考验,尤其是一些在如今使用RAD进行软件开发时并不常用的知识,这包括了操作系统原理、汇编语言甚至是关于机器指令代码的(听上去真是有点恐怖,不过这是事实)。当前广泛使用的Windows操作系统中,像Win 9x和WinNT/2K,都提供了一种比较稳健的机制来使得各个进程的内存地址空间之间是相互独立,也就是说一个进程中的某个有效的内存地址对另一个进程来说是无意义的,这种内存保护措施大大增加了系统的稳定性。不过,这也使得进行系统级的API拦截的工作的难度也大大加大了。 当然,我这里所指的是比较文雅的拦截方式,通过修改可执行文件在内存中的映像中有关代码,实现对API调用的动态拦截;而不是采用比较暴力的方式,直接对可执行文件的磁盘存储中机器代码进行改写。 二、API钩子系统一般框架通常,我们把拦截API的调用的这个过程称为是安装一个API钩子(API Hook)。一个API钩子至少有两个模块组成:一个是钩子服务器(Hook Server)模块,一般为EXE的形式;一个是钩子驱动器Hook Driver)模块,一般为DLL的形式。 服务器主要负责向目标进程注入驱动器,使得驱动器工作在目标进程的地址空间中,这是关键的第一步。驱动器则负责实际的API拦截工作,以便在我们所关心的API函数调用的前后能做一些我们需要的工作。 一个大家比较常见的API钩子的例子就是一些实时翻译软件(像金山词霸)中必备的的功能:屏幕抓词,它主要是对一些GDI函数进行了拦截,获取它们的输入参数中的字符串,然后在自己的窗口中显示出来。针对上述的两个部分,有以下两点需要我们重点考虑的:选用何种DLL注入技术采用何种API拦截机制 三、注入技术的选用由于在Win32系统中各个进程的地址是互相独立的,因此我们无法在一个进程中对另一个进程的代码进行有效的修改。而你要完成API钩子的工作就必须进行这种操作。因此,我们必须采取某种独特的手段,使得API钩子(准确的说是钩子驱动器)能够成为目标进程中的一部分,才有较大的可能来对目标进程数据和代码进行有控制的修改。 通常有以下几种注入方式:1.利用注册表如果我们准备拦截的进程连接了User32.dll,也就是使用了User32中的API(一般图形界面的应用程序都符合这个条件),那么就可以简单把你的钩子驱动器DLL的名字作为值添加在下面注册表的键下: HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsNTCurrentVersionWindowsAppInit_DLLs 值的形式可以为单个DLL的文件名,或者是一组DLL的文件名,相邻的名称之间用逗号或空格间隔。所有由该值标识的DLL将在符合条件的应用程序启动的时候装载。这是一个操作系统内建的机制,相对其他方式来说危险性较小,但它有一些比较明显的缺点:该方法仅适用于NT/2K操作系统。看看键的名称你就应该明白为了激活或停止钩子的注入,必须重新启动Windows。这个就似乎太不方便了不能用此方法向没有使用User32的应用程序注入DLL,例如控制台应用程序不管需要与否,钩子DLL将注入每一个GUI应用程序,这将导致整个系统性能的下降 2.建立系统范围的Windows钩子要向某个进程注入DLL,一个十分普遍也是比较简单的方法就是建立在标准的Windows钩子的基础上。Windows钩子一般是在DLL中实现的,这是一个全局性的Windows钩子的基本要求,这也符合我们的需要。当我们成功地调用SetWindowsHookEx函数之后,便在系统中安装了某种类型的消息钩子,这个钩子可以是针对某个进程,也可以是针对系统中的所有进程。一旦某个进程中产生了该类型的消息,操作系统会自动把该钩子所在的DLL映像到该进程的地址空间中,从而使得消息回调函数(在SetWindowsHookEx的参数中指定)能够对此消息进行适当的处理,在这里,我们所感兴趣的当然不是对消息进行什么处理,因此在消息回调函数中只需把消息钩子向后传递就可以了,但是我们所需的DLL已经成功地注入了目标进程的地址空间,从而可以完成后续工作。 我们知道,不同进程中使用的DLL之间是不能直接共享数据的,因为它们活动在不同的地址空间中。但在Windows钩子DLL中,有一些数据,例如Windows钩子句柄HHook,这是由SetWindowsHookEx函数返回值得到的,并且作为参数将在CallNextHookEx函数和UnhookWindoesHookEx函数中使用,显然使用SetWindowsHookEx函数的进程和使用CallNextHookEx函数的进程一般不会是同一个进程,因此我们必须能够使句柄在所有的地址空间中都是有效的有意义的,也就是说,它的值必须必须在这些钩子DLL所挂钩的进程之间是共享的。为了达到这个目的,我们就应该把它存储在一个共享的数据区域中。 在VC 中我们可以采用预编译指令#pragma data_seg在DLL文件中创建一个新的段,并且在DEF文件中把该段的属性设置为“shared”,这样就建立了一个共享数据段。对于使用Delphi的人来说就没有这么幸运了:没有类似的比较简单的方法(或许是有的,但我没有找到)。不过我们还是可以利用内存映像技术来申请使用一块各进程可以共享的内存区域,主要是利用了CreateFileMapping和MapViewOfFile这两个函数。这倒是一个通用的方法,适合所有的开发语言,只要它能使用Windows的API。 在Borland的BCB中有一个指令#pragma codeseg与VC 中的#pragma data_seg指令有点类似,应该也能起到一样的作用,但我试了一下,没有没有效果,而BCB的联机帮助中对此也提到的不多,不知怎样才能正确的使用。一旦钩子DLL加载进入目标进程的地址空间后,在我们调用UnHookWindowsHookEx函数之前是无法使它停止工作的,除非目标进程关闭。 这种DLL注入方式有两个优点: 这种机制在Win 9x/Me和WinNT/2K中都是得到支持的,预计在以后的版本中也将得到支持. 钩子DLL可以在不需要的时候,可由我们主动的调用UnHookWindowsHookEx来卸载,比起使用注册表的机制来说方便了许多 尽管这是一种相当简洁明了的方法,但它也有一些显而易见的缺点: 首先值得我们注意的是,Windows钩子将会降低整个系统的性能,因为它额外增加了系统在消息处理方面的时间 其次,只有当目标进程准备接受某种消息时,钩子所在的DLL才会被系统映射到该进程的地址空间中,钩子才能真正开始发挥作用。因此如果我们要对某些进程的整个生命周期内的API调用情况进行监控,用这种方法显然会遗漏某些API的调用 3.使用CreateRemoteThread函数在我看来这是一个相当棒的方法,然而不幸的是,CreateRemoteThread这个函数只能在WinNT/2K系统中才得到支持,虽然在Win9x中这个API也能被安全的调用而不出错,但它除了返回一个空值之外什么也不做。整个DLL注入过程十分简单。我们知道,任何一个进程都可以使用LoadLibrary来动态地加载一个DLL。但问题是,我们如何让目标进程在我们的控制下来加载我们的钩子DLL(也就是钩子驱动器)呢?这里有一个API函数CreateRemoteThread,通过它可在一个进程中可建立并运行一个远程的线程。 调用该API需要指定一个线程函数指针作为参数,该线程函数的原型如下:Function ThreadProc(lpParam: Pointer):DWORD;我们再来看一下LoadLibrary的函数原型:Function LoadLibrary(lpFileName: PChar):HModule;可以看出,这两个函数原型实质上是完全相同的(其实返回值是否相同关系不大,因为我们是无法得到远程线程函数的返回值的),只是叫法不同而已,这种相同使得我们可以把直接把LoadLibrary当做线程函数来使用,从而在目标进程中加载钩子DLL。 类似的,当我们需要卸载钩子DLL时,也可以FreeLibrary作为线程函数来使用,在目标进程中移去钩子DLL。一切看来是十分的简洁方便。通过调用GetProcAddress函数,我们可以得到LoadLibrary函数的地址。由于LoadLibrary是Kernel32中的函数,而这个系统DLL的映射地址对每一个进程来说都是相同的,因此LoadLibrary函数的地址也是如此。这点将确保我们能把该函数的地址作为一个有效的参数传递给CreateRemoteThread使用。 AddrOfLoadLibrary := GetProcAddress(GetModuleHandle(‘Kernel32.dll’),‘LoadLibrary’); HremoteThread := CreateRemoteThread(HTargetProcess,nil,0,AddrOfLoadLibrary,HookDllName,0,nil); 要使用CreateRemoteThread,我们需要目标进程的句柄作为参数。当我们用OpenProcess函数来得到进程的句柄时,通常是希望对此进程有全权的存取操作,也就是以PROCESS_ALL_ACCESS为标志打开进程。但对于一些系统级的进程,直接这样显然是不行的,只能返回一个的空句柄(值为零)。为此,我们必须把自己设置为拥有调试级的特权,这样将具有最大的存取权限,从而使得我们能对这些系统级的进程也可以进行一些必要的操作。 4.通过BHO来注入DLL 有时,我们想要注入DLL的对象仅仅是Internet Explorer。幸运的是,Windows操作系统为我们提供了一个简单的归档的方法(这保证了它的可靠性)―― 利用Browser Helper Objects(BHO)。一个BHO是一个在DLL中实现的COM对象,它主要实现了一个IObjectWithSite接口,而每当IE运行时,它会自动加载所有实现了该接口的COM对象。 四、拦截机制在钩子应用的系统级别方面,有两类API拦截的机制――内核级的拦截和用户级的拦截。内核级的钩子主要是通过一个内核模式的驱动程序来实现,显然它的功能应该最为强大,能捕捉到系统活动的任何细节,但难度也较大,不在我们探讨的范围之内(尤其对我这个使用Delphi的人来说,还没涉足这块领域,因此也无法探讨); 而用户级的钩子则通常是在普通的DLL中实现整个API的拦截工作,这才是我们现在所重点关注的。拦截API函数的调用,一般可有以下几种方法: 1.代理DLL(特洛伊木马)一个容易想到的可行的方法是用一个同名的DLL去替换原先那个输出我们准备拦截的API所在的DLL。当然代理DLL也要和原来的一样,输出所有函数。如果想到DLL中可能输出了上百个函数,我们就应该明白这种方法的效率是不高的。另外,我们还得考虑DLL的版本问题。 2.改写执行代码有许多拦截的方法是基于可执行代码的改写。其中一个就是改变在CALL指令中使用的函数地址,这种方法有些难度,也比较容易出错。它的基本思路是检索出在内存中所有你所要拦截的API的CALL指令,然后把原先的地址改成为你自己提供的函数的地址。 另外一种代码改写的方法的实现方法更为复杂,它的主要的实现步骤是先找到原先的API函数的地址,然后把该函数开始的几个字节用一个JMP指令代替(有时还不得不改用一个INT指令),使得对该API函数的调用能够转向我们自己的函数调用。实现这种方法要牵涉到一系列压栈和出栈这样的较底层的操作,显然对我们的汇编语言和操作系统底层方面的知识是一种考验。这个方法倒和很多病毒的感染机制相类似。 3.以调试器的身份进行拦截另一个可选的方法是在目标函数中安置一个调试断点,使得进程运行到此处就进入调试状态。然而这样一些问题也随之而来,其中较主要的是调试异常的产生将把进程中所有的线程都挂起。它也需要一个额外的调试模块来处理所有的异常,整个进程将一直在调试状态下运行,直至它运行结束。 4.改写输入地址表这种方法主要得益于现如今Windows系统中所使用的可执行文件(包括EXE文件和DLL文件)的良好结构――PE文件格式(Portable Executable File Format),因此它相当稳健,又简单易行。要理解这种方法是如何运作的,首先你得对PE文件格式有所理解。 一个PE文件的结构大致如下图所示: 一般PE文件一开始是一段DOS程序,当你的程序在不支持Windows的环境中运行时,它就会显示“This Program cannot be run in DOS mode”这样的警告语句,接着这个DOS文件头,就开始真正的PE文件内容了。首先是一段称为“IMAG E_NT_HEADER”的数据,其中是许多关于整个PE文件的消息,在这段数据的尾端是一个称为Data Directory的数据表,通过它能快速定位一些PE文件中段(section)的地址。在这段数据之后,则是一个“IMAGE_SECTION_HEADER”的列表,其中的每一项都详细描述了后面一个段的相关信息。接着它就是PE文件中最主要的段数据了,执行代码、数据和资源等等信息就分别存放在这些段中。 在所有的这些段里,有一个被称为“.idata”的段(输入数据段)值得我们去注意,该段中包含着一些被称为输入地址表(IAT,Import Address Table)的数据列表。每个用隐式方式加载的API所在的DLL都有一个IAT与之对应,同时一个API的地址也与IAT中一项相对应。当一个应用程序加载到内存中后,针对每一个API函数调用,相应的产生如下的汇编指令:JMP DWORD PTR [XXXXXXXX] 如果在VC 中使用了_delcspec(import),那么相应的指令就成为 CALL DWORD PTR [XXXXXXXX]。 不管怎样,上述方括号中的总是一个地址,指向了输入地址表中一个项,是一个DWORD,而正是这个DWORD才是API函数在内存中的真正地址。因此我们要想拦截一个API的调用,只要简单的把那个DWORD改为我们自己的函数的地址,那么所有关于这个API的调用将转到我们自己的函数中去,拦截工作也就宣告顺利的成功了。这里要注意的是,自定义的函数的调用形式应该是API的调用方式,也就是stdcall方式,而Delphi中默认的是pascal的调用方式,也就是register方式,它们在参数的传递方式等方面存在着较大的区别。 另外,自定义的函数的参数形式可以和原先的API函数相同的,不过这也不是必须的,而且这样的话在有些时候也会出现一些问题,我在后面将会提到。因此要拦截API的调用,首先我们就要得到相应的IAT的地址。系统把一个进程模块加载到内存中,其实就是把PE文件几乎是原封不动的映射到进程的地址空间中去,而模块句柄HModule实际上就是模块映像在内存中的地址,PE文件中一些数据项的地址,都是相对于这个地址的偏移量,因此被称为相对虚拟地址(RVA,Relative Virtual Address)。 于是我们就可以从HModule开始,经过一系列的地址偏移而得到IAT的地址。不过我这里有一个简单的方法,它使用了一个现有的API函数 ImageDirectoryEntryToData,它帮助我们在定位IAT时能少走几步,省得把偏移地址弄错了,走上弯路。不过纯粹使用RVA从HModule开始来定位IAT的地址其实并不麻烦,而且这样还更有助于我们对PE文件的结构的了解。上面提到的API函数是在DbgHelp.dll中输出的(这是从Win2K才开始有的,在这之前是由ImageHlp.dll提供的),有关这个函数的详细介绍可参见MSDN。 在找到IAT之后,我们只需在其中遍历,找到我们需要的API地址,然后用我们自己的函数地址去覆盖它。下面给出一段对应的源码: procedure RedirectApiCall; var ImportDesc:PIMAGE_IMPORT_DESCRIPTOR; FirstThunk:PIMAGE_THUNK_DATA32; sz:DWORD; begin //得到一个输入描述结构列表的首地址,每个DLL都对应一个这样的结构 ImportDesc:=ImageDirectoryEntryToData(Pointer(HTargetModule),true,IMAGE_DIRECTORY_ENTRY_IMPORT,sz); while Pointer(ImportDesc.Name)nil do begin //判断是否是所需的DLL输入描述 if StrIComp(PChar(DllName),PChar(HTargetModule ImportDesc.Name))=0 then begin //得到IAT的首地址 FirstThunk:=PIMAGE_THUNK_DATA32(HTargetModule ImportDesc.FirstThunk); while FirstThunk.Funcnil do begin if FirstThunk.Func=OldAddressOfAPI then
- 2020-12-06下载
- 积分:1
-
c# 十字光标定位 实例
winform程序 实现了 十字光标定位
- 2013-05-06下载
- 积分:1
-
asp.net 使用统计图表功能源码-饼图
推荐几个 不错的图表开源js ichartjs:http://www.ichartjs.com/ nvd3:http://nvd3.org/ highcharts:http://www.highcharts.com/ echarts:http://ecomfe.github.io/echarts/index.html
- 2014-01-25下载
- 积分:1
-
c#开发的socket封装类
c#开发的socket封装类(包含心跳检测功能)及源码,在开发需要通讯的程序时可以直接使用,内有demo例子
- 2017-06-30下载
- 积分:1
-
.NET 后台管理系统(源码+数据库)
【实例简介】
.NET 通用后台角色权限管理系统
软件架构
通用三层架构,面向接口编程。
技术选型
MVC5
Mysql
Dapper
Autofac
Layui
阿里巴巴矢量库
- 2021-07-04 00:31:14下载
- 积分:1
-
c# 网络编程 实例源码(pdf)
我们知道 C#和 C++的差异之一,就是他本身没有类库,所使用的类库是.Net 框架中的类库--.Net FrameWork SDK。在.Net FrameWork SDK 中为网络编程提供了二个名称空间:"System.Net"和"System.Net.Sockets"。C#就是通过这二个名称空间中封装的类和方法实现网络通讯的。 首先我们解释一下在网络编程时候,经常遇到的几个概念:同步(synchronous)、异步(asynchronous)、阻塞(Block)和非阻塞(Unblock): 所谓同步方式,就是发送方发送数据包以后,丌等接受方响应,就接着发送下一个数据包。异步方式就是当发送方发送一个数据包以后,一直等到接受方响应后,才接着发送下一个数据包。而阻塞套接字是指执行此套接字的网络调用时,直到调用成功才返回,否则此套节字就一直阻塞在网络调用上,比如调用 StreamReader 类的 Readlin ( )方法读取网络缓冲区中的数据,如果调用的时候没有数据到达,那么此 Readlin ( )方法将一直挂在调用上,直到读到一些数据,此函数调用才返回;而非阻塞套接字是指在执行此套接字的网络调用时,丌管是否执行成功,都立即返回。同样调用 StreamReader 类的 Readlin ( )方法读取网络缓冲区中数据,丌管是否读到数据都立即返回,而丌会一直挂在此函数调用上。在 Windows网络通信软件开发中,最为常用的方法就是异步非阻塞套接字。平常所说的 C/S(客户端/服务器)结构的软件采用的方式就是异步非阻塞模式的。 其实在用 C#迚行网络编程中,我们并丌需要了解什么同步、异步、阻塞和非阻塞的原理和工作机制,因为在.Net FrameWrok SDK 中已经已经把这些机制给封装好了。下面我们就用 C#开一个具体的网络程序来说明一下问题。 一.本文中介绍的程序设计及运行环境 (1).微软视窗 2000 服务器版 (2)..Net Framework SDK Beta 2 以上版本 二.服务器端程序设计的关键步骤以及解决办法: 在下面接受的程序中,我们采用的是异步阻塞的方式。 (1).首先要要在给定的端口上面创建一个"tcpListener"对象侦听网络上面的请求。当接收到连结请求后通过调用"tcpListener"对象的"AcceptSocket"方法产生一个用于处理接入连接请求的 Socket 的实例。下面是具体实现代码: //创建一个 tcpListener 对象,此对象主要是对给定端口迚行侦听 tcpListener = new TcpListener ( 1234 ) ; //开始侦听 tcpListener.Start ( ) ; //返回可以用以处理连接的 Socket 实例 socketForClient = tcpListener.AcceptSocket ( ) ; (2).接受和发送客户端数据: 此时 Socket 实例已经产生,如果网络上有请求,在请求通过以后,Socket 实例构造一个"NetworkStream"对象,"NetworkStream"对象为网络访问提供了基础数据流。我们通过名称空间"System.IO"中封装的二个类"StreamReader"和"StreamWriter"来实现对"NetworkStream"对象的访问。其中"StreamReader"类中的 ReadLine ( )方法就是从"NetworkStream"对象中读取一行字符;"StreamWriter"类中的 WriteLine ( )方法就是对"NetworkStream"对象中写入一行字符串。从而实现在网络上面传输字符串,下面是具体的实现代码: try { //如果返回值是"true",则产生的套节字已经接受来自进方的连接请求 if ( socketForClient.Connected ) { ListBox1.Items.Add ( "已经和客户端成功连接!" ) ; while ( true ) { //创建 networkStream 对象通过网络套节字来接受和发送数据 networkStream = new NetworkStream ( socketForClient ) ; //从当前数据流中读取一行字符,返回值是字符串 streamReader = new StreamReader ( networkStream ) ; string msg = streamReader.ReadLine ( ) ; ListBox1.Items.Add ( "收到客户端信息:" msg ) ; streamWriter = new StreamWriter ( networkStream ) ; if ( textBox1.Text != "" ) { ListBox1.Items.Add ( "往客户端反馈信息:" textBox1.Text ) ; //往当前的数据流中写入一行字符串 streamWriter.WriteLine ( textBox1.Text ) ; //刷新当前数据流中的数据 streamWriter.Flush ( ) ; } } } } catch ( Exception ey ) { MessageBox.Show ( ey.ToString ( ) ) ; } (3).最后别忘了要关闭所以流,停止侦听网络,关闭套节字,具体如下: //关闭线程和流 networkStream.Close ( ) ; streamReader.Close ( ) ; streamWriter.Close ( ) ; _thread1.Abort ( ) ; tcpListener.Stop ( ) ; socketForClient.Shutdown ( SocketShutdown.Both ) ; socketForClient.Close ( ) ; 三.C#网络编程服务器端程序的部分源代码(server.cs): 由于在此次程序中我们采用的结构是异步阻塞方式,所以在实际的程序中,为了丌影响服务器端程序的运行速度,我们在程序中设计了一个线程,使得对网络请求侦听,接受和发送数据都在线程中处理,请在下面的代码中注意这一点,下面是 server.cs 的完整代码: using System ; using System.Drawing ; using System.Collections ; using System.ComponentModel ; using System.Windows.Forms ; using System.Data ; using System.Net.Sockets ; using System.IO ; using System.Threading ; using System.Net ; //导入程序中使用到的名字空间 public class Form1 : Form { private ListBox ListBox1 ; private Button button2 ; private Label label1 ; private TextBox textBox1 ; private Button button1 ; private Socket socketForClient ; private NetworkStream networkStream ; private TcpListener tcpListener ; private StreamWriter streamWriter ; private StreamReader streamReader ; private Thread _thread1 ; private System.ComponentModel.Container components = null ; public Form1 ( ) { InitializeComponent ( ) ; } //清除程序中使用的各种资源 protected override void Dispose ( bool disposing ) { if ( disposing ) { if ( components != null ) { components.Dispose ( ) ; } } base.Dispose ( disposing ) ; } private void InitializeComponent ( ) { label1 = new Label ( ) ; button2 = new Button ( ) ; button1 = new Button ( ) ; ListBox1 = new ListBox ( ) ; textBox1 = new TextBox ( ) ; SuspendLayout ( ) ; label1.Location = new Point ( 8 , 168 ) ; label1.Name = "label1" ; label1.Size = new Size ( 120 , 23 ) ; label1.TabIndex = 3 ; label1.Text = "往客户端反馈信息:" ; //同样的方式设置其他控件,这里略去 this.Controls.Add ( button1 ) ; this.Controls.Add ( textBox1 ) ; this.Controls.Add ( label1 ) ; this.Controls.Add ( button2 ) ; this.Controls.Add ( ListBox1 ) ; this.MaximizeBox = false ; this.MinimizeBox = false ; this.Name = "Form1" ; this.Text = "C#的网络编程服务器端!" ; this.Closed = new System.EventHandler ( this.Form1_Closed ) ; this.ResumeLayout ( false ) ; } private void Listen ( ) { //创建一个 tcpListener 对象,此对象主要是对给定端口迚行侦听 tcpListener = new TcpListener ( 1234 ) ; //开始侦听 tcpListener.Start ( ) ; //返回可以用以处理连接的 Socket 实例 socketForClient = tcpListener.AcceptSocket ( ) ; try { //如果返回值是"true",则产生的套节字已经接受来自进方的连接请求 if ( socketForClient.Connected ) { ListBox1.Items.Add ( "已经和客户端成功连接!" ) ; while ( true ) { //创建 networkStream 对象通过网络套节字来接受和发送数据 networkStream = new NetworkStream ( socketForClient ) ; //从当前数据流中读取一行字符,返回值是字符串 streamReader = new StreamReader ( networkStream ) ; string msg = streamReader.ReadLine ( ) ; ListBox1.Items.Add ( "收到客户端信息:" msg ) ; streamWriter = new StreamWriter ( networkStream ) ; if ( textBox1.Text != "" ) { ListBox1.Items.Add ( "往客户端反馈信息:" textBox1.Text ) ; //往当前的数据流中写入一行字符串 streamWriter.WriteLine ( textBox1.Text ) ; //刷新当前数据流中的数据 streamWriter.Flush ( ) ; } } } } catch ( Exception ey ) { MessageBox.Show ( ey.ToString ( ) ) ; } } static void Main ( ) { Application.Run ( new Form1 ( ) ) ; } private void button1_Click ( object sender , System.EventArgs e ) { ListBox1.Items .Add ( "服务已经启动!" ) ; _thread1 = new Thread ( new ThreadStart ( Listen ) ) ; _thread1.Start ( ) ; } private void button2_Click ( object sender , System.EventArgs e ) { //关闭线程和流 networkStream.Close ( ) ; streamReader.Close ( ) ; streamWriter.Close ( ) ; _thread1.Abort ( ) ; tcpListener.Stop ( ) ; socketForClient.Shutdown ( SocketShutdown.Both ) ; socketForClient.Close ( ) ; } private void Form1_Closed ( object sender , System.EventArgs e ) { //关闭线程和流 networkStream.Close ( ) ; streamReader.Close ( ) ; streamWriter.Close ( ) ; _thread1.Abort ( ) ; tcpListener.Stop ( ) ; socketForClient.Shutdown ( SocketShutdown.Both ) ; socketForClient.Close ( ) ; } } 四.客户端程序设计的关键步骤以及解决办法: (1).连接到服务器端的指定端口: 我们采用的本地机既做服务器也做客户机,你可以通过修改 IP 地址来确定自己想要连接的服务器。我们在连接的时候采用了"TcpClient"类,此类是在较高的抽象级别(高于Socket 类)上面提供 TCP 服务。下面代码就是连接到本地机(端口为 1234),并获取响应流: //连接到服务器端口,在这里是选用本地机器作为服务器,你可以通过修改 IP 地址来改变服务器 try { myclient = new TcpClient ( "localhost" , 1234 ) ; } catch { MessageBox.Show ( "没有连接到服务器!" ) ; return ; } //创建 networkStream 对象通过网络套节字来接受和发送数据 networkStream = myclient.GetStream ( ) ; streamReader = new StreamReader ( networkStream ) ; streamWriter = new StreamWriter ( networkStream ) ; (2).实现接受和发送数据: 在接受和发送数据上面,我们依然采用了"NetworkStream"类,因为对他迚行操作比较简单,具体实现发送和接受还是通过命名空间"System.IO"中"StreamReader"类ReadLine ( )方法和"StreamWriter"类的 WriteLine ( )方法。具体的实现方法如下: if ( textBox1.Text == "" ) { MessageBox.Show ( "请确定文本框为非空!" ) ; textBox1.Focus ( ) ; return ; } try { string s ; //往当前的数据流中写入一行字符串 streamWriter.WriteLine ( textBox1.Text ) ; //刷新当前数据流中的数据 streamWriter.Flush ( ) ; //从当前数据流中读取一行字符,返回值是字符串 s = streamReader.ReadLine ( ) ; ListBox1.Items.Add ( "读取服务器端发送内容:" s ) ; } catch ( Exception ee ) { MessageBox.Show ( "从服务器端读取数据出现错误,类型为:" ee.ToString ( ) ) ; } (3).最后一步和服务器端是一样的,就是要关闭程序中创建的流,具体如下: streamReader.Close ( ) ; streamWriter.Close ( ) ; networkStream.Close ( ) ; 五.客户端的部分代码: 由于在客户端丌需要侦听网络,所以在调用上面没有程序阻塞情冴,所以在下面的代码中,我们没有使用到线程,这是和服务器端程序的一个区别的地方。总结上面的这些关键步骤,可以得到一个用 C#网络编程 完整的客户端程序(client.cs),具体如下: using System ; using System.Drawing ; using System.Collections ; using System.ComponentModel ; using System.Windows.Forms ; using System.Data ; using System.Net.Sockets ; using System.IO ; using System.Threading ; //导入程序中使用到的名字空间 public class Form1 : Form { private ListBox ListBox1 ; private Label label1 ; private TextBox textBox1 ; private Button button3 ; private NetworkStream networkStream ; private StreamReader streamReader ; private StreamWriter streamWriter ; TcpClient myclient ; private Label label2 ; private System.ComponentModel.Container components = null ; public Form1 ( ) { InitializeComponent ( ) ; } //清除程序中使用的各种资源 protected override void Dispose ( bool disposing ) { if ( disposing ) { if ( components != null ) { components.Dispose ( ) ; } } base.Dispose ( disposing ) ; } private void InitializeComponent ( ) { label1 = new Label ( ) ; button3 = new Button ( ) ; ListBox1 = new ListBox ( ) ; textBox1 = new TextBox ( ) ; label2 = new Label ( ) ; SuspendLayout ( ) ; label1.Location = new Point ( 8 , 168 ) ; label1.Name = "label1" ; label1.Size = new Size ( 56 , 23 ) ; label1.TabIndex = 3 ; label1.Text = "信息:" ; //同样方法设置其他控件 AutoScaleBaseSize = new Size ( 6 , 14 ) ; ClientSize = new Size ( 424 , 205 ) ; this.Controls.Add ( button3 ) ; this.Controls.Add ( textBox1 ) ; this.Controls.Add ( label1 ) ; this.Controls.Add ( label2 ) ; this.Controls.Add ( ListBox1 ) ; this.MaximizeBox = false ; this.MinimizeBox = false ; this.Name = "Form1" ; this.Text = "C#的网络编程客户器端!" ; this.Closed = new System.EventHandler ( this.Form1_Closed ) ; this.ResumeLayout ( false ) ; //连接到服务器端口,在这里是选用本地机器作为服务器,你可以通过修改 IP 地址来改变服务器 try { myclient = new TcpClient ( "localhost" , 1234 ) ; } catch { MessageBox.Show ( "没有连接到服务器!" ) ; return ; } //创建 networkStream 对象通过网络套节字来接受和发送数据 networkStream = myclient.GetStream ( ) ; streamReader = new StreamReader ( networkStream ) ; streamWriter = new StreamWriter ( networkStream ) ; } static void Main ( ) { Application.Run ( new Form1 ( ) ) ; } private void button3_Click ( object sender , System.EventArgs e ) { if ( textBox1.Text == "" ) { MessageBox.Show ( "请确定文本框为非空!" ) ; textBox1.Focus ( ) ; return ; } try { string s ; //往当前的数据流中写入一行字符串 streamWriter.WriteLine ( textBox1.Text ) ; //刷新当前数据流中的数据 streamWriter.Flush ( ) ; //从当前数据流中读取一行字符,返回值是字符串 s = streamReader.ReadLine ( ) ; ListBox1.Items.Add ( "读取服务器端发送内容:" s ) ; } catch ( Exception ee ) { MessageBox.Show ( "从服务器端读取数据出现错误,类型为:" ee.ToString ( ) ) ; } } private void Form1_Closed ( object sender , System.EventArgs e ) { streamReader.Close ( ) ; streamWriter.Close ( ) ; networkStream.Close ( ) ; } } 下图是编译上面二个程序后运行的界面: 图 01:C#编写网络程序运行界面 七.总结: 虽然在.Net FrameWrok SDK 中只为网络编程提供了二个命名空间,但这二个命名空间中的内容却是十分丰富的,C#利用这二个命名空间既可以实现同步和异步,也可以实现阻塞和非阻塞。本文通过用 C#编写一个网络上信息传输的程序,展现了其丰富的内容,由于篇幅所限,更深,更强大的功能还需要读者去实践、探索。
- 2014-07-07下载
- 积分:1
-
工控网关, 轻量级组态软件(SharpSCADA源码)
SCADA源代码 SharpSCADA - 工控网关, 轻量级组态软件. =================== 简介 ------------- 采用技术: 开发语言:C# 运行环境:.NET Framework 数据库:SQL Server 功能: -------------* 1.轻量级工控网关: 支持当前几种主要的工业协议如西门子的Profinet、AB的EtherNetIPs、施耐德的Modbus和OPC。采用类OPC接口网关。 * 2.数据采集、归档、预警及配置工具 支持实时数据采集、历史数据归档、变量触发预警,并使用TagConfig工具简单的配置实现。 * 3.人机界面(设计时和运行时) *设计时: 采用Microsoft Visual Studio 设计器插件(在VS2010-VS2015社区版测试通过)。 通过继承HMIControlBase接口并书写极少量的代码即可实现复杂的图元组件。 支持图元拖放、组合、连线、变量绑定及编辑功能。
- 2020-12-04下载
- 积分:1
-
用C#_编写的一个简单屏保程序
用C#_编写的一个简单屏保程序
- 2015-05-18下载
- 积分:1
-
青春部落格pds图片源码
青春部落格pds图片源码
- 2014-07-17下载
- 积分:1