http服务器端工作流程

2022-06-14 08:20:08 浏览数 (1)

代码语言:javascript复制
/********************************************************************************************
功能: 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 usagen");
    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 Errorn");
        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("Failedn");
				return -1; 
			}
			printf("OKn");
			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 requestn");
			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 listeningn");
            continue;
        }
        printf("Stream file = %s size=%lldn", filename, filesize);

		/* Build HTTP response */
		strncpy(response, 
			"HTTP/1.1 200 OKrn"
			"Content-Length: %lldrn"
			/*"Content-Type: video/mpegrn"*/
			"Connection: Keep-Alivern"
			"Accept-Ranges: bytesrn"
			"Connection: closern"
			"Content-Range: bytes 0-%lld/%lldrn"
			"Server: Linux/2.6.18, UPnP/1.0, my test apprn"
			"rn",
            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 bytesn", 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] = '';
	url_root[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 3n", 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] == '')
		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 %dn",
			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=%dn",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 openedn");
	}

	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 lu bytes, instead wrote %d bytesn", 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 lu Bytes in dt = .2fusec at rate=%2.1f/%2.1fn", 
					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 = .2fusec at rate=%2.1f/%2.1fn", 
		//	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# 

0 人点赞