C++寫的socket網絡爬蟲,代碼會在最後一次講解中提供給大家,同時我也會在寫的同時不斷的對代碼進行完善與修改
我首先向大家講解如何將網頁中的內容,文本,圖片等下載到電腦中。
一、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中的所有/替換為_
FileName( search = pos = ((pos = dir.find(search, pos)) != ++
如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函數
makeSocket( host, WSAStartup(MAKEWORD(, ), &inet_WsaData); (LOBYTE(inet_WsaData.wVersion) != || HIBYTE(inet_WsaData.wVersion) != ) - tcp_socket = socket(AF_INET, SOCK_STREAM, ); hostent * hp = ::gethostbyname(host.c_str()); ==&saddr.sin_addr, hp->h_addr, (connect(tcp_socket, ( sockaddr *)&saddr, (saddr)) == -) << <<
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.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