📄 multithreadproxy.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 + -