简单Web服务器程序设计与实现

题目描述:      

  1. 实现提供静态网页服务的web服务器

  2. 实现提供cgi动态网页服务的web服务器

  3. web服务器实现多进程服务

一、综合实践目的与要求

Linux程序设计综合实践是我们软件工程专业必须经历的过程,是理论与实践相结合的重要方式,使我们在实践中了解Linux操作系统、在实践中巩固知识。实习是个人综合能力的检验,除了有一定的课本知识外,还需有一定的实践动手能力,操作能力。实习是对我们软件工程专业知识的一种检验,它让我们学到了很多在课堂上根本就学不到的知识,技能开阔视野,又能增长见识,为我们走向工作打下坚实的基础,也是我们走向工作岗位的第一步。为了将所学的专业理论知识运用与实践,在实践中结合理论加深对其认识和总结。将专业与实际接轨,逐步认识,体会,从而更好地将所学的运用到工作中去。接触操作系统,认识linux,学会C语言,学会向老师提问,学会团结协作。通过深入编写程序,了解Linux和操作系统的现状,可加深理解并巩固所学专业知识,进一步提高认识问题、分析问题、解决问题的能力,使一个软件工程专业的学生应在linux综合实践与设计中用所学知识解决现实中的一些问题,对所学专业理论和实践知识进行巩固,同时提高自学、独立开发和协作能力,为走向工作岗位奠定良好的基础。

综合实践的要求是: 一、要求学生在实习过程中认真学习技术知识,积极与指导老师和同学配合;二、在前期,按时到勤,认真学习。积极做好实习日志,能够理解当天的内容。对技术的理论知识要及时实践;三、在后期,积极与同学沟通,认真完成项目要求的内容。在这个过程中要与老师同学多做沟通,通过探讨项目的`解决方案以及进展。

二、综合实践任务

       完成一个简单Web服务器程序设计与实现,要求学习网络套接字编程,HTTP协议、web服务器等知识;设计一个简单Web服务器,提供静态网页浏览服务功能;Web服务器可以配置的参数有主目录、首页问价名、HTTP端口号等项。

  1. 学习socket编程、http协议,实现提供静态网页的web服务器;
  2. 学习cgi知识,实现提供cgi动态网页服务的web服务器;
  3. Web服务器支持多进程服务。

三、总体设计

  1. 学习web服务器的基本原理。
  2. 学习socket网络编程。
  3. 学习http协议,实现提供静态网页的web服务器。
  4. 学习cgi知识,实现动态网页服务的web服务器。
  5. 运用多线程的知识,实现并发的web服务器。

四、详细设计说明

  • 了解web服务器的基本原理。Web服务器其实就是一类网络服务器,可以提供人们上网时向浏览器发出一些请求的一种程序。web服务器同时是一种比较被动的程序,只有你在上网的时候发出指令,这时服务器才会响应。

 

  • 所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。

 

  • 学习http协议实现,静态web服务器。超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。这个简单模型是早期Web成功的有功之臣,因为它使开发和部署非常地直截了当。

 

  • 学习cgi的相关知识,实现提供动态网页的web服务器。

 

程度实现:

hanshu.h

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

#define SERVER_PORT 80
static int debug = 1;

int get_line(int sock, char* buf, int size);
void* do_http_request(void* client_sock);
void do_http_response(int client_sock, const char* path);
int  headers(int client_sock, FILE* resource);
void cat(int client_sock, FILE* resource);
void not_found(int client_sock);//404 
void unimplemented(int client_sock);//500
void bad_request(int client_sock); //400
void inner_error(int client_sock);

http.c

#include "hanshu.h"

void* do_http_request(void* pclient_sock) {
	int len = 0;
	char buf[256];
	char method[64];
	char url[256];
	char path[256];
	int client_sock = *(int*)pclient_sock;
	struct stat  st;

	//读取客户端发送的http请求
	len = get_line(client_sock, buf, sizeof(buf));

	if (len > 0) {
		int i = 0, j = 0;
		while (!isspace(buf[j]) && (i < sizeof(method) - 1)) {
			method[i] = buf[j];
			i++;
			j++;
		}

		method[i] = '\0';
		if (debug) printf("request method: %s\n", method);

		if (strncasecmp(method, "GET", i) == 0) { //处理get请求
			if (debug) printf("method = GET\n");

			//获取url
			while (isspace(buf[j++]));
			i = 0;
			while (!isspace(buf[j]) && (i < sizeof(url) - 1)) {
				url[i] = buf[j];
				i++;
				j++;
			}
			url[i] = '\0';
			if (debug) printf("url: %s\n", url);
			do {
				len = get_line(client_sock, buf, sizeof(buf));
				if (debug) printf("read: %s\n", buf);

			} while (len > 0);
			//处理url中的?
			{
				char* pos = strchr(url, '?');
				if (pos) {
					*pos = '\0';
					printf("real url: %s\n", url);
				}
			}
			sprintf(path, "./html_docs/%s", url); // html_doc 网页目录
			if (debug) printf("path: %s\n", path);

			//判断文件是否存在,如果不存在,就响应 404 NOT FOUND.
			if (stat(path, &st) == -1) {//文件不存在或是出错
				fprintf(stderr, "stat %s failed. reason: %s\n", path, strerror(errno));
				not_found(client_sock);
			}
			else {//文件存在

				if (S_ISDIR(st.st_mode)) {
					strcat(path, "/index.html");// 如果是ip地址,则添加主页路径
				}
				do_http_response(client_sock, path);
			}
		}
		else {//非get请求响应客户端 501 	Method Not Implemented
			fprintf(stderr, "warning! other request [%s]\n", method);
			do {
				len = get_line(client_sock, buf, sizeof(buf));
				if (debug) printf("read: %s\n", buf);

			} while (len > 0);

			unimplemented(client_sock);   //请求未实现

		}
	}
	else {//请求格式有问题,出错处理
		bad_request(client_sock);   //在响应时再实现
	}
	close(client_sock);
	if (pclient_sock) free(pclient_sock);//释放动态分配的内存
	return NULL;
}

void do_http_response(int client_sock, const char* path) {
	int ret = 0;
	FILE* resource = NULL;

	resource = fopen(path, "r");

	if (resource == NULL) {
		not_found(client_sock);
		return;
	}

	//1.发送http 头部
	ret = headers(client_sock, resource);

	//2.发送http body .
	if (!ret) {
		cat(client_sock, resource);
	}
	fclose(resource);
}

realization.c

#include "hanshu.h"

// 响应文件的头部
int  headers(int client_sock, FILE* resource) {
	struct stat st;
	int fileid = 0;
	char tmp[64];
	char buf[1024] = { 0 };
	strcpy(buf, "HTTP/1.0 200 OK\r\n");
	strcat(buf, "Server: Martin Server\r\n");
	strcat(buf, "Content-Type: text/html\r\n");
	strcat(buf, "Connection: Close\r\n");

	fileid = fileno(resource);

	if (fstat(fileid, &st) == -1) {
		inner_error(client_sock);
		return -1;
	}

	snprintf(tmp, 64, "Content-Length: %ld\r\n\r\n", st.st_size);
	strcat(buf, tmp);
	if (debug) fprintf(stdout, "header: %s\n", buf);
	if (send(client_sock, buf, strlen(buf), 0) < 0) {
		fprintf(stderr, "send failed. data: %s, reason: %s\n", buf, strerror(errno));
		return -1;
	}
	return 0;
}

// 读取内容,发送给客户端
void cat(int client_sock, FILE* resource) {
	char buf[1024];

	fgets(buf, sizeof(buf), resource);

	while (!feof(resource)) {
		int len = write(client_sock, buf, strlen(buf));

		if (len < 0) {//发送body 的过程中出现问题,怎么办?1.重试? 2.
			fprintf(stderr, "send body error. reason: %s\n", strerror(errno));
			break;
		}

		if (debug) fprintf(stdout, "%s", buf);
		fgets(buf, sizeof(buf), resource);

	}
}


//返回值: -1 表示读取出错, 等于0表示读到一个空行, 大于0 表示成功读取一行
int get_line(int sock, char* buf, int size) {
	int count = 0;
	char ch = '\0';
	int len = 0;

	while ((count < size - 1) && ch != '\n') {
		len = read(sock, &ch, 1);

		if (len == 1) {
			if (ch == '\r') {
				continue;
			}
			else if (ch == '\n') {
				break;
			}
			//处理一般的字符
			buf[count] = ch;
			count++;
		}
		else if (len == -1) {//读取出错
			perror("read failed");
			count = -1;
			break;
		}
		else {// read 返回0,客户端关闭sock 连接.
			fprintf(stderr, "client close.\n");
			count = -1;
			break;
		}
	}
	if (count >= 0) buf[count] = '\0';
	return count;
}


void not_found(int client_sock) {
	const char* reply = "HTTP/1.0 404 NOT FOUND\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML lang=\"zh-CN\">\r\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\
<HEAD>\r\n\
<TITLE>NOT FOUND</TITLE>\r\n\
</HEAD>\r\n\
<BODY>\r\n\
	<P>文件不存在!\r\n\
    <P>The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n\
</BODY>\r\n\
</HTML>";

	int len = write(client_sock, reply, strlen(reply));
	if (debug) fprintf(stdout, reply);

	if (len <= 0) {
		fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
	}


}

void unimplemented(int client_sock) {
	const char* reply = "HTTP/1.0 501 Method Not Implemented\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML>\r\n\
<HEAD>\r\n\
<TITLE>Method Not Implemented</TITLE>\r\n\
</HEAD>\r\n\
<BODY>\r\n\
    <P>HTTP request method not supported.\r\n\
</BODY>\r\n\
</HTML>";

	int len = write(client_sock, reply, strlen(reply));
	if (debug) fprintf(stdout, reply);

	if (len <= 0) {
		fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
	}
}

void bad_request(int client_sock) {
	const char* reply = "HTTP/1.0 400 BAD REQUEST\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML>\r\n\
<HEAD>\r\n\
<TITLE>BAD REQUEST</TITLE>\r\n\
</HEAD>\r\n\
<BODY>\r\n\
    <P>Your browser sent a bad request!\r\n\
</BODY>\r\n\
</HTML>";

	int len = write(client_sock, reply, strlen(reply));
	if (len <= 0) {
		fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
	}
}


void inner_error(int client_sock) {
	const char* reply = "HTTP/1.0 500 Internal Sever Error\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML lang=\"zh-CN\">\r\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\
<HEAD>\r\n\
<TITLE>Inner Error</TITLE>\r\n\
</HEAD>\r\n\
<BODY>\r\n\
    <P>服务器内部出错.\r\n\
</BODY>\r\n\
</HTML>";

	int len = write(client_sock, reply, strlen(reply));
	if (debug) fprintf(stdout, reply);

	if (len <= 0) {
		fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
	}


}

Web_Server.c

#include "hanshu.h"

int main(void) {

	int sock;
	struct sockaddr_in server_addr;

	sock = socket(AF_INET, SOCK_STREAM, 0);
	bzero(&server_addr, sizeof(server_addr));

	server_addr.sin_family = AF_INET;//IPV4
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址
	server_addr.sin_port = htons(SERVER_PORT);//绑定端口号

	bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
	listen(sock, 128);
	printf("等待客户端的连接\n");

	int done = 1;
	while (done) {
		struct sockaddr_in client;
		int client_sock, len, i;
		char client_ip[64];
		char buf[256];
		pthread_t id;
		int* pclient_sock = NULL;

		socklen_t  client_addr_len;
		client_addr_len = sizeof(client);
		client_sock = accept(sock, (struct sockaddr*)&client, &client_addr_len);

		//打印客户端IP地址和端口号
		printf("client ip: %s\t port : %d\n",inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)),ntohs(client.sin_port));

		//启动线程处理http请求
		pclient_sock = (int*)malloc(sizeof(int));
		*pclient_sock = client_sock;

		pthread_create(&id, NULL, do_http_request, (void*)pclient_sock);
	}
	close(sock);
	return 0;
}

结果演示

 

 

 

 

  • 6
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值