并发服务器II多线程.docx
- 文档编号:7621424
- 上传时间:2023-01-25
- 格式:DOCX
- 页数:22
- 大小:1.34MB
并发服务器II多线程.docx
《并发服务器II多线程.docx》由会员分享,可在线阅读,更多相关《并发服务器II多线程.docx(22页珍藏版)》请在冰豆网上搜索。
并发服务器II多线程
四川大学计算机学院、软件学院
实验报告
课程名称
信息安全产品开发实践
实验课时
5
实验项目
并发服务器II——多线程
实验时间
2011年10月28号
实验目的
1)继续了解Linux下C语言程序开发的过程
2)了解线程库Pthreads及提供的基本线程的操作线程的属性,了解线程的属性,线程的互斥和同步
3)掌握多线程网络服务器模型
4)能在Linux环境实现TCP多线程并发服务器模型
5)能编写多线程端口扫描程序
实验环境
VMware5.0,RedHatLinux9.0
实验内容(算法、程序、步骤和方法)
试验题目1
修改远程控制程序服务器程序,将其从循环模式或多进程模式修改为多线程模式
试验题目2
多线程端口扫描程序
实现一个多线程端口扫描程序:
要求:
1.能同时扫描5个IP地址;
2.针对每个iP地址,开设100个线程对其进行扫描;
3.如果端口打开,使用函数getservbyport获取其服务名,在屏幕上打印:
IPportservername,如果是未知服务,则屏幕显示:
ipportunkonown
实验一:
先来回顾一下上次实验实现的多进程并发服务器:
Unix下的大多数网络服务器程序都是这么编写的,即父进程接受连接,派生子进程,子进程处理与客户的交互。
虽然这种模型很多年来使用得很好,但是fork时有一些问题:
(1)fork是昂贵的。
内存映像要从父进程拷贝到子进程,所有描述字要在子进程中复制等等。
目前有的Unix实现使用一种叫做写时拷贝(copy-on-write)的技术,可避免父进程数据空间向子进程的拷贝。
尽管有这种优化技术,fork仍然是昂贵的;
(2)fork子进程后,需要用进程间通信(IPC)在父子进程之间传递信息。
Fork之前的信息容易传递,因为子进程从一开始就有父进程数据空间及所有描述字的拷贝。
但是从子进程返回信息给父进程需要做更多的工作。
;
而上述两个问题可以通过用线程代替进程的方法来克服,接下来我们再来看下进程的一些特点:
•线程有助于解决这两个问题。
线程有时被称为轻权进程(lightweightprocess),因为线程比进程“轻权”,一般来说,创建一个线程要比创建一个进程快10~100倍。
•一个进程中的所有线程共享相同的全局内存,这使得线程很容易共享信息,但是这种简易性也带来了同步问题。
•一个进程中的所有线程不仅共享全局变量,而且共享:
进程指令、大多数数据、打开的文件(如描述字)、信号处理程序和信号处置、当前工作目录、用户ID和组ID。
•但是每个线程有自己的线程ID、寄存器集合(包括程序计数器和栈指针)、栈(用于存放局部变量和返回地址)、error、信号掩码、优先级。
•程序的编译
gcc–otesttest.c-lpthread
有了这些理论准备之后我们就可以编程来实现我们的多线程并发服务器了。
实验步骤:
(1)打开虚拟机,利用VI编辑器打开上次实验的tcpserver.c程序,将其中有进程实现的代码去掉,添加用相关的用线程实现的代码
(2)保存tcpserver.c,使用命令gcc–otcpservertcpserver.c–g-lpthread命令编译程序。
(3)忽略警告,执行./tcpserver
(4)客户端程序不用修改,直接编译,执行即可
(5)客户端输入命令:
ls
(6)打开另外一个终端,再次执行客户端程序:
(7)执行命令pwd:
(8)第一个客户端输入quit命令,退出
(9)第二个客户端输入命令quit,退出
(10)通过以上的这几个步骤,说明我们编写的多线程的并发服务器很好的实现了相应的功能,这样我们就完成了第一个实验题目
实验二:
实验思路;主要分为以下四个部分:
(1):
从控制端接受用户的输入;
(2):
根据用户的输入结果,根据IP地址创建100线程进行扫描
1.创建100个线程描述符
pidthread_t*thread;
thread=(pthread_t*)malloc(THREAD_NUM*sizeof(pthread_t));
2.为每个线程创建扫描的分工
for(intj=0;j for(inti=0;i port_segmentport; port.dest=dest_ip[j]; port.min_port=i*SEG_LEN+1; /*thelastsegment*/ if(i==(THREAD_NUM-1)) port.max_port=MAX_PORT; else port.max_port=port.min_port+SEG_LEN-1; (3): 创建线程,开始扫描 if(pthread_create(&thread[i],NULL,scan,(void*)&port)! =0) { perror("pthread_createfailed\n"); free(thread); exit(-2); } (4): 释放创建的资源 free(pthread); 下图是实现该过程的流程图: 根据以上思路我们就可以编程来实现多线程的端口扫描器了 (1): 打开虚拟机,在Linux下的VI编辑器中编写服务器端程序tcpscanner.c (2)使用命令gcc-otcpscannertcpscanner.c-g-lpthread编译; (3): 执行命令: ./tcpscanner127.0.0.110.0.0.310.0.0.234 (4)通过观察程序执行的结果,发现程序很好的完成了功能,这样我们也就完成了第二个实验。 (接上) 实验内容(算法、程序、步骤和方法) 附试验源代码: mypopen.c //tcpserver.c #include #include #include #include #include #include #include #include #include #definePORT8900 #defineBUFSIZE2048 void*start_routine(void*arg);//声明线程处理函数 intexecute(char*command,char*buf) { FILE*fp; intcount; charcommandbuf[2056]; if((NULL==command)||(NULL==buf)) { perror("commandorbufisempty\n"); return-1; } count=0; memset(commandbuf,0,2056); strcat(commandbuf,"sh-c"); strcat(commandbuf,command); fprintf(stderr,"thecommandis%s\n",commandbuf); if(NULL==(fp=popen(commandbuf,"r"))) { perror("createpipeerror\n"); return-1; } while((count<2047)&&(EOF! =(buf[count++]=fgetc(fp)))); buf[count-1]='\0'; returncount; } intmain() { intthread; intsockfd; intconn_sock; charsendbuf[BUFSIZE]; charrecvbuf[BUFSIZE]; intsendnum=0; intrecvnum=0; intlength=0; structsockaddr_inclient; structsockaddr_inserver; intopt; intcnt; /*Thefirststage: INITILIZE*/ memset(&client,0,sizeof(client)); memset(&server,0,sizeof(server)); memset(sendbuf,0,BUFSIZE); memset(recvbuf,0,BUFSIZE); length=0; sockfd=-1; conn_sock=-1; opt=SO_REUSEADDR; /*Thesecondstage: createlistensocket*/ if(-1==(sockfd=socket(AF_INET,SOCK_STREAM,0))) { perror("createsocketerror\n"); return-1; } setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); /*Thethirdstage: bindsocket*/ server.sin_family=AF_INET; server.sin_addr.s_addr=htonl(INADDR_ANY); server.sin_port=htons(PORT); if(-1==bind(sockfd,(structsockaddr*)&server,sizeof(server))) { perror("bindsocketerror\n"); close(sockfd); return-1; } /*Thefourthstage: listensocket*/ if(-1==listen(sockfd,10)) { perror("listensocketerror\n"); close(sockfd); return-1; } /*Thefifthstage: creatconnectsocket*/ while (1) { if(-1==(conn_sock=accept(sockfd,(structsockaddr*)&client,&length))) { perror("threeshakehandserror\n"); close(sockfd); return-1; } //调用线程函数 if(pthread_create(&thread,NULL,(void*)start_routine,(void*)conn_sock)) { perror("Pthread_create()error"); exit (1); } } } //线程函数的实现 void*start_routine(void*arg) { intconn_sock=arg; intsockfd; charsendbuf[BUFSIZE]; charrecvbuf[BUFSIZE]; intsendnum; intrecvnum; intlength; intopt; intcnt; while (1) { close(sockfd); memset(recvbuf,0,BUFSIZE); memset(sendbuf,0,BUFSIZE); if(0>=(recvnum=read(conn_sock,recvbuf,BUFSIZE))) { perror("thecommucationerror\n"); close(conn_sock); close(sockfd); return-1; } recvbuf[recvnum]='\0'; fprintf(stderr,"thecommandis: %s\n",recvbuf); if(0==strcmp(recvbuf,"quit")) { fprintf(stderr,"theclientisquit\n"); close(conn_sock); break; } if(1>=(cnt=execute(recvbuf,sendbuf))) { sprintf(sendbuf,"theinvalidcommand,pleasetryagain\n"); } fprintf(stderr,"theresultis\n%s",sendbuf); if(0>=(sendnum=write(conn_sock,sendbuf,strlen(sendbuf)))) { perror("thecommucationerror\n"); close(conn_sock); return-1; } } close(sockfd); } tcpscanner.c #include #include #include #include #include #include #include #include #defineSCANLENGTH655//每个线程分配655个端口 #defineTHREADNUM100//100个线程 #defineSTART_PORT1//起始端口号 #defineEND_PORT65535//结束端口号 structPort//定义端口结构体 { intscan_minPort; intscan_maxPort; char*IP; }Port; voidprint_usage(char*str);//打印出错信息 void*scan(void*arg);//声明扫描函数 intmain(intargc,char*argv[]) { intret; structsockaddr_inserver; structservent*serve; if(argc<2) { print_usage(argv[0]); exit (1); } //创建100个线程描述符 pthread_t*thread; thread=(pthread_t*)malloc(THREADNUM*sizeof(pthread_t)); intj; inti; //为每个线程创建扫描的分工 for(j=1;j { for(i=0;i { structPort*port=(structPort*)malloc(sizeof(structPort)); port->IP=argv[j]; port->scan_minPort=i*SCANLENGTH+1; if(i==(THREADNUM-1)) port->scan_maxPort=END_PORT; else port->scan_maxPort=port->scan_minPort+SCANLENGTH-1; //创建线程,开始扫描 if(pthread_create(&thread[i],NULL,(void*)scan,(void*)port)! =0) { perror("thead_createerror! \n"); free(thread);//释放创建的资源 exit(-1); } } } return0; } voidprint_usage(char*str) { printf("thecommand%susageis: \n",str); printf("%sIP_ADDRESSIP_ADDRESSIP_ADDRESS\n",str); } //扫描函数的实现 void*scan(void*arg) { pthread_detach(pthread_self()); intsockfd; intret; intopt=SO_REUSEADDR; structsockaddr_inserver; structservent*serve; char**name; structPort*port=(structPort*)arg; intstart; intend; intscanPort; start=port->scan_minPort; end=port->scan_maxPort; for(scanPort=start;scanPort! =end;scanPort++) { if(-1==(sockfd=socket(AF_INET,SOCK_STREAM,0))) { perror("socketfailed! \n"); exit(-1); } setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); memset(&server,0,sizeof(server)); server.sin_family=AF_INET; server.sin_addr.s_addr=inet_addr(port->IP); server.sin_port=htons(scanPort); ret=connect(sockfd,(structsockaddr*)&server,sizeof(structsockaddr))+1; if(ret==0) { close(sockfd); continue; } elseif(ret==1) { if((serve=getservbyport(htons(scanPort),"TCP"))==NULL) printf("IP=%sscanPort=%dUnknownservice\n",port->IP,scanPort); else { printf("IP=%sscanPort=%ds_name=%s\n",port->IP,scanPort,serve->s_name); for(name=serve->s_aliases;*name! =NULL;name++) printf("IP=%sscanPort=%daliasenameis%s\n",port->IP,scanPort,*name); } close(sockfd); } } } 数据记录 和计算 详细见上面 结论 (结果) (1)利用多线程技术能实现并发服务器,且比多进程的实现的方式更有效 (2)利用多线程技术能加强端口扫描的功能。 小结 通过本实验,我掌握了: 1)进一步熟悉了Linux下C语言程序开发的过程; 2)了解了一些基本的线程函数 3)掌握了如何利用多线程实现并发服务器 4)会编写多线程端口扫描程序 目前我存在的问题有: 对线程的函数掌握的不是很熟练 指导老师评议 成绩评定: 指导教师签名: 实验报告说明 专业实验中心 实验名称要用最简练的语言反映实验的内容。 如验证某程序、定律、算法,可写成“验证×××”;分析×××。 实验目的目的要明确,要抓住重点,可以从理论和实践两个方面考虑。 在理论上,验证定理、公式、算法,并使实验者获得深刻和系统的理解,在实践上,掌握使用实验设备的技能技巧和程序的调试方法。 一般需说明是验证型实验还是设计型实验,是创新型实验还是综合型实验。 实验环境实验用的软硬件环境(配置)。 实验内容(算法、程序、步骤和方法)这是实验报告极其重要的内容。 这部分要写明依据何种原理、定律算法、或操作方法进行实验,要写明经过哪几个步骤。 还应该画出流程图(实验装置的结构示意图),再配以相应的文字说明,这样既可以节省许多文字说明,又能使实验报告简明扼要,清楚明白。 数据记录和计算指从实验中测出的数据以及计算结果。 结论(结果)即根据实验过程中所见到的现象和测得的数据,作出结论。 小结 对本次实验的体会、思考和建议。 备注或说明可写上实验成功或失败的原因,实验后的心得体会、建议等。 注意: ∙实验报告将记入实验成绩; ∙每次实验开始时,交上一次的实验报告,否则将扣除此次实验成绩。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 并发 服务器 II 多线程