Socket是进程之间交换数据的机制。这些进程既可以是同一台机器上的,也可以是通过网络连接起来的不同机器。一旦一个Socket连接建立,那么数据就能够双向传输,直到其中一端关闭连接。
通常,请求数据的应用程序叫做客户端Client,而为请求服务叫做服务器Server。基本上说,首先,服务器监听一个端口,并且等待来自客户端的连接。之后客户端创建一个,并且尝试连接服务器。接着,服务器接受了来自客户端的连接,并且开始交换数据。一旦所有的数据都已经通过socket连接传输完毕,那么任意一方都可以关闭连接了。
我们的爬虫程序只需要client端就够了。
int build_connect(int *fd, char *ip, intport)
{
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if (!inet_aton(ip, &(server_addr.sin_addr))) {
return -1;
}
if ((*fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
return -1;
}
if (connect(*fd, (struct sockaddr *)&server_addr, sizeof(structsockaddr_in)) < 0) {
close(*fd);
return -1;
}
return 0;
}
void * recv_response(void * arg)
{
begin_thread();
int i, n, trunc_head = 0, len = 0;
char * body_ptr = NULL;
evso_arg * narg = (evso_arg *)arg;
Response *resp = (Response *)malloc(sizeof(Response));
resp->header = NULL;
resp->body = (char *)malloc(HTML_MAXLEN);
resp->body_len = 0;
resp->url = narg->url;
regex_t re;
if (regcomp(&re, HREF_PATTERN, 0) != 0) {/* compile error */
SPIDER_LOG(SPIDER_LEVEL_ERROR, "compile regex error");
}
SPIDER_LOG(SPIDER_LEVEL_INFO, "Crawling url: %s/%s",narg->url->domain, narg->url->path);
while(1) {
/* what if content-length exceeds HTML_MAXLEN? */
n = read(narg->fd, resp->body + len, 1024);
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
/**
* TODO: Why always recvEAGAIN?
* should we deal EINTR
*/
//SPIDER_LOG(SPIDER_LEVEL_WARN,"thread %lu meet EAGAIN or EWOULDBLOCK, sleep", pthread_self());
usleep(100000);
continue;
}
SPIDER_LOG(SPIDER_LEVEL_WARN, "Read socket fail: %s",strerror(errno));
break;
} else if (n == 0) {
/* finish reading */
resp->body_len = len;
if (resp->body_len > 0) {
extract_url(&re,resp->body, narg->url);
}
/* deal resp->body */
for (i = 0; i < (int)modules_post_html.size(); i++) {
modules_post_html[i]->handle(resp);
}
break;
} else {
//SPIDER_LOG(SPIDER_LEVEL_WARN, "read socket ok! len=%d", n);
len += n;
resp->body[len] = '\0';
if (!trunc_head) {
if ((body_ptr =strstr(resp->body, "\r\n\r\n")) != NULL) {
*(body_ptr+2) = '\0';
resp->header =parse_header(resp->body);
if(!header_postcheck(resp->header)) {
goto leave; /* moduluesfilter fail */
}
trunc_head = 1;
/* cover header */
body_ptr += 4;
for (i = 0; *body_ptr; i++){
resp->body[i] =*body_ptr;
body_ptr++;
}
resp->body[i] = '\0';
len = i;
}
continue;
}
}
}
leave:
close(narg->fd); /* close socket */
free_url(narg->url); /* free Url object */
regfree(&re);/* free regex object */
/* free resp */
free(resp->header->content_type);
free(resp->header);
free(resp->body);
free(resp);
end_thread();
return NULL;
}