当前位置:文档之家› 手把手教你玩转网络编程模型之完成端口(CompletionPort)篇

手把手教你玩转网络编程模型之完成端口(CompletionPort)篇

手把手教你玩转网络编程模型之完成端口(CompletionPort)篇
手把手教你玩转网络编程模型之完成端口(CompletionPort)篇

手把手教你玩转网络编程模型系列之三

完成端口(CompletionPort)篇

----- By PiggyXP(小猪)

前言

完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何提笔,所以这篇文档总是在酝酿之中……酝酿了两年之后,终于决定开始动笔了,但愿还不算晚…..

这篇文档我非常详细并且图文并茂的介绍了关于网络编程模型中完成端口的编程模型方方面面的信息,从API的用法到使用的步骤,从完成端口的实现机理到实际使用的注意事项,都有所涉及,并且为了让朋友们更直观的体会完成端口的用法,本文附带了有详尽注释的使用MFC编写的图形界面的示例代码。

我的初衷是希望写一份互联网上能找到的最详尽的关于完成端口的教学文档,而且让对Socket编程略有了解的人都能够看得懂,都能学会如何来使用完成端口这么优异的网络编程模型,但是由于本人水平所限,不知道我的初衷是否实现了,但还是希望各位需要的朋友能够喜欢。

由于篇幅原因,本文假设你已经熟悉了利用Socket进行TCP/IP编程的基本原理,并且也熟练的掌握了多线程编程技术,太基本的概念我这里就略过不提了,网上的资料应该遍地都是。

本文档凝聚着笔者心血,如要转载,请指明原作者及出处,谢谢!不过代码没有版权,可以随便散播使用,欢迎改进,特别是非常欢迎能够帮助我发现Bug的朋友,以更好的造福大家。^_^ 本文配套的示例源码下载地址(在我的下载空间里)

https://www.doczj.com/doc/932555987.html,/

(里面的代码包括VC++2008/VC++2010编写的完成端口服务器端的代码,还包括一个对服务器端进行压力测试的测试代码,都是经过我精心调试过,并且带有非常详尽的代码注释的。当然,作为教学代码,为了能够使得代码结构清晰明了,我还是对代码有所简化,如果想要用于产品开发,最好还是需要自己再完善一下,另外我的工程是用2010编写的,附带的2008工程不知道有没有问题,但是其中代码都是一样的,暂未测试)

忘了嘱咐一下了,文章篇幅很长很长,基本涉及到了与完成端口有关的方方面面,一次看不完可以分好几次,中间注意休息,好身体才是咱们程序员最大的本钱!

对了,还忘了嘱咐一下,因为本人的水平有限,虽然我反复修正了数遍,但文章和示例代码里肯定还有我没发现的错误和纰漏,希望各位一定要指出来,拍砖、喷我,我都能Hold住,但是一定要指出来,我会及时修正,因为我不想让文中的错误传遍互联网,祸害大家。

OK, Let’s go ! Have fun!

目录:

1.完成端口的优点

2.完成端口程序的运行演示

3.完成端口的相关概念

4.完成端口的基本流程

5.完成端口的使用详解

6.实际应用中应该要注意的地方

一.完成端口的优点

1.我想只要是写过或者想要写C/S模式网络服务器端的朋友,都应该或多或少的听过完成端口的大名吧,

完成端口会充分利用Windows内核来进行I/O的调度,是用于C/S通信模式中性能最好的网络通信模型,没有之一;甚至连和它性能接近的通信模型都没有。

2.完成端口和其他网络通信方式最大的区别在哪里呢?

(1) 首先,如果使用“同步”的方式来通信的话,这里说的同步的方式就是说所有的操作都在一个线

程内顺序执行完成,这么做缺点是很明显的:因为同步的通信操作会阻塞住来自同一个线程的任何其他操作,只有这个操作完成了之后,后续的操作才可以完成;一个最明显的例子就是咱们在MFC的界面代码中,直接使用阻塞Socket调用的代码,整个界面都会因此而阻塞住没有响应!所以我们不得不为每一个通信的Socket都要建立一个线程,多麻烦?这不坑爹呢么?所以要写高性能的服务器程序,要求通信一定要是异步的。

(2) 各位读者肯定知道,可以使用使用“同步通信(阻塞通信)+多线程”的方式来改善(1)的情况,那

么好,想一下,我们好不容易实现了让服务器端在每一个客户端连入之后,都要启动一个新的Thread 和客户端进行通信,有多少个客户端,就需要启动多少个线程,对吧;但是由于这些线程都是处于运行状态,所以系统不得不在所有可运行的线程之间进行上下文的切换,我们自己是没啥感觉,但是CPU 却痛苦不堪了,因为线程切换是相当浪费CPU时间的,如果客户端的连入线程过多,这就会弄得CPU 都忙着去切换线程了,根本没有多少时间去执行线程体了,所以效率是非常低下的,承认坑爹了不?

(3) 而微软提出完成端口模型的初衷,就是为了解决这种"one-thread-per-client"的缺点的,它充

分利用内核对象的调度,只使用少量的几个线程来处理和客户端的所有通信,消除了无谓的线程上下文切换,最大限度的提高了网络通信的性能,这种神奇的效果具体是如何实现的请看下文。

3.完成端口被广泛的应用于各个高性能服务器程序上,例如著名的Apache….如果你想要编写的服务器

端需要同时处理的并发客户端连接数量有数百上千个的话,那不用纠结了,就是它了。

二.完成端口程序的运行演示

首先,我们先来看一下完成端口在笔者的PC机上的运行表现,笔者的PC配置如下:

图1 笔者计算机配置信息

大体就是i7 2600 + 16GB内存,我们进行如下的测试,通过Client生成3万个并发线程同时连接至Server,然后每个线程每隔3秒中发送一次数据,一共发送3次,然后观察服务器端的CPU和内存的占用情况。

如图2所示,是客户端3万个并发线程发送共发送9万条数据的log截图

图2客户端发3万并发线程log信息

图3是服务器端接收完毕3万个并发线程和每个线程的3份数据后的log截图

图3 服务器端接收3万并发线程log信息

最关键是图4,图4是服务器端在接收到28000个并发线程的时候,CPU占用率的截图,使用的软件是大名鼎鼎的Process Explorer,因为相对来讲这个比自带的任务管理器要准确和精确一些。

图4 服务器端和客户端的CPU占用情况

我们可以发现一个令人惊讶的结果,采用了完成端口的Server程序(蓝色横线所示)所占用的CPU才为3.82%,整个运行过程中的峰值也没有超过4%,是相当气定神闲的……哦,对了,这还是在Debug 模式下运行的情况,如果采用Release方式执行,性能肯定还会更高一些,除此以外,在UI上显示信息也很大成都上影响了性能。

相反采用了多个并发线程的Client程序(紫色横线所示)居然占用的CPU高达11.53%,甚至超过了Server程序的数倍……

其实无论是哪种网络操模型,对于内存占用都是差不多的,真正的差别就在于CPU的占用,其他的网络模型都需要更多的CPU动力来支撑同样的连接数据。

虽然这远远算不上服务器极限压力测试,但是从中也可以看出来完成端口的实力,而且这种方式比纯粹靠多线程的方式实现并发资源占用率要低得多。

三.完成端口的相关概念

在开始编码之前,我们先来讨论一下和完成端口相关的一些概念,如果你没有耐心看完这段大段的文字的话,也可以跳过这一节直接去看下下一节的具体实现部分,但是这一节中涉及到的基本概念你还是有必要了解一下的,而且你也更能知道为什么有那么多的网络编程模式不用,非得要用这么又复杂又难以理解的完成端口呢??也会坚定你继续学习下去的信心^_^

1.异步通信机制及其几种实现方式的比较

我们从前面的文字中了解到,高性能服务器程序使用异步通信机制是必须的。

而对于异步的概念,为了方便后面文字的理解,这里还是再次简单的描述一下:

异步通信就是在咱们与外部的I/O设备进行打交道的时候,我们都知道外部设备的I/O和CPU比起来简直是龟速,比如硬盘读写、网络通信等等,我们没有必要在咱们自己的线程里面等待着I/O操作完成再执行后续的代码,而是将这个请求交给设备的驱动程序自己去处理,我们的线程可以继续做其他更重要的事情,大体的流程如图1所示:

图1 异步通信流程

我可以从图中看到一个很明显的并行操作的过程,而“同步”的通信方式是在进行网络操作的时候,

主线程就挂起了,主线程要等待网络操作完成之后,才能继续执行后续的代码,是没法这样并行的;

这样无疑比“阻塞模式+多线程”的方式效率要高的多,这也是前者为什么叫“异步”,后者为什么叫“同步”的原因了,因为不需要等待网络操作完成再执行别的操作。

而在Windows中实现异步的机制同样有好几种,而这其中的区别,关键就在于图1中的最后一步“通知应用程序处理网络数据”上了,因为实现操作系统调用设备驱动程序去接收数据的操作都是一样的,关键就是在于如何去通知应用程序来拿数据。它们之间的具体区别我这里多讲几点,文字有点多,如果没兴趣深入研究的朋友可以跳过下一面的这一段,不影响的:)

(1) 设备内核对象,使用设备内核对象来协调数据的发送请求和接收数据协调,也就是说通过设置设备内核对象的状态,在设备接收数据完成后,马上触发这个内核对象,然后让接收数据的线程收到通知,但是这种方式太原始了,接收数据的线程为了能够知道内核对象是否被触发了,还是得不停的挂起等待,这简直是根本就没有用嘛,太低级了,有木有?所以在这里就略过不提了,各位读者要是没明白是怎么回事也不用深究了,总之没有什么用。

(2) 事件内核对象,利用事件内核对象来实现I/O操作完成的通知,其实这种方式其实就是我以前写文章的时候提到的《基于事件通知的重叠I/O模型》,链接在这里,这种机制就先进得多,可以同时等待多个I/O操作的完成,实现真正的异步,但是缺点也是很明显的,既然用WaitForMultipleObjects()来等待Event的话,就会受到64个Event等待上限的限制,但是这可不是说我们只能处理来自于64个客户端的Socket,而是这是属于在一个设备内核对象上等待的64个事件内核对象,也就是说,我们在一个线程内,可以同时监控64个重叠I/O操作的完成状态,当然我们同样可以使用多个线程的方式来满足无限多个重叠I/O的需求,比如如果想要支持3万个连接,就得需要500多个线程…用起来太麻烦让人感觉不爽;

(3) 使用APC( Asynchronous Procedure Call,异步过程调用)来完成,这个也就是我以前在文章里提到的《基于完成例程的重叠I/O模型》,链接在这里,这种方式的好处就是在于摆脱了基于事件通知方式的64个事件上限的限制,但是缺点也是有的,就是发出请求的线程必须得要自己去处理接收请求,哪怕是这个线程发出了很多发送或者接收数据的请求,但是其他的线程都闲着…,这个线程也还是得自己来处理自己发出去的这些请求,没有人来帮忙…这就有一个负载均衡问题,显然性能没有达到最优化。

(4) 完成端口,不用说大家也知道了,最后的压轴戏就是使用完成端口,对比上面几种机制,完成端口的做法是这样的:事先开好几个线程,你有几个CPU我就开几个,首先是避免了线程的上下文切换,因为线程想要执行的时候,总有CPU资源可用,然后让这几个线程等着,等到有用户请求来到的时候,就把这些请求都加入到一个公共消息队列中去,然后这几个开好的线程就排队逐一去从消息队列中取出消息并加以处理,这种方式就很优雅的实现了异步通信和负载均衡的问题,因为它提供了一种机制来使用几个线程“公平的”处理来自于多个客户端的输入/输出,并且线程如果没事干的时候也会被系统挂起,不会占用CPU周期,挺完美的一个解决方案,不是吗?哦,对了,这个关键的作为交换的消息队列,就是完成端口。

比较完毕之后,熟悉网络编程的朋友可能会问到,为什么没有提到WSAAsyncSelect或者是WSAEventSelect这两个异步模型呢,对于这两个模型,我不知道其内部是如何实现的,但是这其中一定没有用到Overlapped机制,就不能算作是真正的异步,可能是其内部自己在维护一个消息队列吧,总之这两个模式虽然实现了异步的接收,但是却不能进行异步的发送,这就很明显说明问题了,我想其内部的实现一定和完成端口是迥异的,并且,完成端口非常厚道,因为它是先把用户数据接收回来之后再通知用户直接来取就好了,而WSAAsyncSelect和WSAEventSelect之流只是会接收到数据到达的通知,而只

能由应用程序自己再另外去recv数据,性能上的差距就更明显了。

最后,我的建议是,想要使用基于事件通知的重叠I/O和基于完成例程的重叠I/O的朋友,如果不是特别必要,就不要去使用了,因为这两种方式不仅使用和理解起来也不算简单,而且还有性能上的明显瓶颈,何不就再努力一下使用完成端口呢?

2.重叠结构(OVERLAPPED)

我们从上一小节中得知,要实现异步通信,必须要用到一个很风骚的I/O数据结构,叫重叠结构“Overlapped”,Windows里所有的异步通信都是基于它的,完成端口也不例外。

至于为什么叫Overlapped?Jeffrey Richter的解释是因为“执行I/O请求的时间与线程执行其他任务的时间是重叠(overlapped)的”,从这个名字我们也可能看得出来重叠结构发明的初衷了,对于重叠结构的内部细节我这里就不过多的解释了,就把它当成和其他内核对象一样,不需要深究其实现机制,只要会使用就可以了,想要了解更多重叠结构内部的朋友,请去翻阅Jeffrey Richter的《Windows via C/C++》5th的292页,如果没有机会的话,也可以随便翻翻我以前写的Overlapped的东西,不过写得比较浅显……

这里我想要解释的是,这个重叠结构是异步通信机制实现的一个核心数据结构,因为你看到后面的代码你会发现,几乎所有的网络操作例如发送/接收之类的,都会用WSASend()和WSARecv()代替,参数里面都会附带一个重叠结构,这是为什么呢?因为重叠结构我们就可以理解成为是一个网络操作的ID号,也就是说我们要利用重叠I/O提供的异步机制的话,每一个网络操作都要有一个唯一的ID号,因为进了系统内核,里面黑灯瞎火的,也不了解上面出了什么状况,一看到有重叠I/O的调用进来了,就会使用其异步机制,并且操作系统就只能靠这个重叠结构带有的ID号来区分是哪一个网络操作了,然后内核里面处理完毕之后,根据这个ID号,把对应的数据传上去。

你要是实在不理解这是个什么玩意,那就直接看后面的代码吧,慢慢就明白了……

3.完成端口(CompletionPort)

对于完成端口这个概念,我一直不知道为什么它的名字是叫“完成端口”,我个人的感觉应该叫它“完成队列”似乎更合适一些,总之这个“端口”和我们平常所说的用于网络通信的“端口”完全不是一个东西,我们不要混淆了。

首先,它之所以叫“完成”端口,就是说系统会在网络I/O操作“完成”之后才会通知我们,也就是说,我们在接到系统的通知的时候,其实网络操作已经完成了,就是比如说在系统通知我们的时候,并非是有数据从网络上到来,而是来自于网络上的数据已经接收完毕了;或者是客户端的连入请求已经被系统接入完毕了等等,我们只需要处理后面的事情就好了。

各位朋友可能会很开心,什么?已经处理完毕了才通知我们,那岂不是很爽?其实也没什么爽的,那是因为我们在之前给系统分派工作的时候,都嘱咐好了,我们会通过代码告诉系统“你给我做这个做那个,等待做完了再通知我”,只是这些工作是做在之前还是之后的区别而已。

其次,我们需要知道,所谓的完成端口,其实和HANDLE一样,也是一个内核对象,虽然Jeff Richter 吓唬我们说:“完成端口可能是最为复杂的内核对象了”,但是我们也不用去管他,因为它具体的内部如何实现的和我们无关,只要我们能够学会用它相关的API把这个完成端口的框架搭建起来就可以了。我们暂时只用把它大体理解为一个容纳网络通信操作的队列就好了,它会把网络操作完成的通知,都放在这个队

列里面,咱们只用从这个队列里面取就行了,取走一个就少一个…。

关于完成端口内核对象的具体更多内部细节我会在后面的“完成端口的基本原理”一节更详细的和朋友们一起来研究,当然,要是你们在文章中没有看到这一节的话,就是说明我又犯懒了没写…在后续的文章里我会补上。这里就暂时说这么多了,到时候我们也可以看到它的机制也并非有那么的复杂,可能只是因为操作系统其他的内核对象相比较而言实现起来太容易了吧^_^

四.完成端口的基本流程

说了这么多的废话,大家都等不及了吧,我们终于到了具体编码的时候了。

使用完成端口,说难也难,但是说简单,其实也简单----又说了一句废话=。=

大体上来讲,使用完成端口只用遵循如下几个步骤:

(1) 调用CreateIoCompletionPort()函数创建一个完成端口,而且在一般情况下,我们需要且只需要建立这一个完成端口,把它的句柄保存好,我们今后会经常用到它……

(2) 根据系统中有多少个处理器,就建立多少个工作者(为了醒目起见,下面直接说Worker)线程,这几个线程是专门用来和客户端进行通信的,目前暂时没什么工作;

(3) 下面就是接收连入的Socket连接了,这里有两种实现方式:一是和别的编程模型一样,还需要启动一个独立的线程,专门用来accept客户端的连接请求;二是用性能更高更好的异步AcceptEx()请求,因为各位对accept用法应该非常熟悉了,而且网上资料也会很多,所以为了更全面起见,本文采用的是性能更好的AcceptEx,至于两者代码编写上的区别,我接下来会详细的讲。

(4) 每当有客户端连入的时候,我们就还是得调用CreateIoCompletionPort()函数,这里却不是新建立完成端口了,而是把新连入的Socket(也就是前面所谓的设备句柄),与目前的完成端口绑定在一起。

至此,我们其实就已经完成了完成端口的相关部署工作了,嗯,是的,完事了,后面的代码里我们就可以充分享受完成端口带给我们的巨大优势,坐享其成了,是不是很简单呢?

(5)例如,客户端连入之后,我们可以在这个Socket上提交一个网络请求,例如WSARecv(),然后系统就会帮咱们乖乖的去执行接收数据的操作,我们大可以放心的去干别的事情了;

(6)而此时,我们预先准备的那几个Worker线程就不能闲着了,我们在前面建立的几个Worker就要忙活起来了,都需要分别调用GetQueuedCompletionStatus()函数在扫描完成端口的队列里是否有网络通信的请求存在(例如读取数据,发送数据等),一旦有的话,就将这个请求从完成端口的队列中取回来,继续执行本线程中后面的处理代码,处理完毕之后,我们再继续投递下一个网络通信的请求就OK了,如此循环。

关于完成端口的使用步骤,用文字来表述就是这么多了,很简单吧?如果你还是不理解,我再配合一个流程图来表示一下:

当然,我这里假设你已经对网络编程的基本套路有了解了,所以略去了很多基本的细节,并且为了配合朋友们更好的理解我的代码,在流程图我标出了一些函数的名字,另外需要注意的是由于对于客户端的连入有两种方式,一种是普通阻塞的accept,另外一种是性能更好的AcceptEx,为了能够方面朋友们从别的网络编程的方式中过渡,我这里画了两种方式的流程图,方便朋友们对比学习,图1是使用accept 的方式,当然配套的源代码我默认就不提供了,如果需要的话,我倒是也可以发上来;图2是使用AcceptEx 的,并配有配套的源码:

图1 使用accept进行完成端口通信的流程图

图2 使用AcceptEx进行完成端口通信的流程图

两个图中最大的相同点是什么?是的,最大的相同点就是主线程无所事事,闲得蛋疼……

为什么呢?因为我们使用了异步的通信机制,这些琐碎重复的事情完全没有必要交给主线程自己来做了,只用在初始化的时候和Worker线程交待好就可以了,用一句话来形容就是,主线程永远也体会不到Worker线程有多忙,而Worker线程也永远体会不到主线程在初始化建立起这个通信框架的时候操了多少的心……

图1中是由_AcceptThread()负责接入连接,并把连入的Socket和完成端口绑定,另外的多个_WorkerThread()就负责监控完成端口上的情况,一旦有情况了,就取出来处理,如果CPU有多核的话,

就可以多个线程轮着来处理完成端口上的信息,很明显效率就提高了。

图2中最明显的区别,也就是AcceptEx和传统的accept之间最大的区别,就是取消了阻塞方式的accept调用,也就是说,AcceptEx也是通过完成端口来异步完成的,所以就取消了专门用于accept连接的线程,用了完成端口来进行异步的AcceptEx调用;然后在检索完成端口队列的Worker函数中,根据用户投递的完成操作的类型,再来找出其中的投递的Accept请求,加以对应的处理。

读者一定会问,这样做的好处在哪里?为什么还要异步的投递AcceptEx连接的操作呢?

首先,我可以很明确的告诉各位,如果短时间内客户端的并发连接请求不是特别多的话,用accept 和AcceptEx在性能上来讲是没什么区别的。

按照我们目前主流的PC来讲,如果客户端只进行连接请求,而什么都不做的话,我们的Server只能接收大约3万-4万个左右的并发连接,然后客户端其余的连入请求就只能收到WSAENOBUFS (10055)了,因为系统来不及为新连入的客户端准备资源了。

需要准备什么资源?当然是准备Socket了……虽然我们创建Socket只用一行SOCKET s= socket(…) 这么一行的代码就OK了,但是系统内部建立一个Socket是相当耗费资源的,因为Winsock2是分层的机构体系,创建一个Socket需要到多个Provider之间进行处理,最终形成一个可用的套接字。总之,系统创建一个Socket的花费是比较高的,所以用accept的话,系统可能来不及为更多的并发客户端准备Socket了。

而AcceptEx比Accept又强大在哪里呢?是有三点:

(1) 这个好处是最关键的,是因为AcceptEx是在客户端连入之前,就把客户端的Socket建立好了,也就是说,AcceptEx是先建立的Socket,然后才发出的AcceptEx调用,也就是说,在进行客户端的通信之前,无论是否有客户端连入,Socket都是提前建立好了;而不需要像accept是在客户端连入了之后,再现场去花费时间建立Socket。如果各位不清楚是如何实现的,请看后面的实现部分。

(2) 相比accept只能阻塞方式建立一个连入的入口,对于大量的并发客户端来讲,入口实在是有点挤;而AcceptEx可以同时在完成端口上投递多个请求,这样有客户端连入的时候,就非常优雅而且从容不迫的边喝茶边处理连入请求了。

(3) AcceptEx还有一个非常体贴的优点,就是在投递AcceptEx的时候,我们还可以顺便在AcceptEx 的同时,收取客户端发来的第一组数据,这个是同时进行的,也就是说,在我们收到AcceptEx完成的通知的时候,我们就已经把这第一组数据接完毕了;但是这也意味着,如果客户端只是连入但是不发送数据的话,我们就不会收到这个AcceptEx完成的通知……这个我们在后面的实现部分,也可以详细看到。

总而言之,对于普通的PC机来讲,我们只有使用AcceptEx,才可以使我们的并发处理的连接请求达到单主机65000左右的上限。

什么?超过这个上限?No,no,无论你换成多么强大的Server,也不可能超过65535的上限的,因为各位可以去看下表示地址信息的SOCKADDR_IN的定义,对于端口的部分,使用的是unsigned short,这也就意味着端口编号的上限就是65535了,另外系统自己的各种服务还会占用一些端口,所以65000基本就是极限值了。

当然,我说的是IPV4,IPV6就另当别论了。

最后,各位要有一个心里准备,相比accept,异步的AcceptEx使用起来要麻烦得多……

五.完成端口的实现详解

又说了一节的废话,终于到了该动手实现的时候了……

这里我把完成端口的详细实现步骤以及会涉及到的函数,按照出现的先后步骤,都和大家详细的说明解释一下,当然,文档中为了让大家便于阅读,这里去掉了其中的错误处理的内容,当然,这些内容在示例代码中是会有的。

【第一步】创建一个完成端口

首先,我们先把完成端口建好再说。

我们正常情况下,我们需要且只需要建立这一个完成端口,代码很简单:

HANDLE m_hIOCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 );

呵呵,看到CreateIoCompletionPort()的参数不要奇怪,参数就是一个INVALID,一个NULL,两个0…,说白了就是一个-1,三个0……简直就和什么都没传一样,但是Windows系统内部却是好一顿忙活,把完成端口相关的资源和数据结构都已经定义好了(在后面的原理部分我们会看到,完成端口相关的数据结构大部分都是一些用来协调各种网络I/O的队列),然后系统会给我们返回一个有意义的HANDLE,只要返回值不是NULL,就说明建立完成端口成功了,就这么简单,不是吗?

有的时候我真的很赞叹Windows API的封装,把很多其实是很复杂的事整得这么简单……

至于里面各个参数的具体含义,我会放到后面的步骤中去讲,反正这里只要知道创建我们唯一的这个完成端口,就只是需要这么几个参数。

但是对于最后一个参数0,我这里要简单的说两句,这个0可不是一个普通的0,它代表的是NumberOfConcurrentThreads,也就是说,允许应用程序同时执行的线程数量。当然,我们这里为了避免上下文切换,最理想的状态就是每个处理器上只运行一个线程了,所以我们设置为0,就是说有多少个处理器,就允许同时多少个线程运行。

因为比如一台机器只有两个CPU(或者两个核心),如果让系统同时运行的线程多于本机的CPU数量的话,那其实是没有什么意义的事情,因为这样CPU就不得不在多个线程之间执行上下文切换,这会浪费宝贵的CPU周期,反而降低的效率,我们要牢记这个原则。

【第二步】根据系统中CPU核心的数量建立对应的Worker线程

我们前面已经提到,这个Worker线程很重要,是用来具体处理网络请求、具体和客户端通信的线程,而且对于线程数量的设置很有意思,要等于系统中CPU的数量,那么我们就要首先获取系统中CPU的数量,这个是基本功,我就不多说了,代码如下:

SYSTEM_INFO si;

GetSystemInfo(&si);

int m_nProcessors = si.dwNumberOfProcessors;

这样我们根据系统中CPU的核心数量来建立对应的线程就好了,下图是在我的i7 2600k CPU上初始化的情况,因为我的CPU是8核,一共启动了16个Worker线程,如图3所示

图3 在i7 2600上初始化Worker线程示意图

啊,等等!各位没发现什么问题么?为什么我8核的CPU却启动了16个线程?这个不是和我们第二步中说的原则自相矛盾了么?

哈哈,有个小秘密忘了告诉各位了,江湖上都流传着这么一个公式,就是:

我们最好是建立CPU核心数量*2那么多的线程,这样更可以充分利用CPU资源,因为完成端口的调度是非常智能的,比如我们的Worker线程有的时候可能会有Sleep()或者WaitForSingleObject()之类的情况,这样同一个CPU核心上的另一个线程就可以代替这个Sleep的线程执行了;因为完成端口的目标是要使得CPU满负荷的工作。

这里也有人说是建立CPU“核心数量* 2 +2”个线程,我想这个应该没有什么太大的区别,我就是按照我自己的习惯来了。

然后按照这个数量,来启动这么多个Worker线程就好可以了,接下来我们开始下一个步骤。

什么?Worker线程不会建?

…囧…

Worker线程和普通线程是一样一样一样的啊~~~,代码大致上如下:

// 根据CPU数量,建立*2的线程

m_nThreads = 2 * m_nProcessors;

HANDLE* m_phWorkerThreads = new HANDLE[m_nThreads];

for (int i = 0; i < m_nThreads; i++)

{

m_phWorkerThreads[i] = ::CreateThread(0, 0, _WorkerThread, …);

}

其中,_WorkerThread是Worker线程的线程函数,线程函数的具体内容我们后面再讲。

【第三步】创建一个用于监听的Socket,绑定到完成端口上,然后开始在指定的端口上监听连接请求

最重要的完成端口建立完毕了,我们就可以利用这个完成端口来进行网络通信了。

首先,我们需要初始化Socket,这里和通常情况下使用Socket初始化的步骤都是一样的,大约就是如下的这么几个过程(详情参照我代码中的LoadSocketLib()和InitializeListenSocket(),这里只是挑出关键部分):

// 初始化Socket库

WSADATA wsaData;

WSAStartup(MAKEWORD(2,2), &wsaData);

// 初始化Socket

struct sockaddr_in ServerAddress;

// 这里需要特别注意,如果要使用重叠I/O的话,这里必须要使用WSASocket来初始化Socket

// 注意里面有个WSA_FLAG_OVERLAPPED参数

SOCKET m_sockListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

// 填充地址结构信息

ZeroMemory((char *)&ServerAddress, sizeof(ServerAddress));

ServerAddress.sin_family = AF_INET;

// 这里可以选择绑定任何一个可用的地址,或者是自己指定的一个IP地址

//ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);

ServerAddress.sin_addr.s_addr = inet_addr(“你的IP”);

ServerAddress.sin_port = htons(11111);

// 绑定端口

if (SOCKET_ERROR == bind(m_sockListen, (struct sockaddr *) &ServerAddress, sizeof(ServerAddress)))

// 开始监听

listen(m_sockListen,SOMAXCONN))

需要注意的地方有两点:

(1) 想要使用重叠I/O的话,初始化Socket的时候一定要使用WSASocket并带上WSA_FLAG_OVERLAPPED参数才可以;

(2) 注意到listen函数后面用的那个常量SOMAXCONN了吗?这个是在微软在WinSock2.h中定义的,并且还附赠了一条注释,Maximum queue length specifiable by listen.,所以说,不用白不用咯^_^

接下来有一个非常重要的动作:既然我们要使用完成端口来帮我们进行监听工作,那么我们一定要把这个监听Socket和完成端口绑定才可以的吧:

如何绑定呢?同样很简单,用CreateIoCompletionPort()函数。

等等!大家没觉得这个函数很眼熟么?是的,这个和前面那个创建完成端口用的居然是同一个API!但是这里这个API可不是用来建立完成端口的,而是用于将Socket和以前创建的那个完成端口绑定的,大家可要看准了,不要被迷惑了,因为他们的参数是明显不一样的,前面那个的参数是一个-1,三个0,太好记了…

说实话,我感觉微软应该把这两个函数分开,弄个CreateNewCompletionPort() 多好呢?

这里在详细讲解一下CreateIoCompletionPort()的几个参数:

HANDLE WINAPI CreateIoCompletionPort(

__in HANDLE FileHandle, // 这里当然是连入的这个套接字句柄了

__in_opt HANDLE ExistingCompletionPort, // 这个就是前面创建的那个完成端口

__in ULONG_PTR CompletionKey, // 这个参数就是类似于线程参数一样,在

// 绑定的时候把自己定义的结构体指针传递

// 这样到了Worker线程中,也可以使用这个

// 结构体的数据了,相当于参数的传递__in DWORD NumberOfConcurrentThreads // 这里同样置0

);

这些参数也没什么好讲的吧,用处一目了然了。而对于其中的那个CompletionKey,我们后面会详细提到。

到此才算是Socket全部初始化完毕了。

初始化Socket完毕之后,就可以在这个Socket上投递AcceptEx请求了。

【第四步】在这个监听Socket上投递AcceptEx请求

这里的处理比较复杂。

这个AcceptEx比较特别,而且这个是微软专门在Windows操作系统里面提供的扩展函数,也就是说这个不是Winsock2标准里面提供的,是微软为了方便咱们使用重叠I/O机制,额外提供的一些函数,所以在使用之前也还是需要进行些准备工作。

微软的实现是通过mswsock.dll中提供的,所以我们可以通过静态链接mswsock.lib来使用AcceptEx。但是这是一个不推荐的方式,我们应该用WSAIoctl配合SIO_GET_EXTENSION_FUNCTION_POINTER参数来获取函数的指针,然后再调用AcceptEx。

这是为什么呢?因为我们在未取得函数指针的情况下就调用AcceptEx的开销是很大的,因为AcceptEx 实际上是存在于Winsock2结构体系之外的(因为是微软另外提供的),所以如果我们直接调用AcceptEx的话,首先我们的代码就只能在微软的平台上用了,没有办法在其他平台上调用到该平台提供的AcceptEx的版本(如果有的话),而且更糟糕的是,我们每次调用AcceptEx时,Service Provider都得要通过WSAIoctl()获取一次该函数指针,效率太低了,所以还不如我们自己直接在代码中直接去这么获取一下指针好了。

获取AcceptEx函数指针的代码大致如下:

LPFN_ACCEPTEX m_lpfnAcceptEx;// AcceptEx函数指针

GUID GuidAcceptEx = WSAID_ACCEPTEX; // GUID,这个是识别AcceptEx函数必须的

DWORD dwBytes = 0;

WSAIoctl(

m_pListenContext->m_Socket,

SIO_GET_EXTENSION_FUNCTION_POINTER,

&GuidAcceptEx,

sizeof(GuidAcceptEx),

&m_lpfnAcceptEx,

sizeof(m_lpfnAcceptEx),

&dwBytes,

NULL,

NULL);

具体实现就没什么可说的了,因为都是固定的套路,那个GUID是微软给定义好的,直接拿过来用就行了,WSAIoctl()就是通过这个找到AcceptEx的地址的,另外需要注意的是,通过WSAIoctl获取AcceptEx函数指针时,只需要随便传递给WSAIoctl()一个有效的SOCKET即可,该Socket的类型不会影响获取的AcceptEx函数指针。

然后,我们就可以通过其中的指针m_lpfnAcceptEx调用AcceptEx函数了。

AcceptEx函数的定义如下:

BOOL AcceptEx (

SOCKET sListenSocket,

SOCKET sAcceptSocket,

PVOID lpOutputBuffer,

DWORD dwReceiveDataLength,

DWORD dwLocalAddressLength,

DWORD dwRemoteAddressLength,

LPDWORD lpdwBytesReceived,

LPOVERLAPPED lpOverlapped

);

乍一看起来参数很多,但是实际用起来也很简单:

●参数1--sListenSocket, 这个就是那个唯一的用来监听的Socket了,没什么说的;

●参数2--sAcceptSocket, 用于接受连接的socket,这个就是那个需要我们事先建好的,等有客

户端连接进来直接把这个Socket拿给它用的那个,是AcceptEx高性能的关键所在。

●参数3--lpOutputBuffer,接收缓冲区,这也是AcceptEx比较有特色的地方,既然AcceptEx不是

普通的accpet函数,那么这个缓冲区也不是普通的缓冲区,这个缓冲区包含了三个信息:一是客

户端发来的第一组数据,二是server的地址,三是client地址,都是精华啊…但是读取起来就会

很麻烦,不过后面有一个更好的解决方案。

●参数4--dwReceiveDataLength,前面那个参数lpOutputBuffer中用于存放数据的空间大小。

如果此参数=0,则Accept时将不会待数据到来,而直接返回,如果此参数不为0,那么一定得

等接收到数据了才会返回……所以通常当需要Accept接收数据时,就需要将该参数设成为:

sizeof(lpOutputBuffer) - 2*(sizeof sockaddr_in +16),也就是说总长度减去两个地址空间

的长度就是了,看起来复杂,其实想明白了也没啥……

●参数5--dwLocalAddressLength,存放本地址地址信息的空间大小;

●参数6--dwRemoteAddressLength,存放本远端地址信息的空间大小;

●参数7--lpdwBytesReceived,out参数,对我们来说没用,不用管;

●参数8--lpOverlapped,本次重叠I/O所要用到的重叠结构。

这里面的参数倒是没什么,看起来复杂,但是咱们依旧可以一个一个传进去,然后在对应的IO操作完成之后,这些参数Windows内核自然就会帮咱们填满了。

但是非常悲催的是,我们这个是异步操作,我们是在线程启动的地方投递的这个操作,等我们再次见到这些个变量的时候,就已经是在Worker线程内部了,因为Windows会直接把操作完成的结果传递到Worker线程里,这样咱们在启动的时候投递了那么多的IO请求,这从Worker线程传回来的这些结果,到底是对应着哪个IO请求的呢?。。。。

聪明的你肯定想到了,是的,Windows内核也帮我们想到了:用一个标志来绑定每一个IO操作,这样到了Worker线程内部的时候,收到网络操作完成的通知之后,再通过这个标志来找出这组返回的数据到底对应的是哪个Io操作的。

这里的标志就是如下这样的结构体:

typedefstruct_PER_IO_CONTEXT

{

OVERLAPPED m_Overlapped; // 每一个重叠I/O网络操作都要有一个

SOCKET m_sockAccept; // 这个I/O操作所使用的Socket,每个连接的都是一样的

WSABUF m_wsaBuf; // 存储数据的缓冲区,用来给重叠操作传递参数的,关于WSABUF

// 后面还会讲

char m_szBuffer[MAX_BUFFER_LEN]; // 对应WSABUF里的缓冲区

OPERATION_TYPE m_OpType; // 标志这个重叠I/O操作是做什么的,例如Accept/Recv等

} PER_IO_CONTEXT, *PPER_IO_CONTEXT;

这个结构体的成员当然是我们随便定义的,里面的成员你可以随意修改(除了OVERLAPPED那个之外……)。

但是AcceptEx不是普通的accept,buffer不是普通的buffer,那么这个结构体当然也不能是普通的结构体了……

在完成端口的世界里,这个结构体有个专属的名字“单IO数据”,是什么意思呢?也就是说每一个重叠I/O都要对应的这么一组参数,至于这个结构体怎么定义无所谓,而且这个结构体也不是必须要定义的,但是没它……还真是不行,我们可以把它理解为线程参数,就好比你使用线程的时候,线程参数也不是必须的,但是不传还真是不行……

除此以外,我们也还会想到,既然每一个I/O操作都有对应的PER_IO_CONTEXT结构体,而在每一个Socket上,我们会投递多个I/O请求的,例如我们就可以在监听Socket上投递多个AcceptEx请求,所以同样的,我们也还需要一个“单句柄数据”来管理这个句柄上所有的I/O请求,这里的“句柄”

当然就是指的Socket了,我在代码中是这样定义的:

typedef struct _PER_SOCKET_CONTEXT

{

SOCKET m_Socket; // 每一个客户端连接的Socket

SOCKADDR_IN m_ClientAddr; // 这个客户端的地址

CArray<_PER_IO_CONTEXT*>m_array IoContext; // 数组,所有客户端IO操作的参数,

// 也就是说对于每一个客户端Socket,是可以在上面同时投递多个IO请求的

} PER_SOCKET_CONTEXT, *PPER_SOCKET_CONTEXT;

这也是比较好理解的,也就是说我们需要在一个Socket句柄上,管理在这个Socket上投递的每一个IO请求的_PER_IO_CONTEXT。

当然,同样的,各位对于这些也可以按照自己的想法来随便定义,只要能起到管理每一个IO请求上需要传递的网络参数的目的就好了,关键就是需要跟踪这些参数的状态,在必要的时候释放这些资源,不要造成内存泄漏,因为作为Server总是需要长时间运行的,所以如果有内存泄露的情况那是非常可怕的,一定要杜绝一丝一毫的内存泄漏。

至于具体这两个结构体参数是如何在Worker线程里大发神威的,我们后面再看。

以上就是我们全部的准备工作了,具体的实现各位可以配合我的流程图再看一下示例代码,相信应该会理解得比较快。

完成端口初始化的工作比起其他的模型来讲是要更复杂一些,所以说对于主线程来讲,它总觉得自己付出了很多,总觉得Worker线程是坐享其成,但是Worker自己的苦只有自己明白,Worker线程的工作一点也不比主线程少,相反还要更复杂一些,并且具体的通信工作全部都是Worker线程来完成的,Worker线程反而还觉得主线程是在旁边看热闹,只知道发号施令而已,但是大家终究还是谁也离不开谁,这也就和公司里老板和员工的微妙关系是一样的吧……

【第五步】我们再来看看Worker线程都做了些什么

_Worker线程的工作都是涉及到具体的通信事务问题,主要完成了如下的几个工作,让我们一步一步的来看。

(1) 使用GetQueuedCompletionStatus() 监控完成端口

首先这个工作所要做的工作大家也能猜到,无非就是几个Worker线程哥几个一起排好队队来监视完成端口的队列中是否有完成的网络操作就好了,代码大体如下:

void *lpContext = NULL;

OVERLAPPED *pOverlapped = NULL;

DWORD dwBytesTransfered = 0;

BOOL bReturn = GetQueuedCompletionStatus(

pIOCPModel->m_hIOCompletionPort,

&dwBytesTransfered,

(LPDWORD)&lpContext,

&pOverlapped,

INFINITE);

各位留意到其中的GetQueuedCompletionStatus()函数了吗?这个就是Worker线程里第一件也是最重要的一件事了,这个函数的作用就是我在前面提到的,会让Worker线程进入不占用CPU的睡眠状态,直到完成端口上出现了需要处理的网络操作或者超出了等待的时间限制为止。

一旦完成端口上出现了已完成的I/O请求,那么等待的线程会被立刻唤醒,然后继续执行后续的代码。

至于这个神奇的函数,原型是这样的:

BOOL WINAPI GetQueuedCompletionStatus(

__in HANDLE CompletionPort, // 这个就是我们建立的那个唯一的完成端口

__out LPDWORD lpNumberOfBytes,//这个是操作完成后返回的字节数

__out PULONG_PTR lpCompletionKey,// 这个是我们建立完成端口的时候绑定的

// 那个自定义结构体参数

__out LPOVERLAPPED *lpOverlapped,// 这个是我们在连入Socket的时候一起建// 立

的那个重叠结构

__in DWORD dwMilliseconds// 等待完成端口的超时时间,如果线程不需//

要做其他的事情,那就INFINITE就行了);

所以,如果这个函数突然返回了,那就说明有需要处理的网络操作了--- 当然,在没有出现错误的情况下。

然后switch()一下,根据需要处理的操作类型,那我们来进行相应的处理。

但是如何知道操作是什么类型的呢?这就需要用到从外部传递进来的loContext参数,也就是我们封装的那个参数结构体,这个参数结构体里面会带有我们一开始投递这个操作的时候设置的操作类型,然后我们根据这个操作再来进行对应的处理。

但是还有问题,这个参数究竟是从哪里传进来的呢?传进来的时候内容都有些什么?

这个问题问得好!

首先,我们要知道两个问题:

(1) 这个参数,是在你绑定Socket到一个完成端口的时候,用的CreateIoCompletionPort()函数,传入的那个CompletionKey参数,要是忘了的话,就翻到文档的“第三步”看看相关的内容;我们在这里传入的是定义的PER_SOCKET_CONTEXT,也就是说“单句柄数据”,因为我们绑定的是一个Socket,这里自然也就需要传入Socket相关的上下文,你是怎么传过去的,这里收到的就会是什么样子,也就是说这个lpCompletionKey就是我们的PER_SOCKET_CONTEXT,直接把里面的数据拿出来用就可以了。

(2) 另外还有一个很神奇的地方,里面的那个lpOverlapped参数,里面就带有我们的PER_IO_CONTEXT。这个参数是从哪里来的呢?我们去看看前面投递AcceptEx请求的时候,是不是传了一个重叠参数进去?这里就是它了,并且,我们可以使用一个很神奇的宏,把和它存储在一起的其他的变量,全部都读取出来,例如:

PER_IO_CONTEXT* pIoContext = CONTAINING_RECORD(lpOverlapped, PER_IO_CONTEXT, m_Overlapped);

这个宏的含义,就是去传入的lpOverlapped变量里,找到和结构体中PER_IO_CONTEXT中m_Overlapped成员相关的数据。

你仔细想想,其实真的很神奇……

但是要做到这种神奇的效果,应该确保我们在结构体PER_IO_CONTEXT定义的时候,把Overlapped变量,定义为结构体中的第一个成员。

只要各位能弄清楚这个GetQueuedCompletionStatus()中各种奇怪的参数,那我们就离成功不远了。

既然我们可以活得PER_IO_CONTEXT结构体,那么我们就自然可以根据其中的m_OpType参数,得知这次收到的这个完成通知,是关于哪个Socket上的哪个I/O操作的,这样就分别进行对应处理就好了。

在我的示例代码里,在有AcceptEx请求完成的时候,我是执行的_DoAccept()函数,在有WSARecv 请求完成的时候,执行的是_DoRecv()函数,下面我就分别讲解一下这两个函数的执行流程。

【第六步】当收到Accept通知时 _DoAccept()

在用户收到AcceptEx的完成通知时,需要后续代码并不多,但却是逻辑最为混乱,最容易出错的地方,这也是很多用户为什么宁愿用效率低下的accept()也不愿意去用AcceptEx的原因吧。

和普通的Socket通讯方式一样,在有客户端连入的时候,我们需要做三件事情:

(1) 为这个新连入的连接分配一个Socket;

(2) 在这个Socket上投递第一个异步的发送/接收请求;

(3) 继续监听。

其实都是一些很简单的事情但是由于“单句柄数据”和“单IO数据”的加入,事情就变得比较乱。因为是这样的,让我们一起缕一缕啊,最好是配合代码一起看,否则太抽象了……

(1) 首先,_Worker线程通过GetQueuedCompletionStatus()里会收到一个lpCompletionKey,这个也就是PER_SOCKET_CONTEXT,里面保存了与这个I/O相关的Socket和Overlapped还有客户端发来的第一组数据等等,对吧?但是这里得注意,这个SOCKET的上下文数据,是关于监听Socket 的,而不是新连入的这个客户端Socket的,千万别弄混了……

(2) 所以,AcceptEx不是给咱们新连入的这个Socket早就建好了一个Socket吗?所以这里,我们需要再用这个新Socket重新为新客户端建立一个PER_SOCKET_CONTEXT,以及下面一系列的新PER_IO_CONTEXT,千万不要去动传入的这个Listen Socket上的PER_SOCKET_CONTEXT,也不要用传入的这个Overlapped信息,因为这个是属于AcceptEx I/O操作的,也不是属于你投递的那个Recv I/O操作的……,要不你下次继续监听的时候就悲剧了……

(3) 等到新的Socket准备完毕了,我们就赶紧还是用传入的这个Listen Socket上的PER_SOCKET_CONTEXT和PER_IO_CONTEXT去继续投递下一个AcceptEx,循环起来,留在这里太危险了,早晚得被人给改了……

(4) 而我们新的Socket的上下文数据和I/O操作数据都准备好了之后,我们要做两件事情:一件事

零基础魔方入门教程.看完这个你就能复原一个魔方

零基础魔方入门教程.看完这个你就能复原一个魔方 希望本日志能给你带来关于魔方的兴趣,普及魔方运动 是每一名Cuber的责任~~真心希望会有更多人热爱这个运动. 该教程适合零基础以上的人,会复原以下的人.看完这个教程都能学会复原.I promise. 1.讲几个单词吧,魔方的英文是Rubik's Cube 译为鲁比克方块,Cuber就是魔方玩家的意思.. 2.魔方标准配色这个新手不用记,架十字的时候再找就 行.(如果不清楚架十字是什么意思没有关系,继续往下看,不 耽误) 简单了解一下,上面图片就是魔方的标准配色----上黄下白,前蓝后绿,左橙右红. (说了可以不用背下来,知道有这个东西就行,继续往下看) 3.魔方基本构造大家能了 解棱块,角块,中心块这三个概念分别指什么就行了,因为下面的教程会提到,不用管支架. 然后是区分"面"和"层":面是指一个平面的3 x 3块,层是指一个平面3 x 3块所处3 x 3 x 1块. 举个例子:我们旋转的是一层,复原的是六面(面,层这个东西不理解也没关系,没什么用,大概那个意思就行了,继续往下不用纠结...) 4.魔方算法(公式)符号体系/魔方在架十字和第一层的时候是可以靠理解的,但是后面需要算法,就是很多人说的公式,(注意,教程里的"算法"就是通常说的"公式",理解成一个意思就行,用算法更恰当一些其实.)大家通常会比较费解在于---我前面做好了再做后面的时候,怎么能不破坏

前面呢?是的,正是因为只有极少数人能想出来后面该怎么做还不会破坏前面,所以需要直接记住算法.这里的算法是个什么意思呢?举个例子,你从教学楼到宿舍,会走一条路,这就是一个算法,就是你每次在教学楼想要回宿舍就走这条路,魔方是一样的,你每次看到一种情况就用同样的方法解决它,就是要背一条算法,为什么要背,因为你想不出..所以不得不背,背了以后你下次都会解决这种情况了.不过放心,初级公式情况不多,可以说是--非常少^^ 所以不要担心你掌握不了~/ 那就来介绍一下符号体系吧!很简单,就是英文首字母. F = front 前面B = back 后面L = left 左面R = right 右面U = up 上面D = down 下面转法如下: (1) 字母代表该面上顺时针转动90度. (2) 有'的代表逆时针. p.s. 字母后面跟个2就是两次,即180度. 练习: (R U R’) 这里贴不了视频,我就给大家说说,这个算法就是:右边顺时针90度,顶层顺时针90度,右边逆时针90度.. 然后再介绍一下整体转动的概念: 看图就好了,相信大家都懂,字母就是顺时针,加' 的就是逆时针. /这个大家可以看看,如果不想看也可 以先不看,因为我设计的初级公式不需要整体转动,因此你要是懒得看,那就跳过~/ 如下: x---(整个魔方以R的方向转动) y---(整个魔方以U的方向转动) z---(整个魔方以F的方向转动) 5.魔方复原过程恭喜你,现在你已经掌握了足够的

TCP的客户 服务器 端口扫描程序设计

海南大学信息科学技术学院《安全扫描技术》 TCP的客户/服务器/端口扫描程序设计 学号: ______ 姓名: 年级: 2010级 __________ 专业:信息安全 ______ 指导老师:顾剑 ____

目录 1实验目的及要求 (1) 2实验的背景及意义 (1) 3实验流程 (1) 4实验内容与步骤 (3) 5实验代码 (5) 5.1 TCP服务器程序: (5) 5.2 TCP客户端: (8) 5.3 TCP端口扫描: (10) 6实验操作手册 (11) 7实验总结 (14)

第 1 页共17 页 1实验目的及要求 (1)、熟悉Microsoft Visual Studio 2006编程环境。 (2)、了解TCP客户/服务器/扫描端口的模型原理。 (3)、熟悉Socket编程原理,掌握简单的套接字编程。 2实验的背景及意义 (1)、TCP客户和服务器 TCP是面向连接的,所谓面向连接,就是当计算机双方通信时必需先建立连接,然后数据传送,最后拆除连接三个过程并且TCP在建立连接时又分三步走: 第一步是请求端(客户端)发送一个包含SYN即同步(Synchronize)标志的TCP报文,SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号; 第二步,服务器在收到客户端的SYN报文后,将返回一个SYN+ACK的报文,表示客户端的请求被接受,同时TCP 序号被加一,ACK即确认(Acknowledgement)。 第三步,客户端也返回一个确认报文ACK给服务器端,同样TCP序列号被加一,到此一个TCP连接完成。然后才开始通信的第二步:数据处理。 这就是所说的TCP三次握手(Three-way Handshake)。简单的说就是:(C:客户端,S:服务端) C:SYN到S S:如成功--返回给C(SYN+ACK) C:如成功---返回给S(ACK)以上是正常的建立连接方式(2)、TCP端口扫描 “端口”在计算机网络领域中是个非常重要的概念。它是专门为计算机通信而设计的,它不是硬件,不同于计算机中的“插槽”,可以说是个“软插槽”。如果有需要的话,一台计算机中可以有上万个端口。 端口是由TCP/IP协议定义的。其中规定,用IP地址和端口作为套接字,它代表TCP连接的一个连接端,一般称为Socket。具体来说,就是用[IP:端口]来定位一台主机中的进程。可以做这样的比喻,端口相当于两台计算机进程间的大门,可以随便定义,其目的只是为了让两台计算机能够找到对方的进程。计算机就像一座大楼,这个大楼有好多入口(端口),进到不同的入口中就可以找到不同的公司(进程)。如果要和远程主机A的程序通信,那么只要把数据发向[A:端口]就可以实现通信了。 可见,端口与进程是一一对应的,如果某个进程正在等待连接,称之为该进程正在监听,那么就会出现与它相对应的端口。由此可见,入侵者通过扫描端口,便可以判断出目标计算机有哪些通信进程正在等待连接,这也是端口扫描的主要目的。 3实验流程 (1)、TCP客户程序和服务器程序流程图 程序分两部分:客户程序和服务器程序。 工作过程是:服务器首先启动,它创建套接字之后等待客户的连接;客户启动后创建套接字,然后和服务器建立连接;建立连接后,客户接收键盘输入,然后将数据发送到服务器,服务器收到到数据后,将接收到的字符在屏幕上显示出来。或者服务器接收键盘输入,然后将数据发送到客户机,客户机收到数据后,将接收到的字符在屏幕上显示出来。

手把手教你玩刺客凤舞天骄

?把?教你玩刺客凤舞天骄 看了很多朋友写了关于刺客的?章,我也来说?句! 选择刺客我只推荐两种:??刺和??刺! 我觉得刺客刺客当中,?系和?系的技能是?可挑剔的!?系的两个眩晕,?系的落?贯穿整个连招,??呵成! 刺客在战???我认为是全能的,虽然没有战????防,没有法师的?攻,但是我们既可远程亦可近攻!很多玩了?天刺客的朋友都说练级如何如何难,P K如何如何菜.这?我强调?点刺客45级才成形!不论多么艰难请你坚持坚持再坚持,眼光总在风?后! 偶先说说??刺吧,P K?战有点难对付,毕竟克我!胜率60-70%!65+的??战别和他硬来,他?再多,我们克他,游击?下就搞定了!?战和?战还没输过!打?法,别让他近?,如果副?的,被?技能绑定就马上跑开,对攻的话不怕他,他攻击没我们?!打?法,注意他的?球,最好?静?避开!其他法师?等级的没见过! ?于??刺嘛,个?感觉就?等级的??战难打点,好象有种杀不死他的感觉!始终??刺到60以后攻击跟不上了!但是??刺打他又相对简单! 刺客P K刺客,看谁的M I S S多,破招多,前期?刺可能会?直被?刺克,但60以后也不好说,刺客讲的就是操作! 下?我谈谈刺客的练法!4个字:越级杀怪!从新?村出来到?论你玩到什么时候,都是这样!我都是杀?我?7,8级的怪.有地?卡的时候?10多级也杀,M I S S不多经验又?,舒服!(单练)有朋友组当然更好,毕竟游戏讲的是团队合作!55级以前?直都有任务装备,别为了追求视觉效果盲??搞装备,不划算!55以后如果你声望?就穿声望装,不然就收集套装绿材!(最好花1/3的时间在收集材料上,做好?套装备?你多练10级效果都好)条件允许的最好做绿套!我有个朋友穿上55级的?系绿套,会?20%+,再加上他是?刺,攻击可怖啊!59级杀75以下的职业轻松哦! 这?我不会和?家说刺客该去什么什么地?练级最快,如果你?天12个?时都在练级的话,那么你练什么职业都快!有的朋友喜欢泡市场,有的喜欢跑任务,有的喜欢聊天,有的喜欢P K.如果你是其中之?的话就别抱怨??等级低!?较下两种刺客:?刺后期渐渐赶上?刺,?刺60以后攻击可能有点跟不上,但这两种职业我都?常喜欢,?于练哪种看个?喜好了!建议没玩过刺客又想玩刺客的朋友,坚持,坚持,再坚持. 逗游?——中国2亿游戏?户?致选择的”?站式“游戏服务平台

教你玩魔方,只要7步就能还原-推荐下载

首先,玩魔方,我们就要先了解它的结构,魔方共6色6面,每面又分为中央块(最中间的块6个)、角块(4角的块8个)和边块(4条边中间的块12个)。其中中央块只有1个面,他们是固定的结构,所以中央是红色的块,那么其他的红色都要向这个面集中。而且红色的中央块对面永远是橙色中央块(国际标准)。而边块有2个面2个颜 个颜色。 色,角块则有3个面3 接下来我们将每个面都用字母来代表:

R(代表右面顺时针转90度),R`(代表右面逆时针转90度), R2(代表右面顺时针转2次90 度) 最后要说明的是:每面的名称是相对的,例如F是前面,就是手拿魔方时面向自己的一面,若把魔方旋转到另一面,那么就有新的一面成 为前面。 第一步:先将中间是白色块的一面(有个rubiks logo的那块)对着上面,然后在顶部做出白十字,就是其他颜色的块都到相应的位置(小复杂,见图示,注意上面标的口诀哦,照做无误)

第二步:然后是将白色的角块归位(秘籍说的很复杂,还是看图比较容易理解)

第三步:然后让中层边块归位。 把白色面转向下,找出红绿边块,若红绿边块在顶层则按顺时针方向转动顶层,直到边块与图上的1个情况相同,在按照口诀转动魔方,使边块归位。若红绿边块在中间某层,但位置错误或颜色错误,则先使红绿边块在右前方的位置,再重新按照下面其中一个次序旋转1次。

第四步:然后将顶层(应该是黄色)边块调整向上,做出黄十字。若按照口诀转动1次后,顶层仍未出现黄色十字,可重复按口诀转动,直到黄色十字出现为止。

第五步:然后将黄色角块调整到十字周围,有点难度,看口诀提示吧。

零基础魔方入门教程

零基础魔方入门教程 看完这个你就能复原一个魔方 该教程适合零基础以上的人,会复原以下的人.看完这个教程都能学会复原 写这个教程一个是因为校内上缺乏这类文章,再一个是不同网站的教程步骤大体一样,但细节却略有差别,选取公式各选各的,有些我个人认为真的不是很方便新手记忆,甚至有些书面教程公式不全,没有列出所有可能情况,而且大多数教程没有通俗文字说明,难于学习理解,甚至产生误导,所以写一个我觉得最合适新手看的.这个教程我认为是我见过的最详细最通俗最适合新手的教程. 1.讲几个单词吧,魔方的英文是Rubik's Cube 译为鲁比克方块,Cuber就是魔方玩家的意思.. 2.魔方标准配色 这个新手不用记,架十字的时候再找就行.(如果不清楚架十字是什么意思没有关系,继续往下看,不耽误) 简单了解一下,上面图片就是魔方的标准配色----上黄下白,前蓝后绿,左橙右红. (说了可以不用背下来,知道有这个东西就行,继续往下看) 3.魔方基本构造

大家能了解棱块,角块,中心块这三个概念分别指什么就行了,因为下面的教程会提到,不用管支架. 然后是区分"面"和"层":面是指一个平面的3 x 3块,层是指一个平面3 x 3块所处3 x 3 x 1块. 举个例子:我们旋转的是一层,复原的是六面 (面,层这个东西不理解也没关系,没什么用,大概那个意思就行了,继续往下不用纠结...) 4.魔方算法(公式)符号体系 /魔方在架十字和第一层的时候是可以靠理解的,但是后面需要算法,就是很多人说的公式,(注意,教程里的"算法"就是通常说的"公式",理解成一个意思就行,用算法更恰当一些其实.)大家通常会比较费解在于---我前面做好了再做后面的时候,怎么能不破坏前面呢?是的,正是因为只有极少数人能想出来后面该怎么做还不会破坏前面,所以需要直接记住算法.这里的算法是个什么意思呢?举个例子,你从教学楼到宿舍,会走一条路,这就是一个算法,就是你每次在教学楼想要回宿舍就走这条路,魔方是一样的,你每次看到一种情况就用同样的方法解决它,就是要背一条算法,为什么要背,因为你想不出..所以不得不背,背了以后你下次都会解决这种情况了.不过放心,初级公式情况不多,可以说是--非常少^^ 所以不要担心你掌握不了~/ 那就来介绍一下符号体系吧!很简单,就是英文首字母. F = front 前面 B = back 后面

主机端口扫描程序设计

主机端口扫描程序设计 摘要 计算机信息网络的发展加速了信息化时代的进程,但是随着社会网络化程度的增加,对计算机网络的依赖也越来越大,网络安全问题也日益明显。端口扫描技术是发现安全问题的重要手段之一。 本程序是在Windows系统中使用C语言用MFC完成的一个端口扫描程序。此程序主要完成了TCP connect()扫描和UDP扫描功能。TCP扫描支持多线程,能对单个指定的主机进行扫描或对指定网段内的主机进行逐个扫描。能扫描特定的部分端口号或对指定的端口段内的端口进行逐个扫描。此端口扫描程序能快速地进行TCP扫描,准确地检测出对TCP协议开放的端口。而对于UDP扫描只支持单线程,速度较慢。扫描结果以列表的形式直观地展现出来。 关键词:端口扫描、TCP扫描、UDP扫描、TCP多线程扫描

目录 1引言 (1) 1.1 课题的背景及意义 (1) 1.2 端口扫描现状 (1) 2系统设计 (1) 2.1 系统主要目标 (1) 2.2 开发环境及工具 (1) 2.3 功能模块与系统结构 (2) 3系统功能程序设计 (4) 3.1 获取本机IP (4) 3.2 分割字符串函数的实现 (4) 3.3 获取待扫描的IP地址 (5) 3.4 获取待扫描的端口号 (5) 3.4.1 指定端口号的初始化 (6) 3.4.2 指定端口号的保存 (7) 3.5 TCP CONNECT()扫描 (8) 3.5.1 基本原理 (8) 3.5.2 扫描多个主机多端口多线程的实现 (8) 3.5.3 扫描结果的显示 (9) 3.6 UDP扫描 (10) 3.6.1 基本原理 (10) 3.6.2 计算效验和 (11) 3.6.3 发送UDP数据包 (11) 3.6.4 接收ICMP数据包 (12) 4测试报告 (12) 4.1 TCP扫描检测 (12) 4.1.1扫描本机 (12) 4.1.2扫描网络中其他主机 (13) 4.1.3 扫描IP段 (13) 4.2 UDP扫描检测 (14) 4.2.1 扫描本机 (14) 4.1.2扫描网络中其他主机 (15) 4.3 TCP、UDP一起扫描 (16) 结论 (17) 参考文献 (17)

教三阶魔方你从2分钟到20秒(1)

教三阶魔方你从2分钟到20秒(1)

7L内容:从30秒到25秒的教程(OLL全集,CROSS强化) 8L内容:从25秒到20秒以内的教程(慢拧与手速) 9L内容:后言 还有的是,5L~9L的内容,都需要回复才能查看,其一,我发现小站的人其实挺多,但是绝大部分都是游客,我希望来小站观光的游客能够注册帐号,这样有益于小站的发展,并且能够增加小站的人数,高手也会增加,当作做善事,其二就是这篇教材我下的功夫很多,希望各位把帖子能让跟多有需要的人看到,你回复一个顶起来后或许新手就看见了呢~ 另外说一下,你能到魔方小站的论坛来练习,都是渴望能够成为魔方高手的人。所以,一时的艰难不算什么,希望大家能够辛勤果敢,不怕困难地学习魔方,成为高手!但是假如你已经对魔方渐渐冷淡无趣,我相信你是无法进步的。所以,不怕万人阻挡,只怕自己投降!勤奋是高手的另一个名字! 还有,对教程不明白的,可以在本帖回复,对于其他魔方知识不明白的私信我,需要经常咨询或者要问的比较多的,可以加我QQ2609047698,下面进入内容,不懂可于本帖提问。

2L内容:从2分钟到1分钟的教程: 【前言】(可跳过) 三速无法达到1分钟的魔友,多半是练习不够,并且关乎到手法以及魔方的问题,其实进入1分钟相当简单,只要你肯下功夫学习,并且加上对魔方的热情,我相信每一位魔友都可以!这一步大概需要花掉半个月左右。 【关于练习】(必读) 学习了初级玩法后,必须要加强巩固初级玩法,不然初级玩法都没法掌握,就别说进一步学习新的了,必须要练习到一下几点:1.不用错公式2.不搞乱步骤3.能够独立还原。反正就是练习10遍,一遍都没有失误,发挥出正常水平就可以了,必须要保证这一点,这是很基础的。并且每天除了学习新的内容之外,还要天天都保证30次还原的练习量,有时间可以50次,甚至100次,反正就是尽可能多练习,这样进入1分是没问题的。 【关于手法】(必读) 手法,其实就是玩魔方的时候,你手指拧的方法。大家可以看到高手拧魔方,手都非常灵活,他们手速快是一方面的原因,其次就是手法问题。手法关系到你玩魔方的手速,所以新手练习手法是很有必要的。 大家可以看看FSC(就是手指快捷方法),你也可以直

手把手教你结构设计(入门到熟练)

手把手教你结构设计(入门到熟练) 1.结构设计的过程(了解) 本文是送给刚接触结构设计及希望从事结构设计的新手的,其目的是使新手们对结构设计的过程以及结构设计所包括的内容有一个大致的了解,请前辈们不要见笑了,新人们有什么问题也可以在贴中提出来,大家共同讨论,共同进步。 1,看懂建筑图 结构设计,就是对建筑物的结构构造进行设计,首先当然要有建筑施工图,还要能真正看懂建筑施工图,了解建筑师的设计意图以及建筑各部分的功能及做法,建筑物是一个复杂物体,所涉及的面也很广,所以在看建筑图的同时,作为一个结构师,需要和建筑,水电,暖通空调,勘察等各专业进行咨询了解各专业的各项指标。在看懂建筑图后,作为一个结构师,这个时候心里应该对整个结构的选型及基本框架有了一个大致的思路了. 2,建模(以框架结构为例)(关键) 当结构师对整个建筑有了一定的了解后,可以考虑建模了,建模就是利用软件,把心中对建筑物的构思在电脑上再现出来,然后再利用软件的计算功能进行适当的调整,使之符合现行规范以及满足各方面的需要.现在进行结构设计的软件很多,常用的有PKPM,广厦,TBSA等,大致都差不多。这里不对软件的具体操作做过多的描述,有兴趣的可以看看,每个软件的操作说明书(好厚好厚的,买起来会破产)。每个软件都差不多,首先要建轴网,这个简单,反正建筑已经把轴网定好了,输进去就行了,然后就是定柱截面及布置柱子。柱截面的大小的确定需要一定的经验,作为新手,刚开始无法确定也没什么,随便定一个,慢慢再调整也行。柱子布置也需要结构师对整个建筑的受力合理性有一定的结构理念,柱子布置的合理性对整个建筑的安全与否以及造价的高低起决定性作用...不过建筑师在建筑图中基本已经布好了柱网,作为结构师只需要对布好的柱网进行研究其是否合理.适当的时候需要建议建筑更改柱网.当布好了柱网以后就是梁截面以及主次梁的布置.梁截面相对容易确定一点,主梁按1/8~1/12跨度考虑,次梁可以相对取大一点主次梁的高度要有一定的差别,这个规范上都有要求。而主次梁的布置就是一门学问,这也是一个涉及安全及造价的一个大的方面.总的原则的要求传力明确,次梁传到主梁,主梁传到柱.力求使各部分受力均匀。还有,根据建筑物各部分功能的不同,考虑梁布置及梁高的确定(比如住宅,在房中间做一道梁,本来层就只有3米,一道梁去掉几十公分,那业主不骂人才怪...)。梁布完后,基本上板也就被划分出来了,当然悬挑板什么的现在还没有,需要以后再加上...,梁板柱布置完后就要输入基本的参数啦,比如混凝土强度啊,每一标准层的层高啊,板厚啊,保护层啊,这个每个软件设置的都不同,但输入原则是严格按规范执行.当整个三维线框构架完成,就需要加入荷载及设置各种参数了,比如板厚啊,板的受力方式啊,悬挑板的位置及荷载啊什么的,这时候模形也可以讲基本完成了,生成三维线框看看效果吧,可以很形象的表现出原来在结构师脑中那个虚构的框架. 2.计算 计算过程就是软件对结构师所建模型进行导荷及配筋的过程,在计算的时候我们需要根据实际情况调整软件的各种参数,以符合实际情况及安全保证,如果先前所建模型不满足要求,就可以通过计算出的各种图形看出,结构师可以通过对计算出的受力图,内力图,弯矩图等等对电算结果进行分析,找出模型中的不足并加以调整,反复至电算结果满足要求为止,这时模型也就完全的确定了.然后再根据电算结果生成施工图,导出到CAD中修改就行了,通常电算的只是上部结构,也就是梁板柱的施工图,基础通常需要手算,手工画图,现在通常采用平面法出图了,也大大简化了图纸有利于施工. 3.绘图 当然,软件导出的图纸是不能够指导施工的,需要结构师根据现行制图标准进行修改,这就看每个人的绘图功底了,施工图是工程师的语言,要想让别人了解自己的设计,就需要更为详细的说明,出图前结构师要确定,别人根据施工图能够完整的将整个建筑物再现于实际中,这是个复杂的过程,需要仔细再仔细,认真再认真。结构师在绘图时还需要针对电算的配筋及截面大小进一步的确定,适当加强薄弱环节,使施工图更符合实际情况,毕竟模型不能完完全全与实际相符.最后还需要根据现行各种规范对施工图的每一个细节进行核对,宗旨就是完全符合规范,结构设计本就是一个规范化的事情.我们的设计依据就是那几十本规范,如果施工图中有不符合规范要求的地方,那发生事故,设计者要负完全责任的......总的来讲,结构施工图包括设计总说明,基础平面布置及基础大样图,如果是桩基础就还有桩位图,柱网布置及柱平面法大样图,每层的梁平法配筋图,每层板配筋图,层面梁板的配筋图,楼梯大样图等,其中根据建筑复杂程度,有几个到几十个结点大样图. 4.校对审核出图 当然,一个人做如此复杂的事情往往还是会出错,也对安全不利,所以结构师在完成施工图后,需要一个校对人对整个施工图进行仔细的校对工作,校对通常比较仔细资格也比较老,水平也比较高,设计中的问题多是校对发现的,校对出了问题后返回设计者修改。修改完毕交总工审

端口扫描程序设计

网络安全程序设计结课论文端口扫描程序设计

目录 第一章序言 (3) 第二章系统设计 (5) 2.1 运行环境及语言 (5) 2.2系统功能 (6) 2.3程序运行流程图 (6) 2.4 程序设计过程 (6) 2.41创建工程 (6) 2.42 主机端口扫描程序设计流程 (8) 2.43主要代码 (9) 2.5运行结果测试 (15) 第三章总结及心得 (16) 3.1 总结 (16) 3.2 心得体会 (16) 第四章参考文献 (16)

第一章序言 1.1 背景 Internet快速的发展,为我们带来了方便同时也带给了我们信息安全担忧。在计算机信息安全管理中可以通过端口扫描收集系统的信息来自动监测远程或本地主机安全性弱点的程序,可以发现远程服务器的各种tcp端口的分配及提供的服务与他们的软件版本。从而让管理员间接的或直观的了解到远程主机所存在的安全问题。从而端口扫描技术得到人们的重视。 1.2目的 该文章对端口扫描技术的原理和应用进行了阐述,并设计了一个简单的基于windows平台上的端口扫描系统。此程序主要完成了TCP connect()扫描和UDP扫描功能。TCP扫描支持多线程,能对单个指定的主机进行扫描或对指定网段的主机进行逐个扫描。能扫描特定的部分端口号或对指定的端口段的端口进行逐个扫描。此端口扫描程序能快速地进行TCP扫描,准确地检测出对TCP协议开放的端口。而对于UDP扫描只支持单线程,速度较慢。扫描结果以列表的形式直观地展现出来。 1.3端口扫描概述 网络安全探测在网络安全中起着主动防御的作用,占有非常重要的地位。网络安全探测的所有功能都是建立在端口扫描的基础

魔方快速入门,教你如何玩转魔方

把第一层的色彩玩共同,并让第一层的边上的色彩和魔方4侧边的色彩共同。第二层公式:上顺—右顺—上逆—右逆—上逆—前逆—上顺—前顺第三层公式(起十字):右逆—上逆—前逆—上顺—前顺—右顺第三层公式(四角块):右顺—上顺—右逆—上顺—上顺180°—右顺—右逆第三层公式(还四角):右顺—上顺—右逆—上逆—右逆—前顺—右顺180°—上逆—右逆—上逆—右顺—上顺—右逆—前逆第三层公式(还中间):右顺—上逆—右顺—上顺—右顺—上顺—右顺—下逆—右逆—上逆—右180°注!:顺:顺时针转九十度逆:逆时针转九十度 这些天也开端玩这小东西了,开端老公在玩,他一弄好我就损坏掉 ,这即是我俩的玩法,如今我才干架个十字康复一面。想玩魔方,没想到这小小的东西,还有许多的诀窍!并且,我才会的是第一步啊,玩在路上的兄弟,和我一同尽力吧! 魔方恢复法Rubic's Cube Solution ————先看理论“ 魔方的恢复办法许多 在这里向咱们介绍一种比较简略的魔方六面恢复办法。这种办法娴熟之后能够在大概30秒之内将魔方的六面恢复。 在介绍恢复法之前,首要阐明一下魔方移动的记法。魔方状况图中标有字母“F”的为前面,图后所记载的操作都以这个前面为基准。各个面用以下字母表明: F:前面 U:上面 D:下面 L:左边 R:右面 H:水平方向的中间层 V:笔直方向的中间层 魔方操作过程中,独自写一个字母表明将该面顺时针旋转90度,字母后加一个减号表明将该面逆时针旋转90度,字母后加一个数字2表明将该面旋转180度。H的状况下,由上向下看来决议顺逆时针方向;V的状况下,由右向左看来决议顺逆时针方向。例如 U:将上层顺时针旋转90度 L-:将左边逆时针旋转90度 H2:将水平中间层旋转180度 目录 上层四角恢复 基层四角恢复 上基层八角恢复 上基层边块恢复 中层边块恢复 上层四角恢复 首要咱们用最简略的几步使得上层的三个角块归位,暂不用思考附近的色向方位)。还有一个角块存在五种状况,归位办法如下。 L D L- F- D- F D L2 D- L2 F L D- L- L- F- D F 基层四角恢复 上层四角归位后,将上层放在下面方位上,作为基层。然后看上层和附近的色彩和图画摆放,依照以下的操作使上层四个角块一次归位。共存在七种状况。 R2 U2 R- U2 R2 R- U- F- U F U- F- U F R

端口扫描报告

杭州电子科技大学软件学院网络工程试验报告 端口扫描报告 09109146 王子龙

1.端口及端口扫描技术简介 (2) 2.对现有端口扫描工具程序的理解 (2) 主界面 (3) 3.核心代码 (6) 4.个人总结 (13) 1.端口及端口扫描技术简介 根据提供服务类型的不同,端口分为两种,一种是TCP端口,一种是UDP端口。计算机之间相互通信的时候,分为两种方式:一种是发送信息以后,可以确认信息是否到达,也就是有应答的方式,这种方式大多采用TCP协议;一种是发送以后就不管了,不去确认信息是否到达,这种方式大多采用UDP协议。对应这两种协议的服务提供的端口,也就分为TCP 端口和UDP端口。 一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等,这些服务完全可以通过1个IP地址来实现。主机不只是靠IP地址来区分网络服务,因为IP 地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区分不同的服务的。 一个端口就是一个潜在的通信通道,也就是一个入侵通道。对目标计算机进行端口扫描,能得到许多有用的信息。进行扫描的方法很多,可以是手工进行扫描,也可以用端口扫描软件进行。 在手工进行扫描时,需要熟悉各种命令。对命令执行后的输出进行分析。用扫描软件进行扫描时,许多扫描器软件都有分析数据的功能。 通过端口扫描,可以得到许多有用的信息,从而发现系统的安全漏洞。 2. 对现有端口扫描工具程序的理解 该程序是有C++编写的。C++是一种使用非常广泛的计算机编程语言。C++是一种静态数据类型检查的、支持多重编程范式的通用程序设计语言。它支持过程化程序设计、数据抽象、面向对象程序设计、制作图标等等泛型程序设计等多种程序设计风格。 该程序能够扫描主机IP的某一个端口,或者是扫描该主机IP某一范围内的端口。并且提供多次扫描功能。如果要扫描的端口很多,在扫描过程中可以暂停扫描。扫描结果在界面的下方显示。主要显示内容有IP地址、端口号、端口状态、连接次数及备注。

从细节手把手教你如何快速、高效推本

从细节手把手教你如何快速、高效推本不管你是屌丝还是土豪,注意一些细节问题总是好的。推本的成功与否往往取决于一些细节性的突破,因为你卡本了并不代表你的团队战力不够,可能是你的战术有一定的问题。 小仓这里给大家总结了几点细节,希望能对玩家和堂友有所帮助。如果你觉得好,请持续关注任玩堂&百度攻略全民英雄专区: 任玩堂出品《全民英雄》攻略APP正式上线 废话不多说了,直接进入今天的快速、高效推本正题。 1、领导力 在游戏初期,最让玩家苦恼的要数领导力了,抽了一堆蓝卡紫卡,却因为领导力不够的问题而无法上阵卡牌参战副本。然而领导力却潜移默化左右着战局,因为他直接影响了玩家出战卡牌的数量。 关于领导力,玩家需要知道的重点是野怪卡的领导力是6。为何需要了解野怪的领导力,关键在于游戏中基友参战的设定机制。如果你的剩余领导力超过6点,请不要浪费,果断带个野怪换上基友的强力卡牌,而不是苦苦等到领导力达到16、22点再去上阵已有的蓝卡、紫卡。要知道多一张参战卡牌就多一份赢得希望。 2、技能 玩家都非常关心英雄技能的取舍问题,原因之一是技能书来之不易,原因之二是对技能的效果不了解。到底哪些技能对推本有用,哪些没用,小仓觉得只要你了解了战斗模式和技能释放的机制就清楚了。 每场战斗下方都有个读条版,卡牌英雄根据其速度属性的高低在读条版上从左到右循环移动

。其中最左边为旗帜,如图中的1所示;中间是水晶球,如图中的2所示;右边为宝剑,如图中的3所示。这三个点中,影响技能释放的点只有后面两个,其中水晶球这个点可能触发道具、辅助类主动技能和诅咒类主动技能,每回合可能触发一次;宝剑这个点可能触发攻击性主动技能,包括AOE、单体攻击、晕眩技能等。 简单地说,英雄的主动技能分为两类:非伤害类【治疗守卫】【沉默】、伤害类【凤凰冲击】【火焰气息】,由于这两类技能触发读条板上的点是不同的,所以对于技能的释放互相没有 影响,唯独有影响的就是英雄的魔法值是否足够的问题。 回到重点,什么技能适合推本?很简单,带集火的AOE技能、带晕眩的AOE控制技能、带减伤的辅助技能、带增益攻击速度BUFF的辅助技能,这四种技能是推本的王牌,最快的速度清理小怪才能最大限度保证团队的生存,也才能保证3星通本获得扫荡的能力。 举几个例子: 这是龙骑的技能,其中三技能是被动技能,提高气血上限,与其他两个技能没有冲突,不过加强了T的生存资本。对于想主要培养龙骑的玩家来说可以选择学习这个技能。但是第一、第二个技能都属于伤害类技能,他们会在读条版最右边的宝剑点被触发,如果没有学习2技能,那么龙骑在战斗的时候要么就普攻、那么就放AOE对怪物造成大量伤害,反之如果学习了2技能,导致的结果有两个:1、当龙骑在读条版上移动到宝剑点的时候,多了一个随机技能选择,这意味着第一个AOE技能释放的概率由原来的二分之一下降到了三分之一;2、龙骑属于力量英雄,如果释放了第二个技能,有可能就没有机会释放AOE技能。再看第二个技能的效果,晕眩一个前排单位,这种技能在推本的时候是基本没用的,没有伤害不说,还浪费一个回合,所以综上所述,龙骑不适合学第二个技能。以此类推的还有小小、火女之类的,第二个技能尽量不要去学习,有时候放出来是团灭的信号。 这是凤凰的技能,三个技能均为主动技能。其中1、3两个技能为AOE伤害技能,2技能为治疗类技能。其实凤凰三个技能都非常好用,一个全体打击、一个全体回复、一个单侧打击都有着不俗的伤害和效果,推本可以说技能放出来了配合其他AOE对小怪来说是毁灭性打击,但凤凰最大的问题在于它是力量型英雄,魔法少是硬伤,而它的技能特点都是耗魔很多,所以需要谨慎学习。如果你的凤凰的魔法值只能够放一次技能,建议还是不要学习,不然有时候会得不偿失、

教三阶魔方你从2分钟到20秒1资料全

【教程】教你从2分钟到20秒 作者:魔方世纪(魔方小站) 前言: 首先,世纪写这篇文章的时候,已经sub20,大概avg16的水平了,待在小站答疑那么久,我发现小站新手居多,高手大多潜水,所以小站经常是很多新手问一些很基础的问题,为了新手不再发一些伸手帖,我特写此帖帮助那些新手。当然,这篇教程的幅度很大,不管你是两分钟的新手还是avg25的半高手,或许都适宜阅读,我的成绩是sub20,所以这篇教程为了不误导他人也就写到sub20的部分。假如已经sub20的半高手,请勿喷此帖。 这篇文章写得都是关于三阶速拧提高速度的方法,都是我的心得,本帖可能会借鉴一些小站以及魔方吧高手的原创作品,部分会进行修改,都是为了广大魔友,望高手不吝。 卖关子是可耻的,还是立刻进入真正内容,需要的魔友可以刻意去看某一楼。 2L内容:从2分钟到1分钟的教程 3L内容:从1分钟到50秒的教程(OLL21~25与PLL1~4) 4L内容:从50秒到40秒的教程(十步CROSS的学习) 5L内容:从40秒到35秒的教程(基础F2L) 6L内容:从35秒到30秒的教程(PLL全集) 7L内容:从30秒到25秒的教程(OLL全集,CROSS强化) 8L内容:从25秒到20秒以内的教程(慢拧与手速) 9L内容:后言 还有的是,5L~9L的内容,都需要回复才能查看,其一,我发现小站的人其实挺多,但是绝大部分都是游客,我希望来小站观光的游客能够注册帐号,这样有益于小站的发展,并且能够增加小站的人数,高手也会增加,当作做善事,其二就是这篇教材我下的功夫很多,希望各位把帖子能让跟多有需要的人看到,你回复一个顶起来后或许新手就看见了呢~ 另外说一下,你能到魔方小站的论坛来练习,都是渴望能够成为魔方高手的人。所以,一时的艰难不算什么,希望大家能够辛勤果敢,不怕困难地学习魔方,成为高手!但是假如你已经对魔方渐渐冷淡无趣,我相信你是无法进步的。所以,不怕万人阻挡,只怕自己投降!勤奋是高手的另一个名字! 还有,对教程不明白的,可以在本帖回复,对于其他魔方知识不明白的私信我,需要经常咨询或者要问的比较多的,可以加我QQ2609047698,下面进入内容,不懂可于本帖提问。

基于多线程的端口扫描程序课程设计报告

滁州学院 课程设计报告 课程名称: 设计题目:基于多线程的端口扫描程序 院部:计算机与信息工程学院 专业:网络工程 组别:第六组 起止日期: 2012 年12月31日~2013 年1月6日指导教师: 计算机与信息工程学院二○一二年制

课程设计任务书 目录 1 需求分析. 0 1..1 网络安全 0 1.2 课程背景 0 1.3 扫描器 0 1.4 多线程扫描器介绍 (1) 错误! 未定义书签。

错误! 未定义书签。 错误! 未定义书签。 错误! 未定义书签。 1.5 端口扫描 (2) 2 概要设计. (3) 2.1 整体框架设计 (3) 2.2 流程图描述 (3) 3 详细设计. (3) 3.1 端口扫描线程启动 (3) 3.2 GUI 图形界面 (5) 3.3 按钮监听及异常处理 (6) 4 调试与操作说明. (8) 4.1 运行界面 (8) 4.2 扫描结果 (8) 4.3 错误提示 (8) 5 课程设计总结与体会. (8) 6 参考文献. (9) 7 致谢. (9) 8 附录. 0 1 需求分析 1..1 网络安全二十一世纪是信息化、网络化的世纪,信息是社会发展的重要资源。信息安全保障能力是一个国家综合国力、经济竞争实力和生存能力的重要组成部分,是世界各国在奋力攀登的制高点。网络安全是指网络系统的硬件、软件及其系统中的数据受到保护,不因偶然的或者恶意的原因而遭到破坏、更改、泄露,系统连续可靠正常地运行。网络安全包括技术领域和非技术领域两大部分: 非技术领域包括一些制度、政策、管理、安全意识、实体安全

等方面的内容; 技术领域包括隐患扫描、防火墙、入侵检测、访问控制、虚拟专用网、CA 认证、操作系统等方面的内容。这些技术的目标是保证信息的可控性、可用性、保密性、完整性、和不可抵赖性。端口扫描属于安全探测技术范畴,对应于网络攻击技术中的网络信息收集技术。 1.2 课程背景 随着Internet 的不断发展,信息技术已成为促进经济发展、社会进步的巨大推动力。端口扫描技术是网络安全扫描技术一个重要的网络安全技术。与防火墙、入侵检测系统互相配合,能够有效提高网络的安全性。安全扫描是安全技术领域中重要的一类。通过扫描能自动检测远端或本地主机系统信息,包括主机的基本信息(如计算机名、域名、组名、操作系统 型等)、服务信息、用户信息以及漏洞信息,它的重要性在于能够对网络进行安全评估,及时发现安全隐患,防患于未然。 网络的安全状况取决于网络中最薄弱的环节,任何疏忽都有可能引入不安全的因素,最有效的方法是定期对网络系统进行安全分析,及时发现并修正存在的脆弱,保证系统安全。 国外安全扫描技术的历史可以追溯到20 世纪90 年代,当时因特网刚刚起步,但是在过去的十年内,扫描技术飞速发展,迄今为止,其扫描技术已经非常完善,但是在全面性,隐蔽性和智能性上还有待提高。安全扫描从最初专门为UNIX 系统而编写的一些只有简单功能的小程序发展到现在,已经出现了可以运行多个操作系统平台上的,具有复杂功能的系统程序。 国内的扫描技术是在国外的扫描器基础上发展起来的。其中有一些专门从事安全技术的公司。这些公司的扫描器以硬件为主,其特点是执行速度快,不像软件一样受到安装主机系统的限制。 然而对于更多的基于主机的端口扫描而言,简单,实用,可靠才是它们的长处。 1.3 扫描器扫描器是一种自动检测远程或本地主机安全性弱点的程序,通过使用扫描器你可以不留痕迹的发现远程服务器的各种TCP端口的分配。这就能让我们间接的或直观的了解到远程主机所存在的安全问题。为了保证网络中计算机的安全性,必须采取主动策略, 快速、及时、准确、安全的检测出网络中计算机及防火墙开放的和未开放的端口。计算机端口扫描技术就是这种主动防御策略实现的重要技术手段。 扫描器采用模拟攻击的形式对目标可能存在的已知安全漏洞进行逐项检查。目标可以是工作站、服务器、交换机、数据库应用等各种对象。然后根据扫描结果向系统管理员提供周 密可靠的安全性分析报告,为提高网络安全整体水平产生重要依据。在网络安全体系的建设中,安全扫描工具花费低、效果好、见效快、与网络的运行相对对立、安装运行简单,可以大规模减少安全管理员的手工劳动,有利于保持全网安全政策的统一和稳定。 1.4 多线程扫描器介绍 在java 中,组件放置在窗体上的方式是完全基于代码的。组件放置在窗体上的方式通常不是通过绝对坐标控制,而是由“布局管理器”根据组件加入的顺序决定其位置。每个容器都有一个属于的自己布局管理器。使用不同的布局管理器,组件大小,位置和形状将大不相同。表格型布局管理器将容器划分成为一个多行多列的表格,表格的大小全部相同,是由其中最大的组件所决定。通过add 方法可以将组件一一放在每个表格

手把手教你如何打领带(附图)

手把手教你如何打领带(附图) 一、领带打结方法大全 冬天到了很多人都会穿西装打领带吧!但是男士之中会有真正打领带打得好看的呢?我想因该不多吧!今天看到了一位朋友介绍的如何打完美领结的贴子,我把它贴出来,让大家能学习学习。 1.亚伯特王子结 适用于浪漫扣领及尖领系列衬衫 搭配浪漫质料柔软的细款领带 正确打法是在宽边先预留较长的空间 并在绕第二圈时尽量贴合在一起 即可完成此一完美结型:图1 2.四手结(单结) 是所有领结中最容易上手的 适用于各种款式的浪漫系列衬衫及领带图2

3.浪漫结 浪漫是一种完美的结型 故适合用于各种浪漫系列的领口及衬衫 完成后将领结下方之宽边压以绉折可缩小其结型 窄边亦可将它往左右移动使其小部份出现于宽边领带旁图3 4.温莎结 此种结形因其宽度较一般结形宽 故十分适合使用在意大利式领口(八字领) 的浪漫系列衬衫上 最适合与浪漫细致的丝质领带相互搭配图4

5.简式结(马车夫结) 适用于质料较厚的领带 最适合打在标准式及扣式领口之衬衫将其宽边以180度由上往下翻转 并将折叠处隐藏于后方 待完成后可再调整其领带长度 是最常见的一种结形图5 6.十字结(半温莎结) 此款结型十分优雅及罕见 其打法亦较复杂 使用细款领带较容易上手

最适合搭配在浪漫的尖领及标准式领口系列衬衫图6 二、女生领巾丝巾的打法 1.巴黎结 利用重复对折将方巾折出领带型,绕在颈上打个活结. 将上端遮盖住结眼,并将丝巾调整至适当位置. 如下图7形所示 2.领带结 *将领巾对折再对折成领带型. *较长的 a 端绕过较短的 b 端,穿过领巾内侧向上拉出.*穿过结眼由下拉出,并调整成领带型. *搭配衬衫,简单的中性美感. *搭配洋装,优雅出色. 如下图8所示

魔方解法大全_(超简单_超全_带图片)

五阶公式说明五阶魔方降阶法教程五阶魔方层先玩法教程 本教程中讲解的是高级魔方普遍使用的一种方法降阶法,对于五阶魔方来说,我们第一步来完成六面中心块的复原,第二步完成12 个棱块的复原,第三步我们就完全可以用三阶的公式来完成复原了。有兴趣学习层先法的请进入五阶魔方层先玩法教程页面学习。 下面我们介绍几个最基础的几个公式,就可以完成五阶魔方的复原了,相信你掌握了下面几个公式,五阶魔方复原也非常的简单哟。 第一步:形成中心 在我们完成中心块的时候,前面我们完全可以不用公式,自己根据玩魔方的经验,完全可以靠过去的经验完成4面中心块的复原,只是在完成最后两面中心块的过程中为了保持已经完成的四面的中心块不 被破坏,处理用的步骤稍微复杂一点,下面介绍的就是两个这样的公式,其实也就是一点点小小的技巧,很 多朋友估计不看公式也是这样来完成的,在此,我们仅仅是提供一种思路,其中的一些技巧,大家可活学 活用。

公式一:TR' F' MR' F TR F' MR 公式二:TR U TR' U TR U2 TR' 合并中心块,上面的公式仅仅是一种方式,根据上面的公式,也可反向完成,如公式:ML' U' ML ,初学时,仔细领会公式的技巧和规律,理解了公式的意义,不用公式就也能随心所欲复原了。 第二步:集成棱边 在我们完成复原 12个棱块的时候,同样前面我们完全可以不用公式,自己根据玩魔方的经验,完全可以靠过去的经验完成10个棱块的复原,只是在完成最后两个棱块的的过程中为了保持已经完成的10个棱块不被破坏,处理用的步骤稍微复杂一点,下面介绍的就是4个这样的公式,其实也就是一点点小小的技巧,很多朋友估计不看公式也是这样来完成的,在此,我们仅仅是提供一种思路,其中的一些技巧,大家可活学活用。 当我们完全的正确的复原了6面的中心块和12个棱块以后,我们就可以把5阶完全的当作三阶魔方

手把手教你天线设计——用MATLAB仿真天线方向图

手把手教你天线设计—— 用MATLAB仿真天线方向图 吴正琳 天线是一种变换器,它把传输线上传播的导行波,变换成在无界媒介(通常是自由空间)中传播的电磁波,或者进行相反的变换。在无线电设备中用来发射或接收电磁波的部件。无线电通信、广播、电视、雷达、导航、电子对抗、遥感、射电天文等工程系统,凡是利用电磁波来传递信息的,都依靠天线来进行工作。此外,在用电磁波传送能量方面,非信号的能量辐射也需要天线。一般天线都具有可逆性,即同一副天线既可用作发射天线,也可用作接收天线。同一天线作为发射或接收的基本特性参数是相同的。这就是天线的互易定理。天线的基本单元就是单元天线。 1、单元天线 对称振子是一种经典的、迄今为止使用最广泛的天线,单个半波对称振子可简单地单独立地使用或用作为抛物面天线的馈源,也可采用多个半波对称振子组成天线阵。两臂长度相等的振子叫做对称振子。每臂长度为四分之一波长、全长为二分之一波长的振子,称半波对称振子。 对称振子是一种经典的、迄今为止使用最广泛的天线,单个半波对称振子可简单地单独立地使用或用作为抛物面天线的馈源,也可采用多个半波对称振子组成天线阵。两臂长度相等的振子叫做对称振子。每臂长度为四分之一波长、全长为二分之一波长的振子,称半波对称振子。

1.1用MATLAB画半波振子天线方向图 主要是说明一下以下几点: 1、在Matlab中的极坐标画图的方法: polar(theta,rho,LineSpec); theta:极坐标坐标系0-2*pi rho:满足极坐标的方程 LineSpec:画出线的颜色 2、在方向图的过程中如果rho不用abs(f),在polar中只能画出正值。也就是说这时的方向图只剩下一半。 3、半波振子天线方向图归一化方程: Matlab程序: clear all lam=1000;%波长 k=2*pi./lam;

相关主题
文本预览
相关文档 最新文档