该爬虫的主事件流程大致如下:

1.获取命令行参数,执行相应操作

2.读取配置文件,解析得到各种设置

3.载入各种模块

4.种子入队,开启DNS解析线程(原始队列不为空时解析)

5.创建epoll,开启任务,发起请求等等,关注事件

6.while大循环中使用epoll_wait返回活跃的事件,每个事件开启一个线程处理(线程中主要是解析页面,保存页面,url处理等),在线程结束的时候可能会开启新的任务。

创建epoll

//创建epoll,参数为监听的数目(自从linux2.6.8之后,size参数是被忽略的)
g_epfd = epoll_create(g_conf->max_job_num);

开始若干个任务,关注事件

while(ourl_num++ < g_conf->max_job_num)
{
if (attach_epoll_task() < 0)//执行epoll任务,如果中途遇到队列为空的情况就退出循环
break;
}

attach_epoll_task函数如下:

//开始一个epoll任务,建立连接还有就是关注事件
int attach_epoll_task()
{
struct epoll_event ev;
int sock_rv;
int sockfd;
Url * ourl = pop_ourlqueue();//从url队列取出一个url
if (ourl == NULL)
{
SPIDER_LOG(SPIDER_LEVEL_WARN, "Pop ourlqueue fail!");
return -1;
} // connect socket and get sockfd
//连接
if ((sock_rv = build_connect(&sockfd, ourl->ip, ourl->port)) < 0)
{
SPIDER_LOG(SPIDER_LEVEL_WARN, "Build socket connect fail: %s", ourl->ip);
return -1;
} set_nonblocking(sockfd);//设定socket模式,非阻塞
//发送请求
if ((sock_rv = send_request(sockfd, ourl)) < 0)
{
SPIDER_LOG(SPIDER_LEVEL_WARN, "Send socket request fail: %s", ourl->ip);
return -1;
}
////保存触发事件的某个文件描述符相关的数据(与具体使用方式有关) // typedef union epoll_data {
// void *ptr;
// int fd;
// __uint32_t u32;
// __uint64_t u64;
// } epoll_data_t;
// //感兴趣的事件和被触发的事件
// struct epoll_event {
// __uint32_t events; /* Epoll events */
// epoll_data_t data; /* User data variable */
// };
evso_arg * arg = (evso_arg *)calloc(1, sizeof(evso_arg));
arg->fd = sockfd;
arg->url = ourl;
ev.data.ptr = arg;
ev.events = EPOLLIN | EPOLLET;//边沿触发 if (epoll_ctl(g_epfd, EPOLL_CTL_ADD, sockfd, &ev) == 0)//EPOLL_CTL_ADD注册事件
{// add event
SPIDER_LOG(SPIDER_LEVEL_DEBUG, "Attach an epoll event success!");
}
else
{
SPIDER_LOG(SPIDER_LEVEL_WARN, "Attach an epoll event fail!");
return -1;
} g_cur_thread_num++; //当前正在执行抓取的任务数(关注的事件数)
return 0;
}

while大循环

……
……
……
while(1)
{
n = epoll_wait(g_epfd, events, 10, 2000);//epoll将会把发生的事件赋值到events数组中,超时时间单位毫秒
printf("epoll:%d\n",n);//打印活跃事件数
if (n == -1)
printf("epoll errno:%s\n",strerror(errno));
fflush(stdout); if (n <= 0) //退出是在这里
{//判断事件数,队列是否为空,为空退出
if (g_cur_thread_num <= 0 && is_ourlqueue_empty() && is_surlqueue_empty())
{
sleep(1);
if (g_cur_thread_num <= 0 && is_ourlqueue_empty() && is_surlqueue_empty())
break;
}
} for (i = 0; i < n; i++) //n为活跃的事件数
{
evso_arg * arg = (evso_arg *)(events[i].data.ptr);
//发生错误,挂起,不是epollin事件
if ((events[i].events & EPOLLERR) ||(events[i].events & EPOLLHUP) ||(!(events[i].events & EPOLLIN)))
{
SPIDER_LOG(SPIDER_LEVEL_WARN, "epoll fail, close socket %d",arg->fd);
close(arg->fd);//关闭文件描述符
continue;
}
//注销事件
epoll_ctl(g_epfd, EPOLL_CTL_DEL, arg->fd, &events[i]); // del event printf("hello epoll:event=%d\n",events[i].events);
fflush(stdout);//清除读写缓冲区,需要立即把输出缓冲区的数据进行物理写入时
//create_thread是自己封装的,在线程的头文件中
//recv_response为回调函数
//产生epollin事件就调用接收函数接收,产生一个线程处理任务
create_thread(recv_response, arg, NULL, NULL);//创建线程处理
}
}
……
……
……

create_thread(recv_response, arg, NULL, NULL);该函数创建线程,在线程结束的时候会开启新的任务,将事件添加进epoll关注,epoll_wait返回后就注销事件,并创建新的线程去处理这个事件

注意下面的代码

 if (n <= 0) //退出是在这里
{//判断事件数,队列是否为空,为空退出
if (g_cur_thread_num <= 0 && is_ourlqueue_empty() && is_surlqueue_empty())
{
sleep(1);
if (g_cur_thread_num <= 0 && is_ourlqueue_empty() && is_surlqueue_empty())
break;
}
}

满足处理的线程数小于等于0,或者两个队列都为空则会退出循环,程序也就此结束。

一只简单的网络爬虫(基于linux C/C++)————主事件流程的更多相关文章

  1. 一只简单的网络爬虫(基于linux C/C++)————开篇

    最近学习开发linux下的爬虫,主要是参考了该博客及其他一些网上的资料.网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息 ...

  2. 一只简单的网络爬虫(基于linux C/C++)————浅谈并发(IO复用)模型

    Linux常用的并发模型 Linux 下设计并发网络程序,有典型的 Apache 模型( Process Per Connection ,简称 PPC ), TPC ( Thread Per Conn ...

  3. 一只简单的网络爬虫(基于linux C/C++)————支持动态模块加载

    插件在软件设计中有很大的好处,可以方便地扩展各种功能,使用插件技术能够在分析.设计.开发.项目计划.协作生产和产品扩展等很多方面带来好处: (1)结构清晰.易于理解.由于借鉴了硬件总线的结构,而且各个 ...

  4. 一只简单的网络爬虫(基于linux C/C++)————socket相关及HTTP

    socket相关 建立连接 网络通信中少不了socket,该爬虫没有使用现成的一些库,而是自己封装了socket的相关操作,因为爬虫属于客户端,建立套接字和发起连接都封装在build_connect中 ...

  5. 一只简单的网络爬虫(基于linux C/C++)————守护进程

    守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装入时启动, ...

  6. 一只简单的网络爬虫(基于linux C/C++)————读取命令行参数及日志宏设计

    linux上面的程序刚开始启动的时候一般会从命令行获取某些参数,比如以守护进程运行啊什么的,典型的例子就是linux下的man,如下图所示 实现该功能可以使用getopt函数实现,该函数在头文件uni ...

  7. 一只简单的网络爬虫(基于linux C/C++)————利用正则表达式解析页面

    我们向一个HTTP的服务器发送HTTP的请求后,服务器会返回可能一个HTML页面(当然也可以是其他的资源),我们可以利用返回的HTML页面,在其中寻找其他的Url,例如我们可以这样在浏览器上查看一下H ...

  8. 一只简单的网络爬虫(基于linux C/C++)————线程相关

    爬虫里面采用了多线程的方式处理多个任务,以便支持并发的处理,把主函数那边算一个线程的话,加上一个DNS解析的线程,以及我们可以设置的max_job_num值,最多使用了1+1+max_job_num个 ...

  9. 一只简单的网络爬虫(基于linux C/C++)————Url处理以及使用libevent进行DNS解析

    Url处理 爬虫里使用了两个数据结构来管理Url 下面的这个数据结构用来维护原始的Url,同时有一个原始Url的队列 //维护url原始字符串 typedef struct Surl { char * ...

随机推荐

  1. Linux C++ 网络编程学习系列(2)——多路IO之select实现

    select实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/select 源码说明: server.cpp: 监听127.1:6666 ...

  2. 搭建WEB、NFS共享、sersync实时同步以及全网定时备份服务流程

    本次实验的主要目的: 1.搭建web服务,使用nfs服务共享的/data目录挂载到web站点目录上. 2.nfs服务器与backup服务器使用sersync实时同步/data目录中的文件. 3.bac ...

  3. 编写高质量Python程序(三)基础语法

    本系列文章为<编写高质量代码--改善Python程序的91个建议>的精华汇总. 关于导入模块 Python的3种引入外部模块的方式:import语句.from ... import ... ...

  4. day02,静态库和动态库

    一.首先我们来看一下什么是静态库和动态库,在这之前我们来看一下编译成可执行文件的过程: 1.静态库(.a..lib):就是在使用的时候会把代码复制到文件中: 它的优点:独立,在链接后不需要静态库源文件 ...

  5. Python—一个简单搜索引擎索引库

    因为课业要求,搭建一个简单的搜索引擎,找了一些相关资料并进行了部分优化(坑有点多) 一.数据 数据是网络上爬取的旅游相关的攻略页面 这个是travels表,在索引中主要用到id和url两个字段. 页面 ...

  6. JSON Extractor(JSON提取器)

    JSON提取器 Variable names(名称):提取器的名称Apply to(应用范围):Main sample and sub-samples:应用于主sample及子sampleMain s ...

  7. Daily Scrum 12/24/2015

    Process: Zhaoyang: Some UI change and compile the Caffe in the IOS. Yandong: Do some code integratio ...

  8. .NET Core 初识

    什么是 ASP.NET Core? ASP.NET Core 是一个新的开源和跨平台的框架,用于构建如 Web 应用.物联网(IoT)应用和移动后端应用等连接到互联网的基于云的现代应用程序.ASP.N ...

  9. 模糊字符串匹配:FuzzyWuzzy

    FuzzyWuzzy 模糊字符串匹配,它使用Levenshtein Distance来计算简单易用的包中序列之间的差异. 前置条件 Python 2.7 or higher difflib pytho ...

  10. Java中常量的概念

    常量:在程序执行过程中,其值不发生改变的量.分类:A:字面值常量B:自定义常量字面值常量A:字符串常量(用“”括起来的内容).举例:"hello"B:整数常量 (所有的整数)举例: ...