http服务端工作流程
/********************************************************************************************
功能: http服务器端测试程序
时间:2014-03-19
说明:网络服务器端程序一般是守护进程,这里只是测试调试,
没有做到守护。因此只支持单用户单次服务。
http服务器逻辑:
1.创建一个socket,bind一个socket,listen
2.客户端发来connect,服务器进行accept
3.客户端发来 ( send )请求get ,post 等,服务器读取请求
3.服务器端对请求进行分析:提取url;通过url搜索请求资源,如果
请求资源成功,则发出请求成功的响应
4.发出http响应(response)
5.客户端获得响应成功,就等待服务器发出数据并接收数据
*********************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sched.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/syscall.h>
#define DEFAULT_CONTENT_DIR "/home/hfl/hflsamb/network"
#define ALIGN_4096 (4096 - 1)
#define BUF_SIZE (188*7*128)
#define NUM_BUF_DESC 24
#define O_DIRECT 0x8000
#define FILENAME_MAX_LEN 256
char url[256]; /* content name to stream out */
char url_root[256]; /* Root directory where content is */
char recvbuf[1024];
char sendbuf[2048];
char response[2048];
off_t filesize;
void usage()
{
printf("Usage: http_test_server [-p <port> -a -r <rate> -c <cpu affinity> -m -f <content-directory> -l -k -h]\n");
printf("options are:\n");
printf(" -p <port> # port to listen on (default: 5000)\n");
printf(" -r <rate> # rate (default: 20Mpbs)\n");
printf(" -c <cpu affinity> # affinity (1 || 2 || 3, default: CPU0 = 1; CPU1 = 2; ANY = 3)\n");
printf(" -m # send test pattern from memory (default: stream file from disk)\n");
printf(" -f <content-directory # (default: /data/videos) \n");
printf(" -l # keep resending the file (default: stream only once)\n");
printf(" -v # print periodic stats (default: no)\n");
printf(" -k # disable TCP checksum computation (default: enabled)\n");
printf(" -h # prints http_test_server 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;
}
/* Open the listener service on port 5000 */
int tcpServerSetup(int port, int socket_type)
{
struct sockaddr_in localAddr;
int sd;
int reuse_flag = 1;
if ( (sd = socket(AF_INET, socket_type, 0)) < 0) {
/* Socket Create Error */
perror("Socket Open Err");
return -EINVAL;
}
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
localAddr.sin_port = htons(port);
if(setsockopt( sd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_flag, sizeof(reuse_flag) ) < 0 ) {
printf("REUSE Socket Error\n");
return -EINVAL;
}
if (bind(sd, (struct sockaddr *) &localAddr, sizeof(localAddr))) {
perror("Socket Bind Err");
return -EINVAL;
}
if (listen(sd, 4)) {
perror("Socket listen Err");
return -EINVAL;
}
return sd;
}
/* Parses the input for pattern begin; followed by pattern end */
int parseToken(char *input, char *output, int output_len, char *begin, char *end)
{
char *p_begin = strstr(input, begin);
char *p_end;
char temp;
if (p_begin)
{
p_begin += strlen(begin);
p_end = strstr(p_begin,end);
if(!p_end)
return -1;
temp = *p_end;
*p_end = 0;
printf("TokenFound = [%s]\n",p_begin);
strncpy(output,p_begin, output_len-1);
*p_end = temp;
return 0;
}
return -1; /* Not found */
}
int openUrlFile(char *url, char *filename)
{
struct stat file_stat;
char *p;
FILE *fp;
filename[0] = 0;
snprintf(filename, FILENAME_MAX_LEN, "%s/%s", url_root, url);
while(stat(filename, &file_stat)) {
if ( (p = strstr(filename,".mp3"))) {
printf("Original %s, ",filename);
strncpy(p,".mp3", FILENAME_MAX_LEN-6);
printf("Trying [%s] ",filename);
if(stat(filename, &file_stat)) {
printf("Failed\n");
return -1;
}
printf("OK\n");
break;
}
return 0;
}
/* File found */
filesize = file_stat.st_size;
return 0;
}
#ifdef USE_POLL
#include <sys/poll.h>
void waitForNetworkEvent(int sd)
{
struct pollfd pfd;
int rc;
pfd.fd = sd;
pfd.events = POLLIN | POLLPRI;
pfd.revents = 0;
while (1) {
if ( (rc = poll(&pfd, 1, -1)) < 0) {
perror("ERROR: select(): exiting...");
exit(1);
}
else if (rc == 0)
continue;
else if (pfd.revents == POLLIN)
break;
}
return;
}
#else /* Use select */
void waitForNetworkEvent(int sd)
{
fd_set rfds;
struct timeval tv;
while (1) {
FD_ZERO(&rfds);
FD_SET(sd, &rfds);
tv.tv_sec = 2;
tv.tv_usec = 0;
if ( select(sd +1, &rfds, NULL, NULL, &tv) < 0 ) {
perror("ERROR: select(): exiting...");
break;
}
if (!FD_ISSET(sd, &rfds))
/* No request from Client yet, go back to select loop */
continue;
break;
}
return;
}
#endif
/* waits for incoming HTTP requests, sends out HTTP response and returns new socket descriptor */
int handleHttpRequest(int sd, int socket_type, char *filename)
{
fd_set rfds;
struct timeval tv;
int nsd = -1;
struct sockaddr_in remoteAddr;
int addrLen = sizeof(remoteAddr);
int nbytes;
while (1) {
waitForNetworkEvent(sd);
/* accept connection */
if ( (nsd = accept(sd, (struct sockaddr *)&remoteAddr, (socklen_t *)&addrLen)) < 0 ) {
perror("ERROR: accept(): exiting...");
break;
}
printf("Accepted Connection from %lx:%d \n", remoteAddr.sin_addr.s_addr, ntohs(remoteAddr.sin_port));
waitForNetworkEvent(nsd);
/* Read HTTP request */
if ((nbytes = read(nsd, recvbuf, 1024)) <= 0) {
perror("read failed to read the HTTP Get request\n");
continue;
}
recvbuf[nbytes] = 0;
printf("Read HTTP Req ( %d bytes)[\n%s]\n", nbytes, recvbuf);
/* Parse HTTP request & open the content file */
parseToken(recvbuf, url, sizeof(url), "GET /", " ");
if( openUrlFile(url, filename) ) {
printf("File Not found, go back to listening\n");
continue;
}
printf("Stream file = %s size=%lld\n", filename, filesize);
/* Build HTTP response */
strncpy(response,
"HTTP/1.1 200 OK\r\n"
"Content-Length: %lld\r\n"
/*"Content-Type: video/mpeg\r\n"*/
"Connection: Keep-Alive\r\n"
"Accept-Ranges: bytes\r\n"
"Connection: close\r\n"
"Content-Range: bytes 0-%lld/%lld\r\n"
"Server: Linux/2.6.18, UPnP/1.0, my test app\r\n"
"\r\n",
sizeof(response)-1);
nbytes = snprintf(sendbuf, sizeof(sendbuf), response, filesize, filesize-1, filesize);
printf("HTTP Response [\n%s]", sendbuf);
/* send out HTTP response */
if (write(nsd, sendbuf, nbytes) != nbytes) {
printf("Failed to write HTTP Response of %d bytes\n", nbytes);
perror("write(): \n");
break;
}
return nsd;
}
return -1;
}
int main(int argc, char *argv[])
{
int port = 5000; /* Server Port */
unsigned long cpu_affinity_mask = 1; /* CPU0 = 1, CPU1 = 2; CPU0 || CPU1 = 3 */
unsigned long new_mask;
double rate = 19.4; /* Default rate to stream content at */
int loop = 0;
int send_from_memory = 0;
struct timeval start, stop;
int verbose = 0;
int disable_checksum = 0;
int sd; /* socket descriptor */
unsigned long count = 0;
unsigned long total = 0;
int buflen = BUF_SIZE;
struct sockaddr_in dest;
int c, i, j;
int fd = -1;
unsigned char *xbuf, *buf;
int len;
loff_t bytes= 0;
double dt=0, maxrate = 19.4;
struct iovec iov[NUM_BUF_DESC];
int nextBuf;
int socket_type;
char filename[FILENAME_MAX_LEN];
url[0] = '\0';
url_root[0] = '\0';
while ((c = getopt(argc, argv, "ap:c:f:r:lmkvh")) != -1)
{
switch (c)
{
\
case 'p':
port = atoi(optarg);
break;
case 'c':
cpu_affinity_mask = atoi(optarg);
if (cpu_affinity_mask == 0 || cpu_affinity_mask > 3) {
printf("Incorrect CPU Affinity %d, valid values are 1, 2, or 3\n", cpu_affinity_mask);
usage();
exit(-1);
}
break;
case 'f':
strncpy(url_root, optarg, sizeof(url_root)-1);
break;
case 'r':
maxrate = atof(optarg);
break;
case 'l':
loop = 1;
break;
case 'm':
send_from_memory = 1;
break;
case 'v':
verbose = 1;
break;
case 'k':
disable_checksum = 1;
break;
case 'h':
default:
usage();
return -1;
}
}
if (url_root[0] == '\0')
strncpy(url_root, DEFAULT_CONTENT_DIR, sizeof(url_root)-1);
printf("http_test_server: port %d, cpu affinity %s, content directory %s, rate %f, loop around %d, send_from_memory %d, verbose %d, disable TCP checksum %d\n",
port,
(cpu_affinity_mask == 1 ? "CPU0" : "CPU1"),
url_root, maxrate, loop, send_from_memory, verbose, disable_checksum);
/* allocate FIFO where data is read from disk & then sent out on the network */
for (i=0; i<NUM_BUF_DESC; i++) {
if ( (xbuf = malloc(BUF_SIZE + ALIGN_4096)) == NULL ) {
perror("posix_memalign failure():");
goto exit;
}
iov[i].iov_base = (unsigned char *)(((unsigned long)xbuf + ALIGN_4096) & ~ALIGN_4096);
iov[i].iov_len = BUF_SIZE;
}
printf("Allocated %d-bytes for buffering, # of DESC=%d\n",NUM_BUF_DESC*(BUF_SIZE + ALIGN_4096),NUM_BUF_DESC);
if (send_from_memory) {
for (i=0; i<NUM_BUF_DESC; i++)
memset(iov[i].iov_base, 0xff, BUF_SIZE);
}
/* setup HTTP Server */
socket_type = SOCK_STREAM;
if ( (sd = tcpServerSetup(port, socket_type)) < 0) {
printf("Failed to Setup TCP Server, Exiting...\n");
exit(-1);
}
/* wait for HTTP request & send HTTP reply */
sd = handleHttpRequest(sd, socket_type, filename);
if (sd<0) {
printf("Failed handling HttpRequest, Exiting...\n");
goto exit;
}
/* get file ready for streaming */
if (!send_from_memory) {
if ((fd = open(filename, O_RDONLY)) <= 0) {
perror("open():");
goto exit;
}
printf("Content File opened\n");
}
gettimeofday(&start, NULL);
nextBuf = 0;
while(1) {
/* make sure driver has finished sending all the queued up data */
/* get next buffer to read into */
buf = (unsigned char *)iov[nextBuf++].iov_base;
nextBuf %= NUM_BUF_DESC;
if (send_from_memory)
goto send_data;
/* read next data chunk */
if ( (buflen = read(fd, buf, BUF_SIZE)) < 0 ) {
perror("read():");
goto exit;
}
else if (buflen == 0) {
printf("**** Reached EOF *******\n");
gettimeofday(&stop, NULL);
if (loop) {
lseek(fd, 0, SEEK_SET);
continue;
}
break;
}
send_data:
/* send the data */
if ( (bytes = write(sd, buf, buflen)) != buflen) {
printf("Write failed to write %10lu bytes, instead wrote %d bytes\n", buflen, bytes);
perror("write():");
goto exit;
}
/* pacing logic */
count++;
total += bytes;
gettimeofday(&stop, NULL);
dt = difftime1(&start, &stop);
rate = 8.*total/dt;
while(rate > maxrate) {
usleep(10000);
gettimeofday(&stop, NULL);
dt = difftime1(&start,&stop);
rate = 8.*total/dt;
}
if(verbose && (count % 100) == 0) {
printf("[%6lu] Wrote %10lu Bytes in dt = %12.2fusec at rate=%2.1f/%2.1f\n",
count, total, dt, rate, maxrate);
}
}
exit:
// for (i=0; i<NUM_BUF_DESC; i++)
// free(iov[i].iov_base);
//printf("Final stats: Streamed %lu Bytes of %s file in dt = %10.2fusec at rate=%2.1f/%2.1f\n",
// total, filename, dt, rate, maxrate);
/* needed for all data to get drained from the socket */
sleep(2);
return 0;
}
服务器端:
root@ubuntu:/home/hfl/hflsamb/network# ./http_test_server -p 8000
http_test_server: port 8000, cpu affinity CPU0, content directory /home/hfl/hflsamb/network, rate 19.400000, loop around 0, send_from_memory 0, verbose 0, disable TCP checksum 0
Allocated 4141032-bytes for buffering, # of DESC=24
Accepted Connection from 801ca8c0:51541
Read HTTP Req ( 125 bytes)[
GET //love.mp3 HTTP/1.1
Host: 192.168.28.128:8000
Rate: 19
PlaySpeed.dlna.org: speed=1
User-Agent: STDSOCKET Test App
]
TokenFound = [/love.mp3]
Stream file = /home/hfl/hflsamb/network//love.mp3 size=577760125939925376
HTTP Response [
HTTP/1.1 200 OK
Content-Length: 2464551019266432
Connection: Keep-Alive
Accept-Ranges: bytes
Connection: close
Content-Range: bytes 0--5224570189344358016/1
Server: Linux/2.6.18, UPnP/1.0, my test app
]Content File opened
**** Reached EOF *******
客户端请求过程:
root@ubuntu:/home/hfl/hflsamb/network/http_client# ./http_test_client -d 192.168.28.128 -p 8000 -f /love.mp3
Server 192.168.28.128, Port 8000, File /love.mp3, Maxrate 19
Sending HTTP Request:----->[
GET //love.mp3 HTTP/1.1
Host: 192.168.28.128:8000
Rate: 19
PlaySpeed.dlna.org: speed=1
User-Agent: STDSOCKET Test App
]
TCP - Connection to 192.168.28.128:8000 ...
TCP: Connection Success
Succesfully Send HTTP Get Request to to 192.168.28.128:8000
Total response len 1024, payload len 814
HTTP Response: -----> [
HTTP/1.1 200 OK
Content-Length: 2464551019266432
Connection: Keep-Alive
Accept-Ranges: bytes
Connection: close
Content-Range: bytes 0--5224570189344358016/1
Server: Linux/2.6.18, UPnP/1.0, my test app
]
read failed: n = 0
read(): : Success
Final stats: Received 573824 bytes to love.mp3 file in 2013719.0usec at 2.3 rate
root@ubuntu:/home/hfl/hflsamb/network/http_client#