📄 425.htm
字号:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>CTerm非常精华下载</title>
</head>
<body bgcolor="#FFFFFF">
<table border="0" width="100%" cellspacing="0" cellpadding="0" height="577">
<tr><td width="32%" rowspan="3" height="123"><img src="DDl_back.jpg" width="300" height="129" alt="DDl_back.jpg"></td><td width="30%" background="DDl_back2.jpg" height="35"><p align="center"><a href="http://apue.dhs.org"><font face="黑体"><big><big>apue</big></big></font></a></td></tr>
<tr>
<td width="68%" background="DDl_back2.jpg" height="44"><big><big><font face="黑体"><p align="center"> ● UNIX网络编程 (BM: clown) </font></big></big></td></tr>
<tr>
<td width="68%" height="44" bgcolor="#000000"><font face="黑体"><big><big><p align="center"></big></big><a href="http://cterm.163.net"><img src="banner.gif" width="400" height="60" alt="banner.gif"border="0"></a></font></td>
</tr>
<tr><td width="100%" colspan="2" height="100" align="center" valign="top"><br><p align="center">[<a href="index.htm">回到开始</a>][<a href="316.htm">上一层</a>][<a href="426.htm">下一篇</a>]
<hr><p align="left"><small>发信人: xh (强吻), 信区: Socket <br>
标 题: 原码重读(1)--proxy scan <br>
发信站: 华南网木棉站 (Sun Nov 7 14:06:22 1999), 转信 <br>
<br>
在Netguy的proxy scan大作里,我们可以学到 <br>
1) 设计后台运行的程序 <br>
2) 操作日志文件 <br>
3) 无阻塞socket的connect <br>
4) 用select进行超时限制 <br>
5) 异常信号的处理 <br>
6) IP的字符串形式和数字形式 <br>
7) 用getsockopt检测是否存在错误 <br>
<br>
下面是Netguy的原程,为了讲解方便,调了一下顺序,对程序作了精简 <br>
原码可在socket版精华区Happy的大作里面找到. <br>
<br>
命令的参数是: 起始IP地址 结束IP地址 日志文件名 <br>
<br>
char serverName[20]; <br>
<br>
main(int argc,char *argv[]) <br>
{ <br>
/*1. 获取搜索的IP范围 <br>
inet_addr()将字符串格式的IP地址转为一个数字形式(255.255.255.255 to FFFFFF) <br>
ntohl()将以网络字节顺序排列的IP地址转为以本机字节顺序排列 <br>
*/ <br>
<br>
startIP=ntohl(inet_addr(argv[1])); <br>
endIP=ntohl(inet_addr(argv[2])); <br>
<br>
/*2.将搜索情况和结果纪录到日志文件 <br>
fprintf直接对文件进行格式化输入,然后用fflush()存盘. <br>
是书写日志文件的标准用法,在后面的程序里将反复出现。 <br>
*/ <br>
<br>
FILE *f = fopen(argv[3],"a"); //打开日记文件 <br>
fprintf(f,"%s--->%s\n",argv[1],argv[2]);//纪录搜索的范围 <br>
fflush(f); <br>
<br>
/* 处理SIGTERM信号 */ <br>
<br>
signal(SIGTERM,terminate); //用terminate函数处理kill命令的信号 <br>
<br>
/*3.以下程序段演示如何书写后台运行的程序*/ <br>
<br>
<br>
/*第一步,先用fork()复制一个跟父进程完全一样的子进程,然后父进程退出 , <br>
用户在shell下将从新得到提示符,可以继续进行别的操作。而子进程(替身)将 <br>
在后台继续运行。*/ <br>
<br>
switch(fork()) <br>
{ <br>
case 0: /*返回值是0,表明是子进程,继续在后台运行*/ <br>
break; <br>
case -1: /* 出错 */ <br>
perror("fork( )"); <br>
exit(-1); <br>
default: /* 返回值不为0,表明是父进程部分,结束 */ <br>
exit(0); <br>
} <br>
<br>
/* 余下的代码将由子进程执行 */ <br>
<br>
/*第二步,使子进程摆脱外来信号的干扰*/ <br>
/*脱离进程组和对话过程,否则子进程将接收任何作为整体发给该进程组或对 <br>
话过程的信号*/ <br>
<br>
setsid(); <br>
setsid(); <br>
<br>
/*切断与控制台的联系 ,否则子进程将接收运行该程序的控制台产生的信号*/ <br>
<br>
i=open("/dev/tty",O_RDWR); <br>
ioctl(i,TIOCNOTTY,0); <br>
close(i); <br>
<br>
/*这个程序不是daemon,否则还要加上umask(设置全限位过滤),changdir(改变 <br>
根目录),以及用文件锁防止程序重复执行等操作*/ <br>
<br>
/*4.下面程序正式开始搜索proxy*/ <br>
for(k=startIP;k<=endIP;k++) /* 循环扫描指定范围内的所有IP */ <br>
{ <br>
if( (k % 256)==0) //略过localhost地址 <br>
continue; <br>
if( (k % 256)==255) //略过广播地址 <br>
continue; <br>
<br>
/*将数字地址转成字符串形式 */ <br>
struct in_addr serverIP; <br>
serverIP.s_addr=k; <br>
sprintf(serverName,"%s",inet_ntoa(serverIP)); //将搜索的IP纪录到serverName <br>
findProxy(k); //对该地址是否存在proxy进行搜索 <br>
} <br>
close(f); <br>
} //主函数结束,我们再看findproxy() <br>
void findProxy(u_long addr) <br>
{ <br>
int port[N]={ 80,8080,3128,1080}; //欲搜的端口号 <br>
for(i=0;i<4;i++) //开始逐个尝试这4个端口 <br>
{ <br>
host.sin_family=AF_INET; <br>
host.sin_addr.s_addr=htonl(addr); <br>
host.sin_port=htons(port[i]); <br>
int sockfd=socket(AF_INET,SOCK_STREAM,0); <br>
<br>
/* 设为非阻塞式socket <br>
因为阻塞式connect()将会阻塞round-trip所需的时间(由内核根据网络 <br>
距离计算)才会认为超时,返回失败 ,这显然不符合快速搜索proxy的要求。 <br>
因此采用非阻塞的connect(),connect()被调用后将立即返回,如果连接 <br>
已经完成就返回0,否则返回-1,errno设为EINPROGRESS, 而TCP三次握手 <br>
则继续进行,连接的结果将通过select获取。所以配合select,就能限制 <br>
connect() TimeOut的时间。 <br>
*/ <br>
*/ <br>
fcntl(sockfd,F_SETFL,O_NDELAY) <br>
status=connect(sockfd,(struct sockaddr *)& host,sizeof(host)); <br>
/*上面的程序段演示了如何限制超时*/ <br>
timeout.tv_sec=2; // 超时限制为2秒 <br>
timeout.tv_usec=0; <br>
<br>
struct fd_set mask; <br>
FD_ZERO( & mask); //清空集合mask <br>
FD_SET(sockfd,& mask); //将sockfd放入集合mask中 <br>
/*select用来判断文件描述符是否就绪 <br>
select的5个参数为需要判断的最大的描述符+1,需要判断是否可读的描述 <br>
符集合,可写的,异常的描述符的集合,select的等待的时间 <br>
第5个参数为0时,将立刻超时,从而有效地轮询集合中的所有的文件描述符 <br>
为NULL时,将会阻塞,直到其中一个描述符就绪。在这里,timeout设为2秒. <br>
按协议,如果非阻塞的connect()连接成功,sockfd将可写,如果出错, <br>
则可读可写,如果没连上,select返回0, 所以这里判断sockfd是否可写 <br>
*/ <br>
<br>
status=select(sockfd+1,NULL,& mask,NULL,& timeout); <br>
switch(status) <br>
{ <br>
case -1: <br>
case -1: <br>
fprintf(f,"select( ) error at %s\t%d\n",serverName,port[i]); <br>
fclose(f); <br>
close(sockfd); <br>
exit(-1); <br>
case 0: /* 连接超时则继续扫下一个IP地址 */ <br>
close(sockfd); <br>
break; <br>
default: /* 连上了 */ <br>
if( FD_ISSET(sockfd,& mask) ) <br>
{ <br>
int errorCode=1; <br>
errorLen=sizeof(errorCode); <br>
/*判断是否出错。如果出错,内核将置一个内核变量so_error. <br>
通过getsockopt(..SO_ERROR)将获得这个关于socket的未判定 <br>
的错误值(然后so_error将清0) <br>
*/ <br>
<br>
getsockopt(sockfd,SOL_SOCKET,SO_ERROR,&errorCode,&errorLen); <br>
if(errorCode==0) //errorCode=0表明无错,proxy可用 <br>
fprintf(f,"%s\t%d\n",serverName,port[i]); //纪录IP和port <br>
} <br>
} <br>
} <br>
close(sockfd); <br>
fflush(f); <br>
} <br>
<br>
/*最后我们看一下kill信号发出时的处理函数,就是纪录kill时搜索到的IP, <br>
以便下次继续搜索*/ <br>
void terminate(void) /* 处理SIGTERM信号 */ <br>
{ <br>
fprintf(f,"Program killed at %s\n",serverName); <br>
fclose(f); <br>
exit(0); <br>
} <br>
<br>
发信人: xh (爱我--很累), 信区: Socket <br>
标 题: Re: 原码重读(1)--proxy scan <br>
发信站: 华南网木棉站 (Tue Nov 9 21:33:41 1999), 转信 <br>
Hehe,今天找不到什么好题材,就拿这篇开灌吧. <br>
的确,这里存在着函数重入的问题。所谓函数重入性,可以理解为 <br>
如果一个函数要对一块静态内存区域进行操作,那么它就是非重入函数, <br>
因为如果这个函数在同一时间被调用两次的话,将有两份拷贝同时对那 <br>
个静态区域进行操作,就可能产生不可预计的后果了。 <br>
相反,如果函数只对自己局部变量和用new/malloc生成的堆内存对象( <br>
进行操作的话,则认为是可重入的,因为它即使被同时调用两次,这两份拷贝 <br>
也互不干扰,局部变量存在于各自的栈区中,new出来的空间在堆区也不会重叠。 <br>
因为signal handler的执行函数很可能与主函数体中的函数一起被调用, <br>
所以对signal handler的函数一定要小心选择。当然,更显而易见需要小心的 <br>
就是多线程的函数了,它们无时无刻不是在并行的。在CPU的大作Solaris多线程 <br>
编程里有关于安全线程的论述。举一些例子,如这里的fprintf对相同的文件操作 <br>
还有inet_ntoa(),解决方法是用锁来实现互斥 <br>
-- <br>
<br>
<br>
<br>
<br>
V2 Studio Xh2000 <br>
<br>
<br>
<br>
<br>
※ 修改:.xh 于 Nov 7 14:21:47 修改本文.[FROM: 202.38.248.62] <br>
</small><hr>
<p align="center">[<a href="index.htm">回到开始</a>][<a href="316.htm">上一层</a>][<a href="426.htm">下一篇</a>]
<p align="center"><a href="http://cterm.163.net">欢迎访问Cterm主页</a></p>
</table>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -