您的位置:首页 > 理论基础 > 计算机网络

【OpenSource】--TinyHttpD

2016-01-22 16:36 337 查看
【源码下载】http://sourceforge.net/projects/tinyhttpd/files/tinyhttpd%20source/tinyhttpd%200.1.0/tinyhttpd-0.1.0.tar.gz/download

【编译环境】ubuntu14.04+gcc

【运  行】工程利用了多线程,需要链接多线程的库libpthread.so;直接编译httpd.c源文件 $ gcc -o 程序名 源文件.c -lpthread 然后在浏览器输入: localhost:程序动态分配的端口号/index.html就可运行了。

请求头:

【GET】【http://localhost:59776/index.html】
Host: localhost:59776
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:39.0) Gecko/20100101 Firefox/39.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive


响应头:

Content-Type: text/html
Server: jdbhttpd/0.1.0


【注  意】源代码中提示将多线程注释掉,注释掉后直接编译不能运行。不注释直接编译虽然会又warnning但可运行。

服务器端原理:

服务器端调用socket()建立socket套接字,调用bind()函数绑定IP和端口,调用listen()函数监听端口,等待浏览器发送URL,调用accept()函数接受数据,关闭套接字。

accept()函数中创建多线程,主线程继续等待,子线程处理http请求,解析浏览器发送的URL字符串,结束后新线程退出。

main():

int main(void)
{
int server_sock = -1;
u_short port = 0;
int client_sock = -1;
struct sockaddr_in client_name;
int client_name_len = sizeof(client_name);
pthread_t newthread;

server_sock = startup(&port);    //传递port的地址到startup()函数
printf("httpd running on port %d\n", port);

while (1)
{
client_sock = accept(server_sock,
(struct sockaddr *)&client_name,
&client_name_len);
if (client_sock == -1)
error_die("accept");
/* accept_request(client_sock); */
if (pthread_create(&newthread , NULL, accept_request, client_sock) != 0)
perror("pthread_create");
}

close(server_sock);

return(0);
}


主函数调用startup()函数创建套接字。其中有一点是sockaddr和sockaddr_in的不同,总结是先创建sockaddr_in结构体,将该结构体中的各个变量赋值完后,在bind()或者accept()函数中将其转化为sockaddr结构体,因为两者所占内存空间一样可相互转化,另外两者所在头文件不一样,sockaddr在socket.h中,sockaddr_in在in.h中。

startup():

int startup(u_short *port)
{
int httpd = 0;
struct sockaddr_in name;    //声明结构体

httpd = socket(PF_INET, SOCK_STREAM, 0);    //创建套接字
if (httpd == -1)
error_die("socket");
memset(&name, 0, sizeof(name));    //初始化
name.sin_family = AF_INET;            //协议簇赋值
name.sin_port = htons(*port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)    //绑定IP和端口
error_die("bind");
if (*port == 0)  /* if dynamically allocating a port */
{
int namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);//动态分配端口
}
if (listen(httpd, 5) < 0)    //监听套接字
error_die("listen");
return(httpd);    //返回套接字
}


startup()函数返回套接字后创建多线程,多线程函数起始为accept_request()函数。

accept_request():

void accept_request(int client)
{
char buf[1024];
int numchars;
char method[255];
char url[255];
char path[512];
size_t i, j;
struct stat st;
int cgi = 0;      /* becomes true if server decides this is a CGI
* program */
char *query_string = NULL;

numchars = get_line(client, buf, sizeof(buf));
i = 0; j = 0;
while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
{
method[i] = buf[j];
i++; j++;
}
method[i] = '\0';
//判断请求方式
if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
{
unimplemented(client);
return;
}

if (strcasecmp(method, "POST") == 0)
cgi = 1;

i = 0;
while (ISspace(buf[j]) && (j < sizeof(buf)))
j++;
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
{
url[i] = buf[j];
i++; j++;
}
url[i] = '\0';
//GET方式
if (strcasecmp(method, "GET") == 0)
{
query_string = url;
while ((*query_string != '?') && (*query_string != '\0'))
query_string++;
if (*query_string == '?')
{
cgi = 1;
*query_string = '\0';
query_string++;
}
}
sprintf(path, "htdocs%s", url);//默认路径为工程下的htddocs文件夹下index.html
if (path[strlen(path) - 1] == '/')
strcat(path, "index.html");
if (stat(path, &st) == -1) {
while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */
numchars = get_line(client, buf, sizeof(buf));
not_found(client);
}
else
{
if ((st.st_mode & S_IFMT) == S_IFDIR)
strcat(path, "/index.html");
if ((st.st_mode & S_IXUSR) ||
(st.st_mode & S_IXGRP) ||
(st.st_mode & S_IXOTH)    )
cgi = 1;
if (!cgi)
serve_file(client, path);    //处理静态页面
else
execute_cgi(client, path, method, query_string);
}

close(client);
}


处理静态文件函数serv_file():

发送一个文件到客户端,调用headers()发送响应头。调用cat()函数将一个文件的内容输出到socket。

void serve_file(int client, const char *filename)
{
FILE *resource = NULL;
int numchars = 1;
char buf[1024];

buf[0] = 'A'; buf[1] = '\0';
while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */
//读取一行,将以\n或\r\n结束的字符串,转化为以\n\0字符结束
numchars = get_line(client, buf, sizeof(buf));

resource = fopen(filename, "r");
if (resource == NULL)
not_found(client);
else
{
//响应头
headers(client, filename);
cat(client, resource);
}
fclose(resource);
}
void headers(int client, const char *filename)
{
char buf[1024];
(void)filename;  /* could use filename to determine file type */

strcpy(buf, "HTTP/1.0 200 OK\r\n");
send(client, buf, strlen(buf), 0);
strcpy(buf, SERVER_STRING);
send(client, buf, strlen(buf), 0);
sprintf(buf, "Content-Type: text/html\r\n");
send(client, buf, strlen(buf), 0);
strcpy(buf, "\r\n");
send(client, buf, strlen(buf), 0);
}
void cat(int client, FILE *resource)
{
char buf[1024];

fgets(buf, sizeof(buf), resource);
while (!feof(resource))
{
send(client, buf, strlen(buf), 0);
fgets(buf, sizeof(buf), resource);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: