我用VC6.0编写了一个端口扫描的客户端程序,在运行之后碰到如下问题:  
扫描程序很好地判断出指定tcp端口的开启状态,但扫描指定且已开启的udp端口时,给出的结果却是该端口已关闭.  
请高手们帮帮忙,看看是否是recvfrom函数有问题??消息处理函数的主要代码如下: void  CPortScannerClientDlg::OnButscan()    
{      
     UpdateData();  
     AfxSocketInit();        //初始化套接字  
 
     char  buffer[500+1];  
             
 
     //tcp  端口扫描  
    if(m_combo.GetCurSel()==0)  
   {              
 
     memset(buffer,0,500+1);  
     if  (m_socket.Create(0,SOCK_STREAM,"192.168.0.226"))  
     {  
        DWORD  RemotePort=m_port;  
        if(m_socket.Connect(m_host,RemotePort))      
        {  
            m_list.AddString("the  target  port  is  open!");  
            m_list.AddString("you  can  communicate  with servernow!");  
            m_socket.Close();  
 
         }  
         else  
         {  
           m_list.AddString("sorry,the  target  port  is  close  now!");  
           m_socket.Close();  
         }  
      }  
                       
   }  
 //udp  端口扫描  
   else  
   {  
       memset(buffer,0,500+1);  
       if(m_socket.Create(0,SOCK_DGRAM,"192.168.0.226"))  
       {  
          DWORD  RemotePort  =  m_port;  
          int  i=2;  
          while(i>0)  
          {  
              int  len=sizeof(m_host);  
              int  ret=recvfrom(m_socket,buffer,sizeof(buffer),0,
              (struct  sockaddr*)&m_host,&len);  
              if(ret==SOCKET_ERROR)                                    
              {  
                 i--;  
                 m_list.AddString("sorry,the  target  port  is
                 closed!");  
                                                           
              }  
              else  
              {  
                 m_list.AddString("the  target  port  is  open!");  
                 m_list.AddString("you  can  communicate  now.");  
                 break;  
 
              }  
           }  
           m_socket.Close();  
       }  
                    
    }  
                    
             
}

解决方案 »

  1.   

    tcp端口扫描的原理是用connect,如果connect(tcp3路握手)成功返回就表示端口存活
       由于udp是无连接的协议,不能根据 “握手” 来判定连接是否建立,只能向目的端口发送数据,然后recvfrom,从recvfrom地返回值判断是否调用WSAGetLasstError()来判断 "icmp端口不可达到"错误,和traceroute程序的原理差不多。   所以,你的程序需要该两个地方
       1.m_socket.Create(0,SOCK_DGRAM,"192.168.0.226")成功后需要调用一下,connect函数,因为只有在已经连接的udp套接字上才会,返回icmp异步错误;
       2.首先调用一下sendto函数,然后再调用recvform
      

  2.   

    UDP ICMP端口不能到达扫描
    使用UDP协议时,由于打开的端口对扫描探测并不发送一个确认,关闭的端口也并不需要发送一个错误数据包。但是许多主机在你向一个未打开的UDP端口发送一个数据包时,会返回一个ICMP_PORT_UNREACH错误。这样你就能发现哪个端口是关闭的。UDP和ICMP错误都不保证能到达,因此这种扫描器必须还实现在一个包看上去是丢失的时候能重新传输。这种扫描方法是很慢的,因为RFC对ICMP错误消息的产生速率做了规定。同样,这种扫描方法需要具有root权限。UDP recvfrom()和write() 扫描  
    当非root用户不能直接读到端口不能到达错误时,Linux能间接地在它们到达时通知用户。比如,对一个关闭的端口的第二个write()调用将失败。在非阻塞的UDP套接字上调用recvfrom()时,如果ICMP出错还没有到达时回返回EAGAIN-重试。如果ICMP到达时,返回ECONNREFUSED-连接被拒绝。这就是用来查看端口是否打开的技术。
      

  3.   

    谢谢"g20044111(虚心向学)"的关注,
    谢谢"Aaron_Jerry(音乐诗人)"的提示,
    异常感谢"anjuta_c()"的指点!按照"anjuta_c()"的提示,我做了修改,不过我不知道ICMP_PORT_UNREACH在什么文件中被定义,刚才运行的时候,编译器提示说"error C2065: 'ICMP_PORT_UNREACH' : undeclared identifier
    Error executing cl.exe."
    请问要包含什么文件?
      

  4.   

    windows paltform sdk下没有这个ICMP_PORT_UNREACH宏,这个宏是用于实现icmp模块的时候定义的命令字,每个宏代表不同的icmp消息类型。windows,linux,unix测试是否存在 监听 的udp端口的关键是怎么样得到目标系统返回的icmp unreachable包?我知道的有两种方法
      1.建立一个已连接的 udp socket(对未连接的udp socket,udp协议栈不会返回ip层返回过来的icmp unreachable消息);
        然后send一些数据,当然也可以是0字节数据,如果send 0个字节,就代表底层生成了一个 ip头部(20字节)+udp头部(8字节)+没有数据的ip数据报。
        然后recv数据,recv函数需要设置一下超时;    # 如果recv返回错误,并且原因是超时,有三种可能
            1.该udp端口在监听
            2.该udp端口被防火墙拦截(测试tcp端口有同样的情况,如果防火墙存在就不能确定端口是否存活)
            3.目标机器返回过来的icmp unreachable包丢失(这种情况比较小)
            上边的第3种情况,都可以判断为该端口在监听,第3种情况可以用重发一次数据报或多次数据报来解决    # 如果recv返回错误,在xp_sp2下错误代码是 WSAECONNRESET=10054,就是连接被重置;
    在linux|unix下返回的错误代码是ECONNREFUSED,就是连接被拒绝的意思;这种情况就代表该udp端口没有被系统打开。   
        2.就是直接使用raw socket,比较复杂,就不说了:);
      

  5.   

    # 如果recv返回错误,在xp_sp2下错误代码是 WSAECONNRESET=10054,就是连接被重置;
    在linux|unix下返回的错误代码是ECONNREFUSED,就是连接被拒绝的意思;这种情况就代表该udp端口没有被系统打开。在这种情况下,如果打开wireshark捕获一下系统ip包,就可以看到目标系统返回来了icmp unreachabled包,不同的操作系统可能会把这个icmp unreachabled映射成不同错误代码
      

  6.   

    这是我写的监测一个udp端口的代码。lz参考一下int _tmain(int argc, _TCHAR* argv[])
    {
    WSADATA        wsd;
    sockaddr_in servaddr;
    SOCKET s;
    int error;
    int itimeout=1000;//接受超时,1秒
    int            ret;
    char sendbuf[5]={0};
    char recvbuf[1024]={0};    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
        {
            printf("WSAStartup failed!\n");
            return 1;
        } s = socket(AF_INET, SOCK_DGRAM, 0);
        if (s == INVALID_SOCKET)
        {
            printf("socket() failed; %d\n", WSAGetLastError());
            return 1;
        }
    setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(char*)&itimeout,sizeof(itimeout)); memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(53);    if ((servaddr.sin_addr.s_addr = inet_addr("192.168.2.99"))
    == INADDR_NONE)
        {
            struct hostent *host=NULL;        host = gethostbyname("192.168.2.99");
            if (host)
                CopyMemory(&servaddr.sin_addr, host->h_addr_list[0],
                    host->h_length);
            else
            {
                printf("gethostbyname() failed: %d\n", WSAGetLastError());
                WSACleanup();
                return 1;
            }
        } if (connect(s, (SOCKADDR *)&servaddr, sizeof(servaddr)) == SOCKET_ERROR)
        {
            printf("connect() failed: %d\n", WSAGetLastError());
            WSACleanup();
            return 1;
        }    ret = send(s,sendbuf, 0, 0);
        if (ret == SOCKET_ERROR)
        {
            printf("send() failed: %d\n", WSAGetLastError());
        }
        else if (ret == 0)
            printf("send() successful!\n"); ret = recv(s,recvbuf,1024,0);
        if (ret == SOCKET_ERROR)
        {
    error = WSAGetLastError();
    if (error == WSAETIMEDOUT)
               printf("recv() failed timeout dst port is active!\n");
    else if (error == WSAECONNRESET)
    printf("recv() failed icmp unreachabled\n");
    else
    printf("recv() unknow error\n");
        } return 0;
    }
      

  7.   

    非常感谢"anjuta_c()"的热心指点!!
    谢谢!!