C++C++ socket网络爬虫(1)

C++写的socket网络爬虫,代码会以最终一糟糕上课中提供于大家,同时自身为会于描绘的而不断的对准代码进行完善以及改

 

自己先是为大家教如何用网页中的情,文本,图片等下充斥及电脑遭到。

C++ 1

我会教大家如何以百度首页上之这百度标志图片(http://www.baidu.com/img/bdlogo.gif)抓到手下充斥到电脑遭到。

C++ 2

 

次第的片代码如下,讲解在代码的下,下充斥链接以最终吃出,

int main()
{

    string url = "www.baidu.com";
    string name = "/img/bdlogo.gif";
    int port = 80;
    int client_socket = makeSocket(url,port);//1
    string request = "GET " + name + " HTTP/1.1\r\nHost:" + url + "\r\nConnection:Close\r\n\r\n";//2

    if (send(client_socket, request.c_str(), request.size(), 0) == SOCKET_ERROR)//3
    {
        cout << "send error" << endl;
    }

    fstream file;
    string fileName = FileName(name);//4
    file.open(fileName, ios::out | ios::binary);//5

    char buf[1024];
    ::memset(buf, 0, sizeof(buf));//6
    int n = 0;
    n = recv(client_socket, buf, sizeof(buf)-sizeof(char), 0);//3
    char* cpos = strstr(buf, "\r\n\r\n");//7
    file.write(cpos + strlen("\r\n\r\n"), n - (cpos - buf) - strlen("\r\n\r\n"));//7
    while ((n = recv(client_socket, buf, sizeof(buf)-sizeof(char), 0)) > 0)//7
    {
        try
        {
            file.write(buf, n);
        }
        catch (...)
        {
            cerr << "ERROR" << endl;
        }
    }
    file.close();
    closesocket(client_socket);
    system("pause");
    return 0;
}

一、main函数

1、makeSocket(url,port)

int makeSocket(string host,int
port)函数是我好修的,接受两个参数,一个是域名还是主机名,第二单凡是所利用的端口号,返回一个用以创造socket的int型数据,将以即时等同页的二.makeSocket中开展讲解

 

2、string request = “GET ” + name + ” HTTP/1.1\r\nHost:” + url +
“\r\nConnection:Close\r\n\r\n”;

夫是http的要报头,有过多的音信,这里就对当下词话被利用到的开展讲解

GET 请求获取Request-URI所标识的资源;

 

name 所标识的资源;

 

HTTP/1.1 表示求的HTTP协议版本;

 

Host:url  指定为请资源的Internet主机和端口号,通常由HTTP
URL中取出的,


我们于浏览器中输入http://baidu.com/index.html浏览器发送的呼吁消息遭,就会见含有Host请求报头域,如下:
Host:www.baidu.com

此间使用短省端口号80,若指定了端口号,则成:Host:www.baidu.com:port

 

Connection:Close
Connection字段用于设定是否利用长连,在http1.1中默认是运长连的,即Connection的价值为Keep-alive,如果不思量以长连则需明确指出connection的值为close

Connection:Close表明当前在利用的tcp链接在呼吁处理完毕后会叫断掉。以后client再展开新的恳求时虽非得创造新的tcp链接了,即必须从新创建socket

又多关于http协议的情可查考http://blog.csdn.net/gueter/article/details/1524447  
HTTP协议详解

 

小心最后一定要是坐一个单身的\r\n作为结束标志

 

3.send/recv

send用于向服务端发送信息

recv/send函数原型如下

int recv(SOCKET s,char FAR * buf,int len,int flags)/int send(SOCKET
s,const char FAR * buf,int len,int flags);

先是个参数表示代表对方的socket,

亚只参数为接受读取的信的字符串

其三个参数为该字符串的分寸

季单参数可以为此来支配读写操作

端详可参照http://www.cnblogs.com/magicsoar/p/3587351.html 中的讲解1

 

4 FileName(name)

自己修的string FileName(string
dir)函数,由于windows中文件之名中不允许含有/

为此FileName函数用于将dir中之富有/替换为_

string FileName(string dir)
{
    string search = "/";
    int pos = 0;
    while ((pos = dir.find(search, pos)) != string::npos) {
        dir.replace(pos, search.size(), "_");
        pos++;
    }
    return dir;
}

如string FileName(“img/bdlogo.gif”)返回_img_bdlogo.gif

 

5 file.open(fileName, ios::out | ios::binary)r45

ios::out以出口方式打开文件,如果文件不存在即创造新的文书

ios::binary因二进制模式进行I/O操作,这里用二进制模式是为了科学的处理图片的下载

 

6 ::memset(buf, 0, sizeof(buf));

函数原型为void *memset(void *s, int ch, size n);

函数解释:将s所指的内存中前n个字节 (typedef unsigned int size_t)用 ch
替换并返回 s 。

memset:作用是于同段外存块中填充某个给定的值,它是本着较生之结构体和数组进行清零操作的平种比较快方法

 

7 在收取和讲请求消息后,服务器返回一个HTTP响应消息。

HTTP响应也是由于三只有构成,分别是:状态行、消息报头、响应正文

响应正文就是服务器返回的资源的情节,所以我们用跨越了态行和信报头部分。

信息报头与相应正文中可据此\r\n\r\n进行区分,当第一不良发现收到至之字符串数组中蕴藏\r\n\r\n时,则将\r\n\r\n前底内容尽忽视,将剩余的情节写到文件被去

 

strstr(*str1,
*str2)实现从字符串str1中找寻是否生字符串str2,如果生,从str1中的str2位置于,返回str1中str2起开始位置的指针,如果没有,返回null。

鉴于同样软顶多可接受1024个字符,而\r\n极有或位于中等位置,所有我们设用1024独char中居\r\n之后的数码勾勒及文件被。

 

二.makeSocket函数

 

int makeSocket(string host,int port)
{
    WSADATA inet_WsaData;//1
    WSAStartup(MAKEWORD(2, 0), &inet_WsaData);//1
    if (LOBYTE(inet_WsaData.wVersion) != 2 || HIBYTE(inet_WsaData.wVersion) != 0)//1
    {
        WSACleanup();
        return -1;
    }
    int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);//1
    struct hostent * hp = ::gethostbyname(host.c_str());//2
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    memcpy(&saddr.sin_addr, hp->h_addr, 4);//3
    if (connect(tcp_socket, (const struct sockaddr *)&saddr, sizeof(saddr)) == -1)//1
    {
        cerr << "error in connect" << endl;
    }
    return tcp_socket;
}

1 见http://www.cnblogs.com/magicsoar/p/3585129.html windows下的C++
socket服务器(3)中讲解

2 struct hostent * hp = ::gethostbyname(host.c_str());

gethostbyname()返回对应于让定主机名的盈盈主机名字跟地方信息之hostent结构指针

 

hostent结构体的定义如下

struct  hostent {
        char    FAR * h_name;           /* official name of host
*/
        char    FAR * FAR * h_aliases;  /* alias list */
        short   h_addrtype;             /* host address type */
        short   h_length;               /* length of address */
        char    FAR * FAR * h_addr_list; /* list of addresses */
#define h_addr  h_addr_list[0]          /* address, for backward
compat */
};

hostent->h_name意味着的是主机的专业名。例如www.baidu.com的科班名其实是www.a.shifen.com。(关于www.a.shifen.com还有同截故事http://www.zhihu.com/question/20100901)
hostent->h_aliases代表的凡主机的别名.www.google.com就是google他协调的别名。有的时候,有的主机可能产生少数只号,这些,其实还是以爱用户记忆而也自己之网站多取的名。
hostent->h_addrtype意味着的是主机ip地址的门类,到底是ipv4(AF_INET),还是pv6(AF_INET6)
hostent->h_length代表的是主机ip地址的长度

hostent->h_addr_list意味着的是主机的ip地址

#define h_addr h_addr_list[0]

 

3 memcpy(&saddr.sin_addr, hp->h_addr, 4);

由于 hp->h_addr是char*路,不克一直赋值给saddr.sin_addr

用我们以了memcpy函数

函数原型如下

void *memcpy(void *dest, const void *src, size_t n);

从今源src所据的内存地址的序曲位置上马拷贝n个字节到目标dest所依靠的内存地址的序幕位置被。

 

程序的下载地址

http://files.cnblogs.com/magicsoar/Webcrawler1.rar