信息安全实践第八次作业 并发服务器II——多线程.docx
《信息安全实践第八次作业 并发服务器II——多线程.docx》由会员分享,可在线阅读,更多相关《信息安全实践第八次作业 并发服务器II——多线程.docx(21页珍藏版)》请在冰点文库上搜索。
![信息安全实践第八次作业 并发服务器II——多线程.docx](https://file1.bingdoc.com/fileroot1/2023-4/29/31b47c66-232b-49e9-8a2d-f51f90fe83b8/31b47c66-232b-49e9-8a2d-f51f90fe83b81.gif)
四川大学计算机学院、软件学院
实 验 报 告
学号:
姓名:
专业:
软件工程
班级:
第 7 周
课程名称
信息安全产品开发实践
实验课时
4
实验项目
并发服务器II——多线程
实验时间
2013.10.25
实验目的
1、修改远程控制程序服务器程序,将其从循环模式或多进程模式修改为多线程模式
2、实现一个多线程端口扫描程序
实验环境
虚拟机RedHatLinux-VMwareWorkstation
虚拟机Ubuntu-VMwareWorkstation
实验内容
(算法、程序、步骤和方法)
第一个比较简单,我直接把上次实验的多进程远程控制服务器改成多线程远程控制服务器了。
这里需要注意的是为了防止僵尸线程这里在建立线程时设置了属性,使子线程成为分离线程。
#include#include#include#include#include#include#include#include#include#include
#definePORT8900
#define SIZE2048 /*2KBUFFER*/
void exec(char*command,char*true_result)
{
FILE*in;charc;
charresult[2048];
memset(result,0,2048);
intfd[2];intpid;
if(pipe(fd)<0)
printf("createpipefail\n");
if((pid=fork())<0)printf("forkpipefail\n");
elseif(pid==0)
{
close(fd[0]);
dup2(fd[1],STDOUT_FILENO);
execlp("sh","sh","-c",command,NULL);
}
close(fd[1]);
read(fd[0],result,sizeof(result));
strcpy(true_result,result);
}
void*work(void*connectd){
intrecvnum;intrvalue;
charcmd[10]; /*these
twovarusedtodealwithcdcommand*/
charpath[2048];
charrecv_buf[2048];charsend_buf[2048];
while
(1)
{
memset(send_buf,0,2048);memset(recv_buf,0,2048);
if(0>(recvnum=recv((int)connectd,recv_buf,sizeof(recv_buf),0)))
{
perror("recverror\n");close((int)connectd);continue;
}
recv_buf[recvnum-1]='\0'; //这里要注意-1,不然会保留换行符
if(0==strcmp(recv_buf,"quit")||(0==strcmp(recv_buf,"QUIT"))
)
{//结束对话,跳出内部循环,继续外部循环printf("over\n");
break;
}
sscanf(recv_buf,"%s%s",cmd,path);
if((0==strcmp("cd",cmd))||(0==strcmp("CD",cmd)))
{
chdir(path);continue;
}
exec(recv_buf,send_buf);
rvalue=-1;rvalue=send((int)connectd,send_buf,sizeof(send_buf),0
);
}
}
intmain(intargc,char**argv)
{
structsockaddr_inserver;structsockaddr_inclient;intlen;
intport;intlistend;intsendnum;intopt;
charaddr_p[2048]; //存储客户端地址的缓冲区
intpid;
port=PORT;
opt=SO_REUSEADDR;
if(-1==(listend=socket(AF_INET,SOCK_STREAM,0)))//使
用TCP创建监听用的套接字
{
perror("createlistensocketerror\n");exit
(1);
}
setsockopt(listend,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
#ifdefDEBUG
printf("thelistenidis%d\n",listend);#endif
memset(&server,0,sizeof(structsockaddr_in));server.sin_family=AF_INET; //IPv4协议
server.sin_addr.s_addr=htonl(INADDR_ANY); //接收任意地址
server.sin_port=htons(port);//设置端口号
if(-1==bind(listend,(structsockaddr
*)&server,sizeof(structsockaddr)))
{ //绑定
perror("binderror\n");
exit
(1);
}
if(-1==listen(listend,5))
{
perror("listenerror\n");exit
(1);
}
memset(&client,0,sizeof(structsockaddr_in));client.sin_family=AF_INET; //IPv4协议
while
(1)
{//外部死循环,用来重复连接客户端
memset(&client,0,sizeof(structsockaddr_in));
intconnectd;//不确定是否一定要在循环以内
if(-1==(connectd=accept(listend,(structsockaddr*)&client,&len)))
{//创建新的套接字连接客户端
perror("createconnectsocketerror\n");continue;
}
inet_ntop(AF_INET,&client.sin_addr,addr_p,sizeof(addr
_p)); //将客户端地址转为字符串
printf("clientIPis%s,portis
%d\n",addr_p,ntohs(client.sin_port));
interr;pthread_ttid;
pthread_attr_tattr;
err=pthread_attr_init(&attr);if(err!
=0)
{
printf("can'tinitattr%s\n",strerror(err));exit
(1);
}
err=pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED
);
if(err!
=0)
{
printf("can'tsetattr%s\n",strerror(err));exit
(1);
}
err=pthread_create(&tid,&attr,work,(void*)connectd);
if(err!
=0)
{
printf("can'tcreatethread
%s\n",strerror(err));
exit
(1);
}
}
close(listend);return0;
}
(接上)实验内容
(算法、程序、步骤和方法)
第二个实验是在之前那个端口扫描器的基础上修改的,有关建立线程的代码是在老师PPT上写的代码的基础上改的,如
port_segment中的dest原本是int,这里改成了直接用struct
sockaddr_in保存IP。
建立的线程比较多,依然使用了初始化属性来防止僵尸线程。
#include#include#include#include#include#include#include#include#include
#defineSEG_LEN655//一个线程扫描的端口数#defineTHREAD_NUM100//每个IP扫描建立的线程数#defineMAX_PORT65535//最大端口号
structport_seg
{
structsockaddr_inserver;unsignedmin_port;unsignedmax_port;
};
typedefstructport_segport_segment;
void*scan(void*arg){
port_segment*seg=(port_segment*)arg;intret;
intport=seg->min_port; //port为实际端口号
while(port<=seg->max_port) //把输入范围以内的所有都连接一次
{
seg->server.sin_port=htons(port); //每循环一次就设置一次端口号
intsockfd; //每次循环都重新建立套接字if(-
1==(sockfd=socket(AF_INET,SOCK_STREAM,0)))
{
perror("cannotcreatesocket\n");
exit
(1);
}
if(-1<(ret=connect(sockfd,(structsockaddr*)&seg->server,sizeof(structsockaddr))))
{
charaddr_p[2048];inet_ntop(AF_INET,&seg-
>server.sin_addr,addr_p,sizeof(addr_p));
printf("%s",addr_p);
printf("%d",port);
if(getservbyport(htons(port),"tcp")==
NULL)
printf("unknown\n");//服务为空的话就输
出unknown
else
printf("%s\n",getservbyport(htons(port),"tcp")-
>s_name);
}
close(sockfd);
port++; //端口号是递增的
}
//pthread_exit((void*)1);returnNULL;
}
intmain(intargc,char**argv)
{
intlen;
/*
f_port=atoi(argv[2]);//第一个端口号l_port=atoi(argv[3]);//最后一个端口号
*/
intdest_num=argc-1;//要扫描的IP数目
pthread_t*thread;
thread=(pthread_t*)malloc(THREAD_NUM*
sizeof(pthread_t));
intj=1;printf("%d\n",dest_num);
while(j<=dest_num){//外循环,一次针对一个IP
inti=0;
while(i一个端口
port_segmentport;
memset(&port.server,0,sizeof(struct
sockaddr_in));
port.server.sin_family=AF_INET;port.server.sin_addr.s_addr=
inet_addr(argv[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;
pthread_attr_tattr;
interr;
err=pthread_attr_init(&attr);if(err!
=0)
{
printf("can'tinitattr
%s\n",strerror(err));
exit
(1);
}
err=pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED
);
if(err!
=0)
{
printf("can'tsetattr
%s\n",strerror(err));
exit
(1);
}
if(pthread_create(&thread[i],&attr,scan,(void*)&port)!
=0)
{
perror("pthread_createfailed\n");free(thread);
exit(-2);
}
++i;
}j++;
//free(thread);
}
//return0;
}
数据记录和计算
结 论
(结果)
通过
小 结
虽说这次实验使用了多线程,但连互斥量和读写锁都没用上,所以感觉自己有必要另外去写一些和同步线程有关的程序来熟悉多线程同步,同
时也要学习用gdb调试线程。
指导老师评 议
成绩评定:
指导教师签名: