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

📄 multithreadproxy.cpp

📁 这是一个运行良好的类unix环境下的支持http 1.1 的网络文件下载器
💻 CPP
字号:
#include <unpthread.h>
#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/sendfile.h>

#define _USEPTHREAD

#define  BACKLOG   10
#define  HTTP_GET  "GET"
#define  CRLF      "\r\n"

#define HEAD_200  "HTTP/1.0 200 OK\r\n"
#define HEAD_404  "HTTP/1.0 404 Not Found\r\n"
#define BODY_404    "<HTML><head><title>404 Not Found</title></head><body><h1>HTTP 404 Error: File Not Found</h1></body></html>"
#define HEAD_500    "HTTP/1.0 500 Server Error\r\n"
#define HEAD_COMMON     "Server: Lin Yin's homework2\r\nConnection: Close\r\n"

#define CNST_FREE 0
#define CNST_RECEIVING 1
#define CNST_OPENING 2
#define CNST_SENDINGHEADER 3
#define CNST_SENDINGBODY 4

#define SEND_SLICE 4096

static char* root = NULL;
int sockfd;

typedef struct
{
	int conn_state;
	int conn_fd;
	int file_fd;
	int error;
	off_t fsize;
	off_t offset;
	const char* ctype;
	char* path;
	char buffer[1024];
}connection;

inline int get_empty_slot();
inline void close_connection(int num);
inline int handle_connect();
inline void handle_open(int num);
inline int handle_receive(int num);
inline int handle_sendheader(int num);
inline int handle_sendbody(int num);

static void
check( void* ptr )
{
    if ( ptr == (void*) 0 )
	{
		(void) fprintf( stderr, "fatal error: out of memory\n" );
		exit( 1 );
	}
}

static void*
malloc_check( size_t size )
{
    void* ptr = malloc( size );
    check( ptr );
    return ptr;
}


int
create_listen_socket(int port)
{
	int sockfd;
	struct sockaddr_in s_addr;
	const int on=1;

	/* create socket */
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
		perror("socket");
		exit(-1);
	}

	/* set address reuse */
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
		perror("setsockopt");
	}

	memset(&s_addr, 0, sizeof(s_addr));
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(port);
	s_addr.sin_addr.s_addr=htonl(INADDR_ANY);

	/* bind to specfic port */
	if(bind(sockfd, (struct sockaddr*)&s_addr, sizeof(struct sockaddr)) == -1){
		printf("Bind Error, %d\n",errno);
		close(sockfd);
		exit(-1);
	}

	/* listening for the client */
	if (listen(sockfd,BACKLOG)) {
		perror("listen");
		exit(-1);
	}

	return sockfd;
}

/* TODO: more content types are needed */
const char *
get_file_type(char *file)
{
	char *p = strrchr(file, '.');

	if (!p)
		return "application/binary";

	p++;
	if (strcasecmp(p, "html") == 0)
		return "text/html";
	else if (strcasecmp(p, "htm") == 0)
		return "text/html";
	else if (strcasecmp(p, "pdf") == 0)
		return "application/pdf";
	else
		return "application/binary";
}

/*
 * parse HTTP header
 * @return file path on local server, NULL if error happens
 *         Caller free() return pointer.
 */
char *
parse_HTTP_header(char *buf, int len)
{
	char *command, *req_file, *path;
	const char* token=" ";
	int n;

	if (len < 0)
		return NULL;

	/* check request command is GET */
	command = strtok(buf, token);
	if (strcmp(command, HTTP_GET)) {
		printf("not GET command\n");
		return NULL;
	}

	/* get reqest path */
	req_file = strtok(NULL, token);
#ifdef DEBUG
	printf("req_file = %s\n", req_file);
#endif
	n = strlen(root) + strlen(req_file)+1;
	path = malloc(n+2);
	bzero(path,n+2);
	sprintf(path,"%s/%s", root, req_file);
	return path;
}

static connection* connections;
int nthr, curthr;

inline int get_empty_slot()
{
	int i;
	for (i=0; i<nthr; i++)
		if (connections[i].conn_state==CNST_FREE) return i;
	return -1;
}

inline void close_connection(int num)
{
	close(connections[num].conn_fd);
	connections[num].conn_state=CNST_FREE;
	curthr--;
}

inline int handle_connect()
{
	/* accept */
#ifdef DEBUG
	printf("handling connect\n");
#endif
	size_t sin_size = sizeof(struct sockaddr_in);	
	struct sockaddr_in cl_addr;
	int num=get_empty_slot();
	if (num==-1) return 0;	
	int new_fd = accept(sockfd, (struct sockaddr *)&cl_addr, &sin_size);
	if (new_fd==-1) {printf("accept failed!\n"); return 0;}	
#ifdef DEBUG
	printf("Connection from %s\n", inet_ntoa(cl_addr.sin_addr));
#endif
	fcntl( new_fd, F_SETFL, fcntl(new_fd, F_GETFL)|O_NONBLOCK);
	curthr++;	
	connections[num].conn_state=CNST_RECEIVING;
	connections[num].conn_fd=new_fd;
	//handle_receive(num);
#ifdef DEBUG
	printf("connect succeed!\n");
#endif
	return 1;
}

inline int handle_receive(int num)
{	
#ifdef DEBUG
	printf("handle receive\n");
#endif
	char* path;
	int new_fd=connections[num].conn_fd;
	int n = recv(new_fd, connections[num].buffer, 1024, 0);	
	if (n<=0)
	{
		if (errno!=EAGAIN) close_connection(num);
		return 0;
	}
	if ( !(path = parse_HTTP_header(connections[num].buffer, n)) ) {
		/* send server error page */
		sprintf(connections[num].buffer, "%s", HEAD_500 HEAD_COMMON);
		connections[num].error=1;
		connections[num].conn_state=CNST_SENDINGHEADER;
		handle_sendheader(num);
		return 0;
	}
	connections[num].path=path;
	connections[num].conn_state=CNST_OPENING;
	handle_open(num);
#ifdef DEBUG
	printf("receive succeed!\n");
#endif
	return 1;
}

inline void handle_open(int num)
{
#ifdef DEBUG
	printf("handling open\n");
#endif
	char* path=connections[num].path;
	int file_fd = open(path, O_RDONLY|O_NONBLOCK);
	if(file_fd < 0)
	{
		if (errno!=EAGAIN)
		{
			sprintf(connections[num].buffer, "%s", HEAD_404  HEAD_COMMON  CRLF  BODY_404);
			connections[num].error=1;
			connections[num].conn_state=CNST_SENDINGHEADER;
			free(path);				
		}

	}
	else
	{
		const char *ctype = get_file_type(path);
		free(path);

        /* get the info of the file */
		struct stat stat;
		if (fstat(file_fd, &stat))   /* function fstat() used when the file has been oepn */
		{
			perror("fstat");
			close(file_fd);
			sprintf(connections[num].buffer, "%s", HEAD_500);
			connections[num].error=1;
			connections[num].conn_state=CNST_SENDINGHEADER;
		}
		else
		{
			connections[num].fsize=stat.st_size;
			connections[num].file_fd=file_fd;	
			/* create and send HTTP response header */
			sprintf(connections[num].buffer, "%s%sContent-Length: %lu\r\n"
				"Content-type: %s\r\n\r\n"
				,
				HEAD_200, HEAD_COMMON,
				stat.st_size,
				ctype);	
			connections[num].error=0;
			connections[num].conn_state=CNST_SENDINGHEADER;		
			
		}			
	}
	handle_sendheader(num);	
#ifdef DEBUG
	printf("open succeed!\n");
#endif
}

inline int handle_sendheader(int num)
{
#ifdef DEBUG
	printf("handling send header\n");
#endif
	int new_fd=connections[num].conn_fd;	
	int error=connections[num].error;
	
	/* send header */
	int n = send(new_fd, connections[num].buffer, strlen(connections[num].buffer), 0);
	
	if (n<=0)
	{
		if(errno==EAGAIN) return 0;
		else error=1;
	}
	if (error)
	{
		close_connection(num);
	}
	else
	{
		connections[num].conn_state=CNST_SENDINGBODY;
		connections[num].offset=0;
		handle_sendbody(num);
	}
#ifdef DEBUG
	printf("send header succeed!\n");
#endif
	return 1;
}


inline int handle_sendbody(int num)
{
#ifdef DEBUG
	printf("handling send body\n");
#endif
	int new_fd=connections[num].conn_fd;
	int file_fd=connections[num].file_fd;
	off_t size=connections[num].fsize;
	off_t* offset=&(connections[num].offset);	
	/* send the requested file */
	int n = sendfile(new_fd, file_fd, offset, SEND_SLICE);//size);
	if (n==-1&&errno==EAGAIN)
		return 0;	
	if (*offset>=size||n<=0)
	{		
		close(file_fd);
		close_connection(num);
	}
#ifdef DEBUG
	printf("send body succeed!\n");
#endif
	return 1;
}

void *
main_loop(void *arg)
{
	size_t sin_size = sizeof(struct sockaddr_in);
	struct sockaddr_in cl_addr;
	char buffer[1024];
	int  n, new_fd, file_fd;
	int sockfd = (int) arg;
	char* path;

	while (1) {
		// accept
		sin_size = sizeof(struct sockaddr_in);
		new_fd = accept(sockfd, (struct sockaddr *)&cl_addr, &sin_size);
#ifdef DEBUG
		printf("Connection from %s\n", inet_ntoa(cl_addr.sin_addr));
#endif
		n = recv(new_fd, buffer, 1024, 0);

		if ( !(path = parse_HTTP_header(buffer, n)) ) {
			// internal error
			send(new_fd, HEAD_500, strlen(HEAD_500), 0);
			send(new_fd, HEAD_COMMON, strlen(HEAD_COMMON), 0);
			close(new_fd);
			continue;
		}

		//open file
		file_fd = open(path, O_RDONLY);
		const char *ctype = get_file_type(path);
		free(path);
		
		if(file_fd < 0){
			//file not found
			perror("open");
			send(new_fd, HEAD_404  HEAD_COMMON  CRLF  BODY_404,
				strlen(HEAD_404) + strlen(HEAD_COMMON) + 2 + strlen(BODY_404),
				0);
		} else {
			char buf[1024];
			struct stat stat;

			if (fstat(file_fd, &stat)) {
				perror("fstat");
				send(new_fd, HEAD_500, strlen(HEAD_500), 0);
				close(new_fd);
				continue;
			}

			// send header
			sprintf(buf, "%s%sContent-Length: %lu\r\nContent-type: %s\r\n\r\n",HEAD_200, HEAD_COMMON,
					stat.st_size,ctype);
			send(new_fd, buf, strlen(buf), 0);

			// send body
			n = sendfile(new_fd, file_fd, NULL, stat.st_size);
#ifdef DEBUG
			if (n != stat.st_size) {
				printf("%d bytes sent, size: %lu bytes\n", n, stat.st_size);
			}
#endif
			close(file_fd);
		}
		close(new_fd);
	}

	return NULL;
}

int
main(int argc, char** argv)
{
	short port = 0;
	int i, sockfd, nthr = -1;
	pthread_t *threads;

	//parse arguments
	int c;
	while( (c = getopt(argc, argv, "r:l:t:")) != EOF) {
		switch(c) {		
		case 'l':
			port = atoi(optarg);
			break;
		case 'r':
			root = optarg;
			break;
		case 't':
			nthr = atoi(optarg);
			break;
		default:
			printf("Usage: %s -r root -l port -t threads\n", argv[0]);
			return -1;
		}
	}

	//set default value
	if(port == 0){
		printf("Invalid port number,  set port to 8000.\n");
		port = 8000;
	}

	if (root==NULL) {
		printf("Invalid root position, set root to ~jzhou/se320.\n");
		root = "~jzhou/se320";
	}

	if (nthr <= 0) {
		printf("Invalid number of threads: %d, set number to 30.\n", nthr);
		nthr = 30;
	}

	printf("port: %d, root: %s, threads: %d\n", port, root, nthr);

	// create listen socket
	sockfd = create_listen_socket(port);
	
	// create threads
	threads = (pthread_t *) malloc(nthr * sizeof(pthread_t));
	if (!threads) {
		printf("Failed to create %d thread\n", nthr);
		return -1;
	}	
	for (i = 0; i < nthr; i++) {
		if (pthread_create(threads+i, NULL, main_loop, (void*) sockfd) ) {
			printf("Error: pthread_create\n");
			exit(-1);
		}
	}

	// so goto sleep
	while (1) {
		sleep(1);
	}

	//never reached
	close(sockfd);

	return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -