完成了基本的下载支持,代码还比较简陋。
/*
* @brief simple http support, very ugly yet
* @author rui.sun, smallrui@126.com
* @date 2012-5-30
* @version 0.1 original version
* @copyright blog.csdn.net/maikforever
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <assert.h>
#ifdef WIN32
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable: 4996)
#else
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif
int get_host(char * src, char * web, char * file, int * port)
{
char * pa;
char * pb;
assert(src != NULL);
memset(web, 0, sizeof(web));
memset(file, 0, sizeof(file));
memset(port, 0, sizeof(port));
pa = src;
if (!strncmp(pa, "http://", strlen("http://")))
pa = src+strlen("http://");
else if (!strncmp(pa, "https://", strlen("https://")))
pa = src+strlen("https://");
pb = strchr(pa, '/');
if (pb)
{
memcpy(web, pa, strlen(pa) - strlen(pb));
if (pb+1)
{
memcpy(file, pb + 1, strlen(pb) - 1);
file[strlen(pb) - 1] = 0;
}
}
else
{
memcpy(web, pa, strlen(pa));
}
if (pb) web[strlen(pa) - strlen(pb)] = 0;
else web[strlen(pa)] = 0;
pa = strchr(web, ':');
if (pa) *port = atoi(pa + 1);
else *port = 80;
return web != NULL && file != NULL && port != NULL;
}
void set_socket_timeout(int sockfd, int time_sec)
{
#ifdef WIN32
int timeout = time_sec * 1000;
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int));
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int));
#else
struct timeval timeout = {time_sec, 0};
// timeout.tv_sec = time_sec;
// timeout.tv_usec = 0;
setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(struct timeval));
setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));
#endif
}
#define CHECK_ABORT(abort) \
if (abort) \
{ \
ret_code = -5; \
goto exit; \
}
/*
* return ret_code
* 0, no error
* -1, cannot connect to host server
* -2, create file error
* -3, network time out&error
* -4, syntax error
* -5, abort
*/
int chttp_get(const char * url, const char * localfile, const char * user_agent, int timeout_sec, int * abort, int * status_code, char * redicturl, int * dlbytes)
{
int sockfd = 0;
struct sockaddr_in server_addr;
struct hostent *host;
char * req_url = NULL;
char header_buffer[1024];
char host_addr[256];
char host_file[1024];
int portnumber;
FILE * fp = NULL;
int ret_code = 0;
int bytessend = 0;
int totalsend = 0;
int nb_tmpbytes = 0;
int nb_recvbytes = 0;
char recvbyte;
char tmpbytes[256];
int tmpret = 0;
char * loc_begin = NULL;
char * loc_tail = NULL;
char fio_buffer[4096];
#ifdef WIN32
static int wsainit = 0;
if (!wsainit)
{
WSADATA wsadata;
WSAStartup(MAKEWORD(1, 1), &wsadata);
wsainit = 1;
}
#endif
assert(url != NULL);
req_url = strdup(url);
if (!get_host(req_url, host_addr, host_file, &portnumber))
{
ret_code = -1;
goto exit;
}
CHECK_ABORT(*abort);
if ((host = gethostbyname(host_addr)) == NULL)
{
ret_code = -1;
goto exit;
}
CHECK_ABORT(*abort);
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
ret_code = -1;
goto exit;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((struct in_addr *)host->h_addr);
set_socket_timeout(sockfd, timeout_sec);
if (connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -1)
{
ret_code = -1;
goto exit;
}
sprintf (header_buffer,
"GET /%s HTTP/1.1\r\n"
"User-Agent: Android AppleCoreMedia Lavf\r\n"
"Host: %s\r\n"
"\r\n",
host_file, host_addr);
CHECK_ABORT(*abort);
// send request
bytessend = 0;totalsend = 0;
nb_tmpbytes=strlen(header_buffer);
while (totalsend < nb_tmpbytes)
{
bytessend = send(sockfd, header_buffer + totalsend, nb_tmpbytes - totalsend, 0);
if (bytessend==-1)
{
ret_code = -1;
goto exit;
}
totalsend += bytessend;
CHECK_ABORT(*abort);
}
nb_recvbytes = 0;
nb_tmpbytes = 0;
tmpret = 0;
memset(header_buffer, 0, sizeof(header_buffer));
CHECK_ABORT(*abort);
while((nb_tmpbytes = recv(sockfd, &recvbyte, sizeof(char), 0)) > 0)
{
if (nb_tmpbytes != sizeof(char)) break;
memcpy(header_buffer + nb_recvbytes, &recvbyte, sizeof(char));
if (strstr(header_buffer, "\r\n\r\n") != NULL)
{
tmpret = 1;
break;
}
CHECK_ABORT(*abort);
nb_recvbytes++;
}
if (!tmpret)
{
ret_code = -3;
goto exit;
}
memset(tmpbytes, 0, sizeof(tmpbytes));
sscanf(header_buffer, "HTTP/1.1 %[0-9] *", tmpbytes);
tmpret = atoi(tmpbytes);
*status_code = tmpret;
if (tmpret == 200)
{
// downloading task now
fp = fopen(localfile, "wb");
if (fp == NULL)
{
ret_code = -2;
goto exit;
}
*dlbytes = 0;
for (;;)
{
nb_tmpbytes = recv(sockfd, fio_buffer, sizeof(fio_buffer), 0);
if (nb_tmpbytes == 0)
{
// download is finished.
ret_code = 0;
break;
}
if (nb_tmpbytes == -1)
{
ret_code = -3;
goto exit;
}
fwrite(fio_buffer, 1, nb_tmpbytes, fp);
*dlbytes += nb_tmpbytes;
CHECK_ABORT(*abort);
printf("write bytes %d.\n", nb_tmpbytes);
}
}
else if (tmpret == 301 || tmpret == 302)
{
loc_begin = strstr(header_buffer, "Location: ");
if (loc_begin == NULL)
{
ret_code = -4;
goto exit;
}
loc_tail = strstr(loc_begin, "\r\n");
if (loc_tail == NULL)
{
ret_code = -4;
goto exit;
}
memcpy(redicturl, loc_begin + strlen("Location: "), loc_tail - loc_begin - strlen("Location: "));
redicturl[loc_tail - loc_begin - strlen("Location: ")] = 0;
}
else
{
ret_code = -3;
goto exit;
}
ret_code = 0;
exit:
free(req_url);
if (sockfd != 0)
{
#ifdef WIN32
closesocket(sockfd);
#else
close(sockfd);
#endif
}
if (fp != NULL)
{
fclose(fp);
}
return ret_code;
}
#ifdef TEST_CHTTP
int main(int argc, char * argv[])
{
const char * url = "http://61.135.183.46/ipad?file=/195/119/xWcw8lNpF7u4XnHPzrGC06.mp4&start=0&end=10";
const char * localfile = "d:/save_test.ts";
const char * user_agent = "android";
int timeout_sec = 2;
int status_code = 0;
char redicturl[256];
int dlbytes;
int ret_code = 0;
bool abort;
// will redirect to 301
ret_code = chttp_get(url, localfile, user_agent, timeout_sec, &abort, &status_code, redicturl, &dlbytes);
printf("ret_code = %d.\n", ret_code);
if (ret_code == 0 && status_code == 301)
{
chttp_get(redicturl, localfile, user_agent, timeout_sec, &abort, &status_code, redicturl, &dlbytes);
}
return 0;
}
#endif
/*
* @brief simple http support, very ugly yet
* @author rui.sun, smallrui@126.com
* @date 2012-5-30
* @version 0.1 original version
* @copyright blog.csdn.net/maikforever
*/
#ifndef CHTTP_H
#define CHTTP_H
/*
* return ret_code
* 0, no error
* -1, cannot connect to host server
* -2, create file error
* -3, network time out&error
* -4, syntax error
* -5, abort
*/
int chttp_get(const char * url, const char * localfile, const char * user_agent, int timeout_sec, int * abort, int * status_code, char * redicturl, int * dlbytes);
#endif