向指定的主机发送一个Web页面的请求。
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/times.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include "web_request.h"
//使用宏来组装一份Http数据包, 此处只需要填写 get的内容,主机名
#define HTTP_HEADER "GET /%s HTTP/1.1/r/n" /
"User-Agent: User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2/r/n" /
"Host: %s/r/n" /
"Accept: text/html/r/n" /
"Connection: Close/r/n/r/n"
/*
* err: 本文件函数从err转换为字串
*/
const char *get_error_string(int err)
{
if (err < 0) err = -err;
if (err >= ERR_LAST) err = 0;
return error_string[err];
}
/*
* 根据域名及端口号,建立TCP连接
*/
static int connect_to_host(const char *host, int port)
{
struct hostent *phe = NULL;
struct sockaddr_in sin;
int sock_fd;
bzero(&sin, sizeof(sin));
if((phe = gethostbyname(host)))
{
memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
}
else
{
return -ERR_RESOLVE;
}
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
return -ERR_SOCKET;
}
if(connect(sock_fd, (void *)&sin, sizeof(sin)) < 0)
{
return -ERR_CONNECT;
}
return sock_fd;
}
/*
* url: 请求url,支持格式如下:
* www.a.com
* http://www.a.com:PORT (PORT可选)
* http://www.a.com:PORT/b/c?d=e
* timeout: 请求过程的超时时间,以秒为单位
* limitsize: 读取字节限制
* buf: web数据缓存指针
*/
int send_request(const char *url, int timeout, int limitsize, char **ppweb)
{
char urldup[2048];
char *host = urldup;
char *get = "";
char *port = "80";
strncpy(urldup, url, 2048);
urldup[2047] = 0;
if(strncmp("http://", urldup, 7) == 0)
{
host = &urldup[7];
}
char *tmp = strchr(host, '/');
if(tmp)
{
*tmp = 0; //使host指向url的A区域。
get = tmp + 1; //get取到A区域后面的区域。。。
}
tmp = strchr(host, ':');
if(tmp)
{
*tmp = 0;
port = tmp + 1; //取端口号
}
//fprintf(stderr, "Send request: GET %s on %s, port %s/n", get, host, port);
int np = atoi(port); //把字符串的端口号转化成整型数
if(np <= 0 || np > 65535)
{
return -ERR_INVALIDPORT;
}
int fd = connect_to_host(host, np); //连接到host
if(fd < 0)
{
return fd;
}
int opt_tm = timeout * 1000;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&opt_tm, sizeof(int));
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&opt_tm, sizeof(int));
char header[4096];
int headlen = snprintf(header, 4096, HTTP_HEADER, get, host);
if(send(fd, header, headlen, 0) < 0)
{
close(fd);
return -ERR_SEND;
}
fd_set fdset;
struct timeval to = { timeout, 0 };
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
if((select(fd + 1, &fdset, NULL, NULL, &to))< 0)
{
close(fd);
return -ERR_SELECT;
}
if(FD_ISSET(fd, &fdset))
{
char *buf = malloc(limitsize + 1);
if(!buf)
{
close(fd);
return -ERR_MEMORY;
}
memset(buf, 0, limitsize + 1);
int len = recv(fd, buf, limitsize, MSG_WAITALL);
if(len <= 0)
{
free(buf);
buf = NULL;
close(fd);
return -ERR_READ;
}
buf[len] = 0;
*ppweb = buf;
}
close(fd);
return 0;
}
错误处理:
#ifndef _WEB_REQUEST_H_
#define _WEB_REQUEST_H_
enum errcode {
NOERR,
ERR_INTERNAL,
ERR_RESOLVE,
ERR_SOCKET,
ERR_CONNECT,
ERR_INVALIDPORT,
ERR_SEND,
ERR_SELECT,
ERR_MEMORY,
ERR_READ,
ERR_LAST,
};
static const char *error_string[ERR_LAST] = {
"No error",
"Internal error",
"Can not recolve",
"Socket error",
"Connect error",
"Invalid port",
"Send error",
"Select error",
"Out of memory",
"Read error",
};
int send_request(const char *url, int timeout, int limitsize, char **ppweb);
#endif