📄 httpsocket.h
字号:
/*******************************************************
* The file contains some methods used to download *
* file from a http server. They are mainly the basic *
* operations for the implementation of Http protocol. *
* *
* @author chenfh *
* @version 1.0 *
* @time 2008-5 *
*******************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <grp.h>
#include <dirent.h>
#include <pwd.h>
#include <unistd.h>
#define MAX_HEADER_LENGTH 1024
#define MAX_PATH_LENGTH 64
#define MAX_PER_REQUEST 102400
/**
* The method is used to generate the request header
* for the http connection. It will contain the fields
* for command, object and so on.
*
* The parameters here, is something like:
* http://info.tsinghua.edu.cn/test/index.html
* So, the "host" should be "http://info.tsinghua.edu.cn",
* and the "file" should be "/test/index.html".
*
* @param host the url of the server host, in char form
* @param file the name of the file wanted to download, it is
* the full path of the file.
* @return the new formed request header
*/
char* GenHttpHeader(char * host, char * file){
char http_header[MAX_HEADER_LENGTH];
memset(http_header, '\0', MAX_HEADER_LENGTH);
//form the http request header
strcat(http_header, "GET ");
strcat(http_header, file);
strcat(http_header, " HTTP/1.1");
strcat(http_header, "\r\n");
strcat(http_header, "Host:");
strcat(http_header, host);
strcat(http_header, "\r\n");
strcat(http_header, "Accept:*/*\r\n");
strcat(http_header, "User-Agent:Mozilla/4.0 (compatible; MSIE 5.00; Ubuntu 7.10)\r\n");
strcat(http_header, "Connection:Keep-Alive\r\n");
strcat(http_header, "\r\n");
//return the new formed http header
return strdup(http_header);
}
/**
* The method is used to create the socket file descriptor, and
* then, it will establish a connection to the host.
*
* @param host the url of the host, for example "info.tsinghua.edu.cn"
* @param port the port to connect, form most occasion, it should be 80
* @return the new created socket file descriptor.
*/
int Open(const char * host, int port) {
struct in_addr ip_addr;
struct hostent * host_ent;
//check whether the host is an ip address
ip_addr.s_addr = inet_addr(host);
if(ip_addr.s_addr == -1) {
host_ent = gethostbyname(host);
if(host_ent == NULL) {
printf("Error when try to get the host by name\n");
return -1;
}
memcpy(&ip_addr, host_ent->h_addr_list[0], 4);
}
//get the protocol and create the socket
struct protoent *ppe = getprotobyname("tcp");
int sock_fd = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
if(sock_fd < 0) {
return -1;
}
//set the host address structure
struct sockaddr_in host_addr;
memset((void*)&host_addr, 0, sizeof(struct sockaddr_in));
host_addr.sin_family = AF_INET;
host_addr.sin_port = htons(port);
host_addr.sin_addr = ip_addr;
//begin to connect
int status = connect(sock_fd, (struct sockaddr*)&host_addr, sizeof(host_addr));
if(status != 0) {
return -1;
}
return sock_fd;
}
/**
* The method is used to send the request header using the
* specified socket file descriptor.
*
* @param request the header of the request message.
* @param sock_fd the file descriptor for the socket connection
* @return -1 if the operation fail, or the length of sended data otherwise.
*/
int SendRequest(const char * request, int sock_fd) {
long length = strlen(request);
return send(sock_fd, request, length, 0);
}
/**
* The method is used to set the time interval between the
* connection is established and timeout
*
* @param sock_fd the file descriptor of the connection
* @param time the time interval
* @param type the type of the connection
* @return -1 if the operation fail, and 0 otherwise
*/
int SetTimeout(int sock_fd, int time, int type) {
return setsockopt(sock_fd, SOL_SOCKET, type, (char*)&time, sizeof(int));
}
/**
* The method reads the response header text from the socket descriptor.
*
* @param sock_fd the socket connection descriptor
* @return the new getted response
*/
char* GetResponseHeader(int sock_fd){
char http_header[MAX_HEADER_LENGTH], c;
memset(http_header, '\0', MAX_HEADER_LENGTH);
int index = 0, header_end = 0;
while(header_end == 0) {
recv(sock_fd, &c, 1, 0);
http_header[index++] = c;
if(index >= 4) {
if(http_header[index - 4] == '\r' && http_header[index - 3] == '\n' &&
http_header[index - 2] == '\r' && http_header[index - 1] == '\n')
{
header_end = 1;
}
}
}
return strdup(http_header);
}
/**
* The method is used to get the specified field of the response's header
* It will used the keyword to retrieve the value of the field from the
* input header string.
*
* @param http_header the header of the response header
* @param field the name of the field that want to get
* @return the value of the field in the header or null otherwise
*/
char* GetField(const char * http_header, const char * field){
char retv[MAX_HEADER_LENGTH];
memset(retv, '\0', MAX_HEADER_LENGTH);
char* pos = strstr(http_header, field);
int index=0;
if(pos != NULL) {
pos += strlen(field);
pos += 2;
while(pos[index] != '\r') {
retv[index] = pos[index++];
}
return strdup(retv);
}
return NULL;
}
/**
* The method is used to get the state of the server
*
* @param http_header the response's header
* @return the state code of the host
*/
int GetHostState(const char * http_header){
char state[3];
state[0] = http_header[9];
state[1] = http_header[10];
state[2] = http_header[11];
return atoi(state);
}
/**
* The method is used to read data from the socket
* file descriptor.
*
* @param sock_fd the file descriptor of the socket
* connection
* @param buffer the buffer for the storage of the read data
* @param length the total length want to read from the socket
* in bytes.
* @return the number of bytes read
*/
int ReadData(int sock_fd, char * buffer, int length){
return recv(sock_fd, buffer, length, 0);
}
/**
* The method is used to close the connection
*
* @param sock_fd the file descriptor of the socket
* @return -1 if the operation fail or 0 otherwise
*/
int Close(int sock_fd){
return close(sock_fd);
}
//////////////////////////////////
//The following are some tools method
//for the manipulate of url
/**
* Get the file path from the url address
*
* @param url the url of the download task
* @return the fetched file name
*/
char * GetFilePath(const char * url) {
char * pos1 = strstr(url, "//");
char * pos2 = 0;
if(pos1 != 0) {
pos1 += 2;
pos2 = strstr(pos1, "/");
}
else {
pos2 = strstr(url, "/");
}
if(pos2 != 0) {
return pos2;
}
else {
return strdup("\0");
}
}
/**
* The method get the file name from the url
*
* @param url the url of the file to download
* @return the name of the file
*/
char * GetFileName(const char * url) {
if(url[strlen(url)-1] == '/') {
return strdup("\0");
}
char * pos1 = strstr(url, "//");
char * pos2 = 0;
if(pos1 != 0) {
pos1 += 2;
pos2 = strstr(pos1, "/");
}
else {
pos2 = strstr(url, "/");
}
if(pos2 == 0) {
return strdup("\0");
}
//fetch the name of the file
char * tmp_url = strdup(url);
char * delim = "/";
char * part = strtok(tmp_url, delim);
char * name = "\0";
while((part = strtok(NULL, delim))) {
name = part;
}
return strdup(name);
}
/**
* Get the host from the url
*
* @param url the url of the download task
* @return the fetched host address
*/
char * GetHost(const char * url) {
char host[MAX_HEADER_LENGTH];
memset(host, '\0', MAX_HEADER_LENGTH);
char * pos = strstr(url, "//");
int index = 0, length = 0;
if(pos != 0) {
pos += 2;
length = strlen(pos);
while(index < length && pos[index] != '/') {
host[index] = pos[index++];
}
}
else {
length = strlen(url);
while(index < length && url[index] != '/') {
host[index] = url[index++];
}
}
return strdup(host);
}
/**
* The method is used to get the port of the host.
* The port number is in the url, so the method has
* to analyse the url to get the port, if the port
* is not specified, then 80 will be returned by default.
*
* @param url the address that needed to get port
* @return the fetched port number or 80 by default
*/
int GetPort(const char * url) {
char * pos = strstr(url, ":");
char port[6] = {'\0'};
if(pos != 0) {
pos += 1;
if(pos[0] != '/') {
int index = 0, length = strlen(pos);
while(index < length && pos[index] != '/') {
port[index] = pos[index++];
}
return atoi(port);
}
}
return 80;
}
////////////////////////////////////////////
//some basic download methods using http protocol
/**
* The method perform the actual action of downloading.
* It will form the full path of local-storage and then
* it will read data from the socket.
*
* @param sock_fd the socket file descriptor
* @param length the length field's value of the response, which
* stands for the total length of the data, in bytes
* @param name the name for the local storage
* @param path the path for the local storage,and by default, it is null
* @return -1 if the operation fail, 0 otherwise.
*/
int PerformDownload(int sock_fd,
const char * length,
const char * name,
const char * path)
{
char buffer[MAX_PER_REQUEST + 1];
char store_path[MAX_PATH_LENGTH];
memset(store_path, '\0', MAX_PATH_LENGTH);
//form the exact full path of local storage
if(path != NULL) {
if(path[strlen(path)-1] == '/') {
strcat(store_path, path);
strcat(store_path, name);
}
else {
sprintf(store_path, "%s/%s", path, name);
}
}
else {
strcat(store_path, name);
}
//begin to download, it will create a new file for this download
//task
FILE * fp = fopen(store_path, "w");
if(fp == NULL) {
printf("[Log]:Unable to create new file for download task.\n");
return -1;
}
//begin to download
printf("[Log]: Receiving data...\n");
if(length != NULL) {
int size = atoi(length);
int finished = 0;
while(finished < size) {
int readed = ReadData(sock_fd, buffer, MAX_PER_REQUEST);
if(readed <= 0) {
break;
}
finished += readed;
fwrite(buffer, sizeof(char), readed, fp);
}
}
else {
while(1) {
int readed = ReadData(sock_fd, buffer, MAX_PER_REQUEST);
if(readed <= 0) {
break;
}
fwrite(buffer, sizeof(char), readed, fp);
}
}
printf("\n[Log]: End receive data.\n");
fflush(fp);
fclose(fp);
}
/**
* The method download the source specified by the
* url
*
* @param url the location of the resource on the internet
* @param loc_name the file name for the local-storage, so
* the user can rename the file from the internet
* @param loc_path the path for the local storage
* @return 0 if the operation is successful or -1 otherwise
*/
int Download(const char * url, const char * loc_name, const char * loc_path) {
printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
printf("+++Task Description...\n");
printf("Download: %s\n", url);
printf("Local Location: %s\n", loc_path);
printf("Local Name: %s\n", loc_name);
//fetch the information about the download task
printf("\nAnalyse the url...\n");
char * host = GetHost(url);
char * file = GetFilePath(url);
char * name = GetFileName(url);
char * path = 0;
int port = GetPort(url);
printf("Host: %s Port: %d\n", host, port);
printf("Object: %s\n", file);
printf("Name: %s\n", name);
//create the socket and open a connection between the client
//and the server, using the address and port
printf("\nBegin Download...\n");
printf("[Log]: Open connection.\n");
int sock_fd = Open(host, port);
if(sock_fd < 0) {
printf("[Log]: Error Occurs when open a connection\n");
printf("End Task\n");
return -1;
}
//send request to the server to start download
printf("[Log]: Generate http request header.\n");
char * request = GenHttpHeader(host, file);
printf("[Log]: Send request.\n");
int status = SendRequest(request, sock_fd);
//if the action is successfule, then go on with downloading
//if not, just quit
if(status >= 0) {
// analyse the response, to get the server state
// response code:
// 200-299, means client request successfully
// 300-399, means redirect, more actions are needed.
// 400-499, means action is incompleted.
// 500-599, server error
printf("[Log]: Get response header.\n");
char * response = GetResponseHeader(sock_fd);
printf("[Log]: Check server state.\n");
int state = GetHostState(response);
//response successful
if(state > 199 && state < 300) {
char * length = GetField(response, "Content-Length");
if(strlen(loc_name) != 0) {
name = strdup(loc_name);
}
if(strlen(loc_path) != 0) {
path = strdup(loc_path);
}
PerformDownload(sock_fd, length, name, path);
}
else if(state > 299 && state < 400) { //error occurs
printf("[Log]: Redirect, more actions are needed.\n");
printf("End Task.\n\n");
return -1;
}
else if(state > 399 && state < 500) {
printf("[Log]: Action is incompleted.\n");
printf("End Task.\n\n");
return -1;
}
else if(state > 99 && state < 200) {
printf("[Log]: Information received.\n");
printf("\n+++End Task.\n\n");
return -1;
}
else {
printf("[Log]: Server error.\n");
printf("End Task.\n\n");
return -1;
}
//close the socket connection
Close(sock_fd);
}
else {
printf("[Log]: Status: %d\n", status);
}
printf("End Task.\n\n");
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -