⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 425.htm

📁 unix高级编程原吗
💻 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 + -