1、武汉理工大学计算机网络课程设计说明书武汉理工大学计算机网络课程论文题目基于多线程的端口扫描程序作者学院专业学号指导教师二一五年 七 月 六 日武汉理工大学信息工程学院课程论文诚信声明本人声明:所呈交的课程论文,是本人在指导老师的指导下,独立开展工作所取得的成果,成果不存在知识产权争议,除文中已经注明引用的内容外,本课程论文不含任何其他个人或集体已经发表或创作过的作品成果。对本文工作做出重要贡献的个人和集体均已在文中以明确方式标明。本人完全意识到本声明的法律结果由本人承担。本科课程论文作者签名:二一五年 月 日课程论文成绩评定表质量评价指标(在相应栏目打)评 价 项 目论文与设计评价质量按对应项
2、目打分工作量和态度(10分)分析问题能力(10分)解决问题能力(10分)内容完整层次分明(10分)设计、实验正确性(10分)书写规范(10分)流程图或拓扑图(10分)论证充分(10分)测试结果情况(10分)总体评价(10分)评定成绩(100分制)指导教师签名年 月 日2一、课题背景随着Internet的不断发展,信息技术已成为促进经济发展、社会进步的巨大推动力。端口扫描技术是网络安全扫描技术一个重要的网络安全技术。与防火墙、入侵检测系统互相配合,能够有效提高网络的安全性。安全扫描是安全技术领域中重要的一类。通过扫描能自动检测远端或本地主机系统信息,包括主机的基本信息(如计算机名、域名、组名、操
3、作系统型等)、服务信息、用户信息以及漏洞信息,它的重要性在于能够对网络进行安全评估,及时发现安全隐患,防患于未然。网络的安全状况取决于网络中最薄弱的环节,任何疏忽都有可能引入不安全的因素,最有效的方法是定期对网络系统进行安全分析,及时发现并修正存在的脆弱,保证系统安全。国外安全扫描技术的历史可以追溯到20世纪90年代,当时因特网刚刚起步,但是在过去的十年内,扫描技术飞速发展,迄今为止,其扫描技术已经非常完善,但是在全面性,隐蔽性和智能性上还有待提高。安全扫描从最初专门为UNIX系统而编写的一些只有简单功能的小程序发展到现在,已经出现了可以运行多个操作系统平台上的,具有复杂功能的系统程序。国内的
4、扫描技术是在国外的扫描器基础上发展起来的。其中有一些专门从事安全技术的公司。这些公司的扫描器以硬件为主,其特点是执行速度快,不像软件一样受到安装主机系统的限制。然而对于更多的基于主机的端口扫描而言,简单,实用,可靠才是它们的长处。二、设计理念每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。进程也可能是整个程序或者是部分程序的动态执行。线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。也可以把它理解为代码运行的上下文。所以线程基本上是轻量级的进程,它负责在单个程序里执行多任务。通常由操作系统负责多个线程的调度和执行。线程是程序中一个单一的顺序控制流程.在单个程序中
5、同时运行多个线程完成不同的工作,称为多线程.端口扫描的原理其实非常简单,只是简单的利用VC+提供库函数Socket进行调用,与每一个感兴趣的目标计算机的端口进行连接。如果对方端口处于监听状态,那么连接就能成功。否则,这个端口不能用,既没有提供服务。这个技术的一个最大的优点是,不需要任何权限,系统中的任何用户都有权利使用这个调用。引入多线程机制,利用多线程扫描的好处就是速度快,如果对每个目标端口以线性的方式使用单独的连接调用,那么将会花费相当长的时间。多线程同时打开多个套接字,从而加速扫描。在本设计中用户可以自定义线程的个数。此处用户还可以自定义扫描方式。用VC+6.0开发多线程应用程序有两种方
6、法,一种是利用Win32API函数,它的特点是代码小巧,执行效率高,但开发难度大;另一种是利用MFC类库,它的特点是开发方便,但代码庞大。下面,以端口扫描程序为例,介绍利用目前较流行MFC类库进行多线程应用程序的开发方法。三、过程论述1.过程流程图图1过程流程图2.程序设计过程(一)MFC多线程的类型MFC区分两种类型的线程:用户界面线程(user2interfacethreads)和工作者线程(workerthreads)。用户界面线程通常用来处理用户的输入并响应各种事件和消息。工作者线程通常用来完成程序的后台处理任务,比如计算、调度、后台打印等,没有消息机制,不需要与用户交互。这两种线程类
7、都是从CWinThread类派生而来的,不同的是,工作者线程不用从CwinThread类派生来创建,而由函数AfxBe2ginThread()自动创建。(二)用户界面线程的创建首先,从CWin Thread类派生出自己的线程类,必须确保用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE对该类进行声明和实现,然后该类重载一些函数,如ExitInstance,InitInstance,OnIdle,PreTrans2lateMessage,ProcessWndProcException,Run等等,最后调用AfxBeginThread()创建并启动线程,其原型如下:CWi
8、nThread3AfxBeginThread()CRuntimeClass3pThreadClass,/从CWinThread派生的运行类int nPriority = THREAD_PRIORITY_NORMAL,/线程优先级UINT nStackSize = 0, /线程堆栈大小,如为0,则与父线程堆栈大小相同DWORD dwCreateFlags = 0, /线程创建时的附加标志LPSECURIT_YATTRIBUTES lpSecurityAttrs = NULL); / /安全属性指针另外,还可以通过构造函数创建类CWinThread的一个对象,再调用函数:CreateThread启
9、动线程的方法来创建一个用户界面线程。(三)工作者线程的创建创建工作者线程相对比较简单,只需要两步:编写控制函数和启动线程。所谓控制函数,就是指想要并行运行的子程序。当进入该函数时,线程开始运行,退出时,线程终止,其声明如下:UINT MyControllingFunction (LPVOID pParam) ;参数pParam是一个单精度32位值,是调用函数创建线程对象时,传递给线程函数的参数。它可以是一个数值,也可以是指向包含多个参数的结构指针,甚至可以忽略。如果该参数是一个结构指针,那么该结构不仅用于调用函数传递参数给线程函数,还可以将数据从线程函数回传给调用函数。如果使用该结构回传数据,
10、当线程函数的数据准备好的时候,要通知调用函数。控制函数的返回值为0,则表示成功,不为0,则表示发生了不同类型的错误。启动线程时,调用函数AfxBeginThread()创建并初始化一个CWinThread对象,启动并返回该线程的地址,以备后用。(四)线程间的同步当两个或两个以上的线程同时访问同一个数据时,可能会导致意想不到的或者非预期的结果,例如,一个线程正在更新一个结构的内容,同时另一个线程正在读取该结构的信息,那么不知道读取结构的线程获得的是更新之前的旧数据还是更新之后的新数据,或者是两者的混合。MFC提供了一组同步类和同步访问类来解决这个问题。包括两大类:同步对象(CsyncObject
11、Csemaphore,Cmutex,CcriticalSection和Cevent)和同步访问对象(CmultiLock和CsingleLock)。其中Csync2Object是其它四个同步对象的基类,不直接使用。信号同步类CSemaphore:允许有限数量的线程访问一个资源,通常用于控制一定数量的用户访问共享资源;互斥同步类CMutex:允许一个线程以独占的方式访问一个资源,通常用于一次只允许一个线程修改数据或其它控制信息;临界区同步类CCriticalSection:允许一个线程以独占的方式访问一个资源,通常用于一次只允许一个线程修改数据或其它控制信息,当程序对速度要求较高并且对共享资源
12、的访问没有超出进程的范围时,可用CCriticalSection代替CMutex;事件同步类CEvent:允许一个线程通知另一个线程某一事件已经发生,通常用于当一个线程需要知道它什么时候开始执行某项操作的时候。四种同步对象的使用方法很简单,多数情况下不需要同步访问类CSingleLock和CMultipleLock。首先在主程序中创建同步对象,然后在线程需要访问共享资源的时候,直接调用同步对象自身的成员函数Lock()和Unlock()就可以了,例如:CriticalSection.Lock();ThreadNum+;CriticalSection.Unlock();(五)程序代码功能实现 用
13、AppWizard生成一个win32控制台程序框架,并选择支持MFC。由于该程序用到多线程和winsock;所以在头文件当中要加入:include“afxmt.h”include“Winsock2.h” 定义当前线程计数器,初始值取0;intThreadNum=0; 因为每个线程都要对当前的线程计数器进行修改,所以要定义线程同步对象:CCriticalSectionCriticalSection; 编写线程控制函数,每个线程需要知道自已要对哪台主机的哪个端口进行扫描,因此定义如下线程参数结构:structThreadParamint iPot; /被扫描的端口号int a,b,c,d; /ip
14、地址的四个分段实现线程控制函数TestThread():UINTTestThread(LPVOIDpParam)charip16;/用于存放ip地址intiPort=(ThreadParam3)pParam)-iPort;/从参数中取出被扫描端口号sprintf(ip,“%d.%d.%d.%d”,(ThreadParam3)pParam)-a,(ThreadParam3)pParam)-b,(ThreadParam3)pParam)-c,(ThreadParam3)pParam)-d; /将4个分段合并成IP地址SOCKET WinSocket;structsockaddrinlocal_si
15、n;/定义端口和地址addr.sin_family=AF_INET; /tcp/ip协议族SOCKET sck_Conn;/socket描述符sck_Conn=socket(AF_INET,SOCK_STREAM,0); /生成一个TCP的socket ,命名为sck_Connif(ddd=SOCKET_ERROR)if(REPORT_FAILED)aa.Format(failed! port:%drn,i); /尝试建立连接,失败则返回“failed”elseif(REPORT_SUCCEED)aa.Format(succeed! port:%drn,i); /否则返回“succeed”cl
16、osesocket(sck_Conn); /关闭套接字socket3. 设计步骤实现(一)首先使用VC+的应用程序生成向导(MFC AppWizardexe)创建一个基于对话框的工程,该工程的名称MyScanPort,如图3.1所示。图3.1建立工程(a)(二)单击确定, 然后选择基本对话框,如图3.2所示。图3.2建立工程(b)(三)点击下一步,再在Windows SocketsW前打上勾,支持Winsock编程,如图3.3所示。图3.3建立工程(c)(四)点击完成,即完成了工程的创建。(五)设计对话框,对话框界面如图3.4所示。图3.4对话框设计 (六)按快捷键Ctrl+W,添加相关变量,
17、如图3.5所示。图3.5添加变量 (七)最后,在程序代码区加入相关程序代码,即可完成设计。四、结果分析1、调试并运行程序之后,进入如下图所示界面。图4.1操作界面2、 输入目标计算机的IP地址,所扫描端口的范围以及线程数,点击查找,即可开始扫描。计算机端口的空闲状态,可通过命令提示符中输入“netstat -ano”即可查找计算机端口状态,以本机为例,本机IP地址为27.17.133.25。端口状态如图4.2所示:图4.2端口状态3、 由图可知,135,445等端口是空闲的,在用户界面中,输入目标计算机的IP地址,所扫描端口的范围以及线程数,得到端口情况,如下图4.3所示。图4.3运行结果 由
18、运行结果可知,135号端口为“succeed”,即开放状态,与实际情况相符合。五、总结 本次设计的基于多线程的端口扫描程序基本满足了课程设计的要求,能够很好地对本机或其他电脑端口进行扫描,同时也处理了程序中可能出现的错误。本次课程设计,使我对计算机网络这门课程有了更深入的理解。计算机网络是一门实践性较强的课程,为了学好这门课程,必须在掌握理论知识的同时,加强上机实践。要想把课程设计做的更好,就要学会参考一定的资料,吸取别人的经验,多多思考。 在本课程设计中,我明白了理论与实际应用相结合的重要性,并提高了自己组织数据及编写大程序的能力。培养了基本的、良好的程序设计技能以及合作能力。这次课程设计同
19、样提高了我的综合运用所学知识的能力。课程设计程序的编写需要有耐心,有些事情看起来很复杂,但问题需要一点一点去解决,分析问题,把问题模块化,划分成小块以后就逐个去解决。再总体解决大的问题。这样做起来不仅有条理也使问题得到了轻松的解决。 通过这段时间的课程设计,我认识到计算机网络是一门比较难的课程。需要多花时间学习。这次的课程设计培养了我们实际分析问题、编程和动手能力,使我们掌握了计算机网络课程设计的基本技能,提高了我们适应实际,运用于实际生活的能力。 这次的课程设计提高了我对于专业课的学习热情,使我能够更加深入的理解课本内容,对计算机网络也有了进一步的理解和认识,同时也理解了将课本内容与实际生活
20、相结合的好处。六、参考文献1.谢希仁等. 计算机网络(第六版) M. 北京:人民邮电工业出版社,2015.;2.肖微.端口扫描技术的原理及应用.网络安全技术与应用.2006.10.3 吴功宜.计算机网络(第二版).北京:清华大学出版社,2007。附件一:程序源代码#include stdafx.h /头文件预编译#include MyScanPort.h#include MyScanPortDlg.h#include afxmt.h /MFC多线程同步的一个扩展头文件, 该头文件中声明了用于MFC编程中多线程同步时所需要的类#ifdef _DEBUG#define new DEBUG_NEW#
21、undef THIS_FILEstatic char THIS_FILE = _FILE_;#endifBOOL REPORT_SUCCEED,REPORT_FAILED;typedef struct ThreadParam /结构体变量定义 :线程参数;int StartPort; /起始端口和终止端口int EndPort;HWND hwnd;byte a,b,c,d;/目标计算机ip地址的4个段THREADPARAM;typedef struct PortParam/结构体变量定义 :端口参数int iPort;HWND hwnd;byte a,b,c,d;PORTPARAM;THREA
22、DPARAM threadparam1;bool IsStop;HANDLE g_Busy;CString g_message;bool ReleaseSmp;CCriticalSection criticalSection;int NowWhere;UINT GetPortMessage(LPVOID pParam); /获取端口信息UINT MyThread(LPVOID pParam)/定义线程函数THREADPARAM* threadparam=(THREADPARAM*)pParam;sockaddr_in addr;addr.sin_family=AF_INET; /tcp/ip协
23、议族addr.sin_addr.S_un.S_un_b.s_b1=threadparam-a; addr.sin_addr.S_un.S_un_b.s_b2=threadparam-b;addr.sin_addr.S_un.S_un_b.s_b3=threadparam-c;addr.sin_addr.S_un.S_un_b.s_b4=threadparam-d;for(int i=threadparam-StartPort;(iEndPort)&(!IsStop)&(NowWhereEndPort);i+)SOCKET sck_Conn;/socket描述符sck_Conn=socket(A
24、F_INET,SOCK_STREAM,0); /生成一个TCP的socket ,命名为sck_Connif(sck_Conn=INVALID_SOCKET)/如果为无效的socket,则返回0,表示faildreturn 0;criticalSection.Lock();/调用同步对象成员函数,进行同步i=NowWhere;NowWhere+;criticalSection.Unlock();addr.sin_port=htons(i);sck_Conn=socket(AF_INET,SOCK_STREAM,0);if(sck_Conn=INVALID_SOCKET)return 0;int
25、ddd=connect(sck_Conn,(sockaddr*)&addr,sizeof(addr);CString aa=;char buffer80;strcpy(buffer,);if(ddd=SOCKET_ERROR)if(REPORT_FAILED)aa.Format(failed! port:%drn,i); elseif(REPORT_SUCCEED)aa.Format(succeed! port:%drn,i);PORTPARAM *portparam=new PORTPARAM;portparam-a=threadparam-a; portparam-b=threadpara
26、m-b; portparam-c=threadparam-c; portparam-d=threadparam-d; /将4个分段合并成IP地址portparam-hwnd=threadparam-hwnd; portparam-iPort=i;AfxBeginThread(GetPortMessage,portparam,THREAD_PRIORITY_IDLE);closesocket(sck_Conn); /关闭套接字socketif(g_message.GetLength()30000)g_message=;g_message=aa+g_message;if(i=threadparam
27、EndPort)g_message=scan port complete!rnrn+g_message;:SetWindowText(threadparam-hwnd,g_message);if(!ReleaseSmp)if(ReleaseSemaphore(g_Busy,1,NULL)ReleaseSmp=true;return 0;UINT GetPortMessage(LPVOID pParam)PORTPARAM* portparam1=(PORTPARAM*)pParam;sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_addr.
28、S_un.S_un_b.s_b1=portparam1-a;addr.sin_addr.S_un.S_un_b.s_b2=portparam1-b;addr.sin_addr.S_un.S_un_b.s_b3=portparam1-c;addr.sin_addr.S_un.S_un_b.s_b4=portparam1-d;addr.sin_port=htons(portparam1-iPort);/建立socket套接字SOCKET sck_Conn;sck_Conn=socket(AF_INET,SOCK_STREAM,0);if(sck_Conn=INVALID_SOCKET)delete
29、 pParam;return 0;sck_Conn=socket(AF_INET,SOCK_STREAM,0);int ddd=connect(sck_Conn,(sockaddr*)&addr,sizeof(addr);if(ddd=SOCKET_ERROR)closesocket(sck_Conn);delete pParam;return 0;char buffer256;strcpy(buffer,);ddd=recv(sck_Conn,buffer,256,0);if(ddd=SOCKET_ERROR)closesocket(sck_Conn);delete pParam;retur
30、n 0;if(strcmp(buffer,)!=0)CString aa=;for(int i=0;i128|bufferiiPort,buffer); closesocket(sck_Conn);if(g_message.GetLength()30000)g_message=;g_message=g_message+aa;:SetWindowText(portparam1-hwnd,g_message);delete pParam;return 0;return 0;void CMyScanPortDlg:OnPaint() if (IsIconic()CPaintDC dc(this);
31、/ device context for paintingSendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);/ Center icon in client rectangleint cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() -
32、 cyIcon + 1) / 2;/ Draw the icondc.DrawIcon(x, y, m_hIcon);elseCDialog:OnPaint();HCURSOR CMyScanPortDlg:OnQueryDragIcon()return (HCURSOR) m_hIcon;void CMyScanPortDlg:OnStart() UpdateData(true);if(m_threadnumEnableWindow(true);IsStop=false;ReleaseSmp=false;REPORT_SUCCEED=true;REPORT_FAILED=true;NowWh
33、ere=m_startport;BYTE a,b,c,d;m_ipaddress.GetAddress(a,b,c,d);g_message=;threadparam1.a=a;threadparam1.b=b;threadparam1.c=c;threadparam1.d=d;threadparam1.EndPort=m_endport;threadparam1.StartPort=m_startport;threadparam1.hwnd=GetDlgItem(IDC_MESSAGE)-GetSafeHwnd();for(int i=0;im_threadnum;i+)AfxBeginThread(MyThread,&threadparam1,THREAD_PRIORITY_IDLE);18