C开发端口扫描

跟Delphi用法差不多。。文章中针对固定ip。。如何批量扫描ip段呢?

 

网上很多,可是并不一定适合基础不深的鸟鸟们学。要不就是代码大多都很长而且使用了多线程(关于多线程的很多概念就够闹腾的了,扫描部分就更算了),让我等小菜都望而生畏;要不就是速度很慢,学会了也派不上用场。今天我就介绍一下自己学习winsock后写的端口扫描软件吧!
    端口扫描软件的基本思路就不说了,没有什么很难的算法,大家想想就应该知道的,只要从起始端口到结尾端口都遍历一遍,找到打开的端口输出就可以了。大体的就是这个样子:for(CurrPort=StartPort;CurrPort<=EndPort;CurrPort++) {scan的执行体; }。这个软件没有使用到多线程技术,也就不用考虑那么多的关于多线程的概念了。因此我们的这个扫描软件从两个方面来讨论,第一方面是如何可以找到打开的端口,第二方面是如何提高扫描端口的速度。
    一、找到打开的端口
        在介绍如何找到打开的端口以前,让我们先来认识一个函数——connect()。connect函数将一个流套接字连接到指定IP地址的指定端口上。connect函数的用法:int connect(SOCKET s,const struct sockaddr FAR*  name,int namelen);参数s指定用于连接的套接字句柄,name参数指向一个sockaddr_in结构,用来指定要连接到的服务器的IP地址和端口,namelen参数则指定sockaddr_in结构的长度。这个参数连接成功的时候,函数返回0,否则返回值是SOCKET_ERROR。connect函数的用法大体我们就说这么多了。说到这里大家应该想到了吧?我们用connect函数的返回值进行判断,找到打开的端口号。好,看下具体的代码,有详细的注释,如果对函数不明白可以到MSDN或网上查询。
int scan(char *Ip, int StartPort, int EndPort)
{
        clock_t StartTime,EndTime; //扫描的开始时间和结束时间
        float CostTime;   //扫描过程中耗费的时间
        WSADATA wsa;  
        SOCKET s;
        struct sockaddr_in server;
        int CurrPort;  //当前端口
        int ret;
        WSAStartup(MAKEWORD(2,2),&wsa);  //使用winsock函数之前,必须用WSAStartup函数来装入并初始化动态连接库
        server.sin_family=AF_INET;  //指定地址格式,在winsock中只能使用AF_INET
        server.sin_addr.s_addr=inet_addr(Ip); //指定被扫描的IP地址
        StartTime=clock();
        for(CurrPort=StartPort;CurrPort<=EndPort;CurrPort++)
        {
                s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //创建套接字
                
                server.sin_port=htons(CurrPort); //指定被扫描IP地址的端口号
                ret=connect(s,(struct sockaddr *)&server,sizeof(server)); //连接
  
                if(0==ret) //判断连接是否成功
                {
                        printf("%s:%d/n",Ip,CurrPort);
                        closesocket(s);
                }
        }
        EndTime=clock();
        CostTime=(float)(EndTime-StartTime)/CLOCKS_PER_SEC;
        printf("Cost time:%f second/n",CostTime); //输出扫描过程中耗费的时间
        WSACleanup();  //释放动态连接库并释放被创建的套接字
        return 1;
}
通过if(0==ret)就可以判断是否连接成功(小菜特殊提示:这个把判断写成0==ret,它跟ret==0没有区别,只是在C语言里“==(等于)”与“=(赋值)”会搞混,如果写成0=ret的话,这样会报错的,是个不错的写法)。好,用主函数调用一下,试试我们的扫描软件如何。扫描本机的10个端口,竟然用了10秒多,如果去扫描一下网络上的主机,那就更慢了!为什么会这么慢呢?由于TCP协议的连接过程需要3次握手,也就是说确认连接用的数据包需要一定的往返时间,当连接互联网上的主机时,连接的过程往往需要几秒的时间。这种扫描速度真的让人无法接受啊!好,那就看看我们说的第二个方面吧,提高速度。

    二、提高扫描端口的速度
        有了上面的基础,那么我们来看看关于网络其他方面的知识吧!当一个套接字被创建的时候,它默认工作在阻塞模式下,但winsock建议程序员使用非阻塞模式。有两个函数可以用来改变一个套接字的模式:ioctlsocket函数和WSAAsyncSelect函数。ioctlsocket函数从BSD UNIX Socket规范中延续过来,它的用法是:
int ioctlsocket(SOCKET s,long cmd,u_long FAR* argp);参数s指定需要被设置模式的套接字句柄,cmd为命令参数,argp是一个指针,指向一个被cmd命令使用的参数。当cmd被指定为FIONBIO时,函数被用来改变套接字模式,这时如果argp指向的变量为1,那么套接字的工作模式被设置为非阻塞模式。这里我们不使用WSAAsyncSelect函数。使用select函数可以进行检测多个套接字是否可读、可写或是有错误发生,并且可以指定检测的超时时间,它的用法如下:
int select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds,fd_set FAR * exceptfds,const struct tim FAR * timeout);参数nfds参数是为了和BSD UNIX Socket的兼容而设置的,函数将这个参数忽略,readfds、writefds、exceptfds分别指向不同的FD_SET结构,用来指定需要检测的套接字句柄。在我们的端口扫描软件中,需要让writefds指定的套接字就绪,因为调用了connect函数的非阻塞模式套接字连接成功,套接字可以用来发送数据。有了这些知识,就可以看看具体的,完整的代码了。同样,有什么不懂的记得去网上或MSDN上查询。
#include
#include
#include
#pragma comment(lib,"ws2_32")
int scan(char *Ip, int StartPort, int EndPort);
int main(int argc,char **argv)
{
        int ret;
        if(argc!=4)
        {
                printf("Usage: %s /n",argv[0]);
                exit(1);
        }
        ret=scan(argv[1],atoi(argv[2]),atoi(argv[3]));
        if(ret)
                printf("Scan OK/n");
        return 0;
}
int scan(char *Ip, int StartPort, int EndPort)
{
        clock_t StartTime,EndTime;
        float CostTime;
        TIM TimeOut;
        FD_SET mask;
        WSADATA wsa;
        SOCKET s;
        struct sockaddr_in server;
        int CurrPort;
        int ret;
        unsigned long mode=1;  //ioctlsocket函数的最后一个参数
        WSAStartup(MAKEWORD(2,2),&wsa);
        TimeOut.tv_sec=0;  
        TimeOut.tv_usec=50;  //超时为50ms
        FD_ZERO(&mask);  
        server.sin_family=AF_INET;
        server.sin_addr.s_addr=inet_addr(Ip);
        StartTime=clock();
        for(CurrPort=StartPort;CurrPort<=EndPort;CurrPort++)
        {
                s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
                FD_SET(s,&mask);
                ioctlsocket(s,FIONBIO,&mode);  //设置为非阻塞模式
                server.sin_port=htons(CurrPort);
                connect(s,(struct sockaddr *)&server,sizeof(server));
                ret=select(0,NULL,&mask,NULL,&TimeOut);  //查询可写入状态
                if(0==ret || -1==ret)
                {
                        closesocket(s);
                }
                else
                {
                        printf("%s:%d/n",Ip,CurrPort);
                        closesocket(s);
                }
        }
        EndTime=clock();
        CostTime=(float)(EndTime-StartTime)/CLOCKS_PER_SEC;
        printf("Cost time:%f second/n",CostTime);
        WSACleanup();
        return 1;
}
好了,编译一下它,让我们看看它的扫描速度吧!扫描了100个端口,才用了2秒钟,没有使用多线程技术,同样也可以让速度提高!
这个程序都使用了winsock2提供的函数,只要了解了网络相关方面的函数就完全可以掌握了。希望会给大家带来提高了!如果有什么不正确的地方请大家指出,同时如果大家有什么新的想法,也请大家共享出来

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值