快捷搜索:  汽车  科技

linux安装tiny proxy(搭建web服务器tinyweb)

linux安装tiny proxy(搭建web服务器tinyweb)#include <sys/stat.h>#include <netdb.h>#include <unistd.h>#include <cstddef>#include <sys/socket.h>

服务器端

linux安装tiny proxy(搭建web服务器tinyweb)(1)

客户端

linux安装tiny proxy(搭建web服务器tinyweb)(2)

linux安装tiny proxy(搭建web服务器tinyweb)(3)

代码设置的默认路径为桌面,hello.txt文件位于桌面,选择端口的端口号为8000(命令行第二个参数),127.0.0.1代表回送地址,指本地机,需要先行确认可用(本人通过Apache打开)。

#include <iostream>

#include <unistd.h>

#include <cstddef>

#include <sys/socket.h>

#include <netdb.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/mman.h>

#include <string>

//保证robust读写

ssize_t rio_writen(int fd void *userbuf size_t n)

{

size_t nleft = n;

ssize_t nwrite;

char *bufp = (char*)userbuf;

while(nleft > 0)

{

if((nwrite = write(fd bufp nleft))<0)

{

if(errno == EINTR)

nwrite = 0;

else

return -1;

}

nleft -= nwrite;

bufp = nwrite;

}

return n;

}

//带缓存版本

static const int RIO_BUFSIZE = 8192;

struct rio_t

{

int rio_fd; //文件描述符

int rio_cnt; //未读字节数

char *rio_bufptr; //下一个未读字节

char rio_buf[RIO_BUFSIZE];//内部缓冲

};

void rio_readinitb(rio_t *rp int fd)

{

rp->rio_fd = fd;

rp->rio_cnt = 0;

rp->rio_bufptr = rp->rio_buf;

}

ssize_t rio_read(rio_t *rp void *userbuf size_t n)

{

while(rp->rio_cnt <= 0)

{

rp->rio_cnt = read(rp->rio_fd rp->rio_buf sizeof(rp->rio_buf));

if(rp->rio_cnt < 0)

{

if(errno != EINTR)

return -1;

}

else if (rp->rio_cnt == 0)

return 0;

else

rp->rio_bufptr = rp->rio_buf;

}

size_t cnt = n > rp->rio_cnt ? rp->rio_cnt : n;

memcpy(userbuf rp->rio_bufptr cnt);

rp->rio_bufptr = cnt;

rp->rio_cnt -= cnt;

return cnt;

}

//读文本行,复制到内存位置userbuf,最后在结尾添NULL(0)

ssize_t rio_readlineb(rio_t *rp void *userbuf size_t maxlen)

{

ssize_t readn;

char c;

char *buf = (char*)userbuf;

int i;

for(i = 1; i < maxlen; i)

{

readn = rio_read(rp &c 1);

if(readn < 0)

return -1;

else if (readn == 0)

{

if(i == 1)

return 0;

else

break;

}

else

{

*buf = c;

if(c == '\n')

{

i;

break;

}

}

}

*buf = 0;

return i-1;

}

//打开服务器端监听描述符

int open_listenfd(const char *port)

{

addrinfo hints *listp *p;

int listenfd optval = 1;

memset(&hints 0 sizeof(addrinfo));

hints.ai_socktype = SOCK_STREAM;

hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;

hints.ai_flags |= AI_NUMERICSERV;

getaddrinfo(NULL port &hints &listp);

for(p = listp; p; p = p->ai_next)

{

if((listenfd = socket(p->ai_family p->ai_socktype p->ai_protocol)) < 0)

continue;

setsockopt(listenfd SOL_SOCKET SO_REUSEADDR &optval sizeof(int));

if(bind(listenfd p->ai_addr p->ai_addrlen) == 0)

break;

close(listenfd);

}

freeaddrinfo(listp);

if(!p)

return -1;

if(listen(listenfd 3) < 0)

{

close(listenfd);

return -1;

}

return listenfd;

}

void doit(int fd); //http事务

void read_requesthdrs(rio_t *rp); //读请求报头并忽略

int parse_uri(char *uri char *filename char *cgiargs); //解析uri

void serve_static(int fd char *filename int filesize); //访问静态资源

void get_filetype(char *filename char *filetype); //几种支持的文件类型

void serve_dynamic(int fd char *filename char *cgiargs); //访问动态资源

void clienterror(int fd char *cause int errnum std::string shortmsg std::string longmsg); //错误检查

int MAXLINE = 100;

int main(int argc const char * argv[]) {

// insert code here...

int listenfd connfd;

char hostname[MAXLINE] port[MAXLINE];

socklen_t clientlen;

sockaddr_storage clientaddr;

if(argc!=2) //两个命令行参数,可执行文件名和端口号

{

fprintf(stderr "usage: %s <port>\n" argv[0]);

exit(1);

}

listenfd = open_listenfd(argv[1]); //打开监听描述符,argv[1]为端口号,这里选择8000

while(1)

{

clientlen = sizeof(clientaddr);

connfd = accept(listenfd (sockaddr*)&clientaddr &clientlen); //等待连接请求,创建已连接描述符

getnameinfo((sockaddr*)&clientaddr clientlen hostname MAXLINE port MAXLINE 0); //获得请求客户端的信息

//客户端主机名,端口号(端口号为临时分配的一个值)

printf("Accepted connection from (%s %s)\n" hostname port);

doit(connfd);

close(connfd);

printf("按任意键继续");

getchar();

}

}

void doit(int fd)

{

int is_static;

struct stat sbuf;

char buf[MAXLINE] method[MAXLINE] uri[MAXLINE] version[MAXLINE];

char filename[MAXLINE] cgiargs[MAXLINE];

rio_t rio;

rio_readinitb(&rio fd);

rio_readlineb(&rio buf MAXLINE);//读请求行

printf("Request headers:\n");

printf("%s\n" buf);

sscanf(buf "%s %s %s" method uri version);

//只支持GET方法

if(strcasecmp(method "GET"))

{

clienterror(fd method 501 "Not implemented" "Tiny does not implement this method");

return;

}

read_requesthdrs(&rio);//读请求报头

is_static = parse_uri(uri filename cgiargs);//解析URI,判断静态访问还是动态

//通过文件名获得文件信息,存于sbuf

if(stat(filename &sbuf) < 0)

{

clienterror(fd filename 404 "Not Found" "Tiny couldn't find the file");

return;

}

//静态访问

if(is_static)

{

if(!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode))

{

clienterror(fd filename 403 "Forbidden" "Tiny couldn't read the file");

return;

}

serve_static(fd filename sbuf.st_size);

}

else

{

if(!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode))

{

clienterror(fd filename 403 "Forbidden" "Tiny couldn't run the CGI program");

return;

}

//动态访问

serve_dynamic(fd filename cgiargs);

}

}

void clienterror(int fd char *cause int errnum std::string shortmsg std::string longmsg)

{

char buf[MAXLINE] body[MAXLINE];

sprintf(body "<html><title>Tiny ERROR</title>");

sprintf(body "%s<body bgcolor=""ffffff"">\r\n" body);

sprintf(body "%s%d: %s\r\n" body errnum shortmsg.c_str());

sprintf(body "%s<p>%s: %s\r\n" body longmsg.c_str() cause);

sprintf(body "%s<hr><em>The Tiny Web server</em>\r\n" body);

sprintf(buf "HTTP/1.0 %d %s\r\n" errnum shortmsg.c_str());

rio_writen(fd buf strlen(buf));

sprintf(buf "Content-type: text/html\r\n");

rio_writen(fd buf strlen(buf));

sprintf(buf "Content-length: %d\r\n\r\n" (int)strlen(body));

rio_writen(fd buf strlen(buf));

rio_writen(fd body strlen(body));

}

void read_requesthdrs(rio_t *rp)

{

char buf[MAXLINE];

rio_readlineb(rp buf MAXLINE);

//找到最后一行,为空行(\r\n)

while(strcmp(buf "\r\n"))

{

rio_readlineb(rp buf MAXLINE);

}

return;

}

int parse_uri(char *uri char *filename char *cgiargs)

{

char *ptr;

//认为动态资源存放于cgi-bin目录

if(!strstr(uri "cgi-bin"))

{

strcpy(cgiargs "");

strcpy(filename "/Users/wanghao/Desktop");//设置桌面为默认路径

strcat(filename uri);

if(uri[strlen(uri)-1] == '/')//路径最后为/时添加的默认路径

strcat(filename "home.html");

return 1;

}

else

{

ptr = index(uri '?');

if(ptr)

{

strcpy(cgiargs ptr 1);

*ptr = '\0';

}

else

{

strcpy(cgiargs "");

}

strcpy(filename "/Users/wanghao/Desktop");

strcat(filename uri);

return 0;

}

}

void get_filetype(char *filename char *filetype)

{

if(strstr(filename ".html"))

strcpy(filetype "text/html");

else if(strstr(filename ".gif"))

strcpy(filetype "image/gif");

else if(strstr(filename ".png"))

strcpy(filetype "image/png");

else if(strstr(filename ".jpg"))

strcpy(filetype "image/jpg");

else

strcpy(filetype "text/plain");

}

void serve_static(int fd char *filename int filesize)

{

char *srcp filetype[MAXLINE] buf[MAXLINE];

get_filetype(filename filetype);

sprintf(buf "HTTP/1.0 200 OK\r\n");

sprintf(buf "%sServer: Tiny Web Server\r\n" buf);

sprintf(buf "%sConnection: close\r\n" buf);

sprintf(buf "%sContent-length: %d\r\n" buf filesize);

sprintf(buf "%sContent-type: %s\r\n\r\n" buf filetype);

rio_writen(fd buf strlen(buf));

printf("Response headers:\n");

printf("%s" buf);

//只读方式打开文件,将文件映射到虚拟内存

int srcfd = open(filename O_RDONLY 0);

srcp = (char*)mmap(0 filesize PROT_READ MAP_PRIVATE srcfd 0);

close(srcfd);

rio_writen(fd srcp filesize);//输出到客户端

munmap(srcp filesize);//内存回收

}

void serve_dynamic(int fd char *filename char *cgiargs)

{

char buf[MAXLINE] *emptylist[] = {NULL};

char *envp[]={0 NULL};

sprintf(buf "HTTP/1.0 200 OK\r\n");

rio_writen(fd buf strlen(buf));

sprintf(buf "Server: Tiny Web Server\r\n");

rio_writen(fd buf strlen(buf));

//开子进程,在子进程执行动态访问

if(fork() == 0)

{

setenv("QUERY_STRING" cgiargs 1);//获得cgi参数

dup2(fd STDOUT_FILENO);//重定位

execve(filename emptylist envp);

}

wait(NULL);

}

猜您喜欢: