http协议客户端下载流程
/**************************************************************************************
功能:http协议客户端网络下载测试
时间:2014-03-09
version : V1.0
说明:http协议客户端工作要点
1.创建socket bind本地socket,发起连接connetct ,完成tcp三次握手
2.向服务器发起http请求,即post或get动作,http协议请求格式见 http协议规范
3.读http响应,可以判定http 服务器和客户端是否通讯正常,如果失败, 可以根据错误代码判定不成功原因。
http 响应和http 错误代码见http协议部分
4.http响应会返回内容的长度和内容类型,这个信息在在在线业务中是个非常有用的信息,比如流媒体是边看边下载,如果要计算流媒体长度 只能从http响应读取
***************************************************************************************/
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#define SERVER_PORT 5000
#define BUF_MASK 0x1ff
#define BUF_SIZE (188*7*64)
#define O_DIRECT 0x8000
void usage()
{
printf("Usage: http_test_client: -d <srv-ip> -p <srv-port> -f <content-file> [-h -a -v]\n");
printf("options are:\n");
printf(" -d <srv-ip> # IP address of HTTP Server (e.g. 192.168.1.110)\n");
printf(" -p <srv-port> # Port of HTTP Server (e.g. 5000)\n");
printf(" -f <file> # url of stream; /data/videos prepended (e.g. portugal.mpg) \n");
printf(" -m # just leave content in memory, dont write to disk (default: write to file)\n");
printf(" -v # print periodic stats (default: no)\n");
printf(" -h # prints http_test_client usage\n");
printf("\n");
}
double difftime1(struct timeval *start, struct timeval *stop)
{
double dt = 1000000.*(stop->tv_sec - start->tv_sec) + (stop->tv_usec - start->tv_usec);
return dt;
}
/* This function creates a TCP connection to server and returns the socket descriptor */
int TcpConnect(char *server, int port, int socket_type)
{
int sd,rc;
struct sockaddr_in localAddr, servAddr;
struct hostent *h;
printf("TCP - Connection to %s:%d ...\n",server, port);
h = gethostbyname(server);
if (h == NULL) {
perror("Unknown Host");
return (-EINVAL);
}
servAddr.sin_family = h->h_addrtype;
memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0],h->h_length);
servAddr.sin_port = htons(port);
/* Open Socket */
sd = socket(AF_INET, socket_type, 0);
if (sd<0) {
perror("Socket Open Err");
return -EINVAL;
}
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
localAddr.sin_port = htons(0);
if (bind(sd,(struct sockaddr *) &localAddr, sizeof(localAddr))) {
perror("Socket Bind Err");
return -EINVAL;
}
/* Connect to server */
rc = connect(sd, (struct sockaddr *) &servAddr, sizeof(servAddr));
if (rc<0) {
perror("Connect Error: Server busy or unavailable:");
return -EBUSY;
}
printf("TCP: Connection Success\n");
return sd;
}
#define PKTS_PER_READ 64
#define MAX_BUFFER_SIZE (1316 * PKTS_PER_READ)
#define HTTP_POST_SIZE 2048
int main(int argc, char **argv)
{
char *post;
char *server = NULL;
char *fname = NULL;
int maxrate = 19;
int c;
int sd;
FILE *fp = NULL;
char *p;
char *rbuf;
int port = 0;
ssize_t n;
unsigned char *buf, *xbuf;
ssize_t bytes = 0;
unsigned long total=0;
unsigned long count=0;
char tok;
ssize_t len;
double dt;
struct timeval start, stop;
int accel_socket = 0; /* Use Standard Socket */
int dont_write_to_disk = 0;
int verbose = 0;
char cname ='/';
while ((c = getopt(argc, argv, "d:p:f:amvh")) != -1) {
switch (c) {
case 'd':
server = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 'f':
fname = optarg;
break;
case 'a':
accel_socket = 1;
break;
case 'm':
dont_write_to_disk = 1;
break;
case 'v':
verbose = 1;
break;
case 'h':
default:
usage();
return -1;
}
}
if (port == 0 || server == NULL || fname == NULL) {
printf("Missing Args...\n");
usage();
exit(1);
}
printf("Server %s, Port %d, File %s, Maxrate %d\n",
server, port, fname, maxrate);
xbuf = (unsigned char *) malloc(BUF_SIZE + BUF_MASK);
rbuf = (char *) malloc(1024);
post = (char *) malloc(HTTP_POST_SIZE);
if (xbuf == NULL || rbuf == NULL || post == NULL) {
perror("malloc failure\n");
exit(-1);
}
buf = (unsigned char *) (((unsigned long)xbuf + BUF_MASK) & ~BUF_MASK);
/* Build HTTP Get Request */
n = snprintf(post, HTTP_POST_SIZE,
"GET /%s HTTP/1.1\r\n"
"Host: %s:%d\r\n"
"Rate: %d\r\n"
"PlaySpeed.dlna.org: speed=1\r\n"
"User-Agent: %s Test App\r\n"
"\r\n",
fname, server, port, maxrate, "STDSOCKET" );
printf("Sending HTTP Request:----->[\n%s]\n", post);
/* open file for writing */
if (!dont_write_to_disk) {
p=strrchr(fname,'/');
fname=p+1;
fp = fopen(fname,"wb");
if (!fp) {
perror("File open error:\n");
exit(-1);
}
}
/*TODO: enable O_DIRECT and test, need to change BUF_SIZE*/
/*fcntl(fileno(fp),F_SETFL,O_DIRECT);*/
/* Connect to Server */
sd = TcpConnect(server, port, SOCK_STREAM);
if (sd < 0) {
printf("Connection to Server Failed, Exiting...\n");
exit(-1);
}
/* Send HTTP Get Request */
n = write(sd, post, strlen(post));
if ((unsigned)n != strlen(post)) {
printf("Failed to write HTTP Get Request: rc %d\n", n);
perror("write(): ");
exit(-1);
}
printf("Succesfully Send HTTP Get Request to to %s:%d\n", server, port);
usleep(10000);
/* Read HTTP Response */
memset(rbuf, 0, 1024);
if ( (n = read(sd, rbuf, 1024)) <= 0) {
printf("Failed to read HTTP Response: rc = %d\n", n);
perror("read(): ");
exit(-1);
}
rbuf[n] = '\0';
/* Scan for end of HTTP header */
p = strstr(rbuf,"\r\n\r\n");
if(!p) {
printf("No HTTP Header\n");
len = 0;
p = rbuf;
tok = 0;
}
else {
p+=4;
tok = *p;
*p = 0;
len = strlen(rbuf);
}
printf("Total response len %d, payload len %d\n", n, n-len);
printf("HTTP Response: -----> [\n%s]\n", rbuf);
*p = tok;
/* write any data that was read part of the initial read */
if (n>len) {
if ( (total = write(fileno(fp), p, n - len)) <= 0 ) {
printf("Failed to write initial payload bytes (%lu)\n", total);
perror("write():\n");
exit(-1);
}
}
/* increase the recv buffer before starting to read the content */
//sockSetRecvParams(sd, accel_socket);
sleep(1);
gettimeofday(&start, NULL);
/* read data from network & write to the file */
while(1) {
if ( (n = read(sd, buf, BUF_SIZE)) <= 0) {
gettimeofday(&stop, NULL);
printf("read failed: n = %d\n", n);
perror("read(): ");
break;
}
gettimeofday(&stop, NULL);
if (dont_write_to_disk) {
bytes = n;
goto after_write;
}
if (((bytes = write(fileno(fp), buf, n)) <= 0) || bytes != n) {
printf("Failed to write payload bytes (%d)\n", bytes);
perror("write():\n");
break;
}
after_write:
dt = difftime1(&start, &stop);
total += bytes;
count++;
if (verbose && (count % 100) == 0) {
printf("[%10lu] Received %10lu Bytes in dt = %10.2fusec at rate %2.1f\n",
count, total, dt, (total * 8. / dt));
}
}
dt = difftime1(&start, &stop);
printf("Final stats: Received %10lu bytes to %s file in %10.1fusec at %2.1f rate\n",
total, fname, dt, (total * 8. / dt));
fclose(fp);
free(xbuf);
free(rbuf);
free(post);
return 0;
}
/********************************************************************************
比如下载一个MP3文件过程如下:
root@ubuntu:/home/hfl/hflsamb/network# ./http_test_client -d 192.168.1.108 -p 80 -f /myserver/wy.mp3 -v
Server 192.168.1.108, Port 80, File /myserver/wy.mp3, Maxrate 19
Sending HTTP Request:----->[
GET //myserver/wy.mp3 HTTP/1.1
Host: 192.168.1.108:80
Rate: 19
PlaySpeed.dlna.org: speed=1
User-Agent: STDSOCKET Test App
]
TCP - Connection to 192.168.1.108:80 ...
TCP: Connection Success
Succesfully Send HTTP Get Request to to 192.168.1.108:80
Total response len 1024, payload len 773
HTTP Response: -----> [
HTTP/1.1 200 OK
Date: Sun, 09 Mar 2014 10:45:55 GMT
Server: Apache/2.2.22 (Win32)
Last-Modified: Tue, 19 Apr 2011 13:10:17 GMT
ETag: "560000000302d3-40275b-4a1453ac09c0f"
Accept-Ranges: bytes
Content-Length: 4204379
Content-Type: audio/mpeg
]
[ 100] Received 3329486 Bytes in dt = 1388794.00usec at rate 19.2
read failed: n = 0
read(): : Success
Final stats: Received 4204379 bytes to wy.mp3 file in 6998282.0usec at 4.8 rate
root@ubuntu:/home/hfl/hflsamb/network#
*********************************************************************************/