C++ socket网络爬虫(1)(二)
s::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所指的内存地址的起始位置中。