⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 httpsocket.h

📁 一个简单的http客户端
💻 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 + -