计算机网络课程设计-简单 Web Server 程序的设计与实现

目录

 

前言

1 实验题目

2 实验目的

3 实验内容

3.1 步骤

3.2 关键代码

3.2.1 利用Stringsplit函数将浏览器发送的请求头信息进行解析

3.2.2 利用getRfcTime函数将本地时间转换为浏览器需要的RFC时间格式

3.2.3 利用getHeader函数根据Stringsplit函数解析得到的请求信息生成成功响应头或失败响应头

3.2.4 利用getContent函数根据Stringsplit函数解析得到的请求路径生成相应的响应数据

4 实验结果与分析

5 代码


前言

        本实验为计算机网络课程设计内容,基本上所有代码都是根据指导书给的附录写出来的。有些实验需要实现图形界面,但是出于期末考试压力,我所有实验均是在控制台输入输出的,没有花额外时间去学习qt了,有精力的同学可以自学一下qt实现简单的图形界面。同时,该博客内容为部分报告内容,仅为大家提供参考,请勿直接抄袭。另外,本次实验所用平台是dev c++5.11

1 实验题目

        实验九 简单 Web Server 程序的设计与实现

2 实验目的

        Web 服务是 Internet 最方便与受用户欢迎的服务类型,它的影响力也远远超出了专业技术范畴,已广泛应用于电子商务、远程教育、远程医疗与信息服务等领域,并且有继续扩大的趋势。目前很多的 Internet 应用都是基于 Web 技术的,因此掌握 Web 环境的软件编程技术对软件人员是至关重要的。
        编写简单的 Web Server 有助于读者了解 Web Server 的工作流程,掌握超文本传送协议( HTTP)基本原理,掌握 Windows 环境中用 socket 实现 C/S 结构程序的编程方法。

3 实验内容

3.1 步骤

        (1)创建套接字并进行初始化。

        (2)绑定套接字到本地端口,用于监听来自客户端的连接。

        (3)循环接受客户端连接,接收并解析HTTP请求。

        (4)根据请求生成HTTP响应,发送给客户端。

3.2 关键代码

3.2.1 利用Stringsplit函数将浏览器发送的请求头信息进行解析

void Stringsplit(string str, const char split,vector<string>& res) {
	/*
	GET / HTTP/1.1
	Host: 192.168.142.119:8000
	Connection: keep-alive
	Cache-Control: max-age=0
	Upgrade-Insecure-Requests: 1
	*/
	istringstream iss(str);	// 输入流
	string token;			// 接收缓冲区
	getline(iss, token,'\n');
	cout<<"token:"<<token<<"\n";
	istringstream is(token);
	string s;
	while (getline(is, s, split)) {	// 以split为分隔符
		res.push_back(s);
	}
}

3.2.2 利用getRfcTime函数将本地时间转换为浏览器需要的RFC时间格式

char * getRfcTime() {
	time_t timep;
	struct tm *p;
	static char timeBuf[30];
	time(&timep); //获取从1970至今过了多少秒,存入time_t类型的timep
	p = localtime(&timep);//用localtime将秒数转化为struct tm结构体
	sprintf(timeBuf,"%s, %d %s %d %02d:%02d:%02d GMT\r\n",
	        wday[p->tm_wday],p->tm_mday,month[p->tm_mon],1900 + p->tm_year,p->tm_hour, p->tm_min, p->tm_sec);
	return timeBuf;
}

3.2.3 利用getHeader函数根据Stringsplit函数解析得到的请求信息生成成功响应头或失败响应头

string getHeader(bool state) {
	/*
	HTTP/1.1 200 OK
	Date: Sat, 31 Dec 2005 23:59:59 GMT
	Content-Type: text/html;charset=ISO-8859-1
	Content-Length:
	*/
	string header = "";
	if(state) {
		header = header + "HTTP/1.1 200 OK" + "\r\n";
		header = header + "Date: ";
		//Sat, 31 Dec 2005 23:59:59 GMT
		char *timeP = getRfcTime();
		char timeBuf[30] = "\0";
		strcpy(timeBuf,timeP);
		header = header +  + timeBuf;
		header = header + "Content-Type: text/html;charset=UTF-8" + "\r\n";
		header = header + "Content-Length: ";
	} else if(state == false) {
		header = header + "HTTP/1.1 404 Not Found" + "\r\n";
		header = header + "Date: ";
		char *timeP = getRfcTime();
		char timeBuf[30] = "\0";
		strcpy(timeBuf,timeP);
		header = header +  + timeBuf;
		header = header + "Content-Type: text/html;charset=UTF-8" + "\r\n";
		header = header + "Content-Length: ";
	}
	return header;
}

3.2.4 利用getContent函数根据Stringsplit函数解析得到的请求路径生成相应的响应数据

string getContent(vector<string> res) {
	string content="";
	string header= "";
	int sendCode;
	if(res[1]=="/") {
		res[1]="index.html";
	} else {
		res[1]=res[1].substr(1,res[1].size());
	}
	ifstream infile;
	infile.open(res[1], ios::in);
	if (!infile.is_open()) {
		cout << "获取文件"<<res[1]<<"失败\n";
		header = getHeader(false);
		infile.open("error.html", ios::in);
	} else {
		cout << "读取文件"<<res[1]<<"成功\n";
		header = getHeader(true);
		//第二种读取方法
	}
	string buf;
	while (getline(infile,buf)) {
		content= content+buf;
	}
	infile.close();
	header = header + to_string(content.length())+"\r\n\r\n";
	const string data =header+content;
	return data;
}

4 实验结果与分析

(1)启动MyWebServer监听客户端请求。

图1.1 启动MyWebServer

(2)实验项目的同级和二级目录下的文件信息。

图1.2 项目的同级目录下的文件

图1.3 项目的二级目录下的文件

(3)打开浏览器,在请求路径栏只输入ip地址和端口号响应index.htm

图1.4 只输入ip地址和端口号

(4)请求其他的正确文件会响应相应的信息,例如请求hardwork.html、shiyan9.html、data.txt文件和/test/end.html文件。

图1.5 请求hardwork.html

图1.6 请求shiyan9.html文件中的信息

图1.7 请求data.txt文件

图1.8 请求/test/end.html文件

(5)请求不存在的目录文件显示404错误信息。

图1.9 404错误信息

5 代码

//#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<sstream>
#include<cstdio>
#include<string>
#include<cmath>
#include<ctime>

#include <sstream>
#include <fstream>

#include <Winsock2.h>
#pragma comment(lib,"ws2_32")

//#pragma comment ( lib  , "Protocol.lib" )

using namespace std;
typedef long long LL;
const int maxn = 10010;

const char *wday[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
const char *month[]   =  {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

string html =
    "<html>\n\
	<head>\n\
		<title>index.html</title>\n\
	</head>\n\
	<body>\n\
		<h1>This is index.html</h1>\n\
		<h2>你好</h2>\n\
	</body>\n\
</html>";

void Stringsplit(string str, const char split,vector<string>& res) {
	/*
	GET / HTTP/1.1
	Host: 192.168.142.119:8000
	Connection: keep-alive
	Cache-Control: max-age=0
	Upgrade-Insecure-Requests: 1
	*/
	istringstream iss(str);	// 输入流
	string token;			// 接收缓冲区
	getline(iss, token,'\n');
	cout<<"token:"<<token<<"\n";
	istringstream is(token);
	string s;
	while (getline(is, s, split)) {	// 以split为分隔符
		res.push_back(s);
//		cout<<"s:"<<s<<"\n";
	}
}

char * getRfcTime() {
	time_t timep;
	struct tm *p;
	static char timeBuf[30];
	time(&timep); //获取从1970至今过了多少秒,存入time_t类型的timep
	p = localtime(&timep);//用localtime将秒数转化为struct tm结构体
	sprintf(timeBuf,"%s, %d %s %d %02d:%02d:%02d GMT\r\n",
	        wday[p->tm_wday],p->tm_mday,month[p->tm_mon],1900 + p->tm_year,p->tm_hour, p->tm_min, p->tm_sec);
	return timeBuf;
}

string getHeader(bool state) {
	/*
	HTTP/1.1 200 OK
	Date: Sat, 31 Dec 2005 23:59:59 GMT
	Content-Type: text/html;charset=ISO-8859-1
	Content-Length:
	*/
	string header = "";
	if(state) {
		header = header + "HTTP/1.1 200 OK" + "\r\n";
		header = header + "Date: ";
		//Sat, 31 Dec 2005 23:59:59 GMT
		char *timeP = getRfcTime();
		char timeBuf[30] = "\0";
		strcpy(timeBuf,timeP);
		header = header +  + timeBuf;
		header = header + "Content-Type: text/html;charset=UTF-8" + "\r\n";
		header = header + "Content-Length: ";
	} else if(state == false) {
		header = header + "HTTP/1.1 404 Not Found" + "\r\n";
		header = header + "Date: ";
		char *timeP = getRfcTime();
		char timeBuf[30] = "\0";
		strcpy(timeBuf,timeP);
		header = header +  + timeBuf;
		header = header + "Content-Type: text/html;charset=UTF-8" + "\r\n";
		header = header + "Content-Length: ";
	}
	return header;
}

string getContent(vector<string> res) {
	string content="";
	string header= "";
	int sendCode;
	if(res[1]=="/") {
		res[1]="index.html";
	} else {
		res[1]=res[1].substr(1,res[1].size());
	}
	ifstream infile;
	infile.open(res[1], ios::in);
	if (!infile.is_open()) {
		cout << "获取文件"<<res[1]<<"失败\n";
		header = getHeader(false);
		infile.open("error.html", ios::in);
//		while (getline(infile,buf)) {
//			content= content+buf;
//		}
	} else {
		cout << "读取文件"<<res[1]<<"成功\n";
		header = getHeader(true);
		//第二种读取方法
//		string buf;
//		while (getline(infile,buf)) {
//			content= content+buf;
//		}
	}
	string buf;
	while (getline(infile,buf)) {
		content= content+buf;
	}
	infile.close();
	header = header + to_string(content.length())+"\r\n\r\n";
	const string data =header+content;
	return data;
}

int main() {

	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		cout<<"初始化套接字库失败"<<"\n";
		return 0;
	}
	cout<<"初始化套接字库成功"<<"\n";
	if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {
		WSACleanup();
		cout<<"确定版本失败"<<"\n";
		return 0;
	}
	cout<<"确定协议版本成功"<<"\n";
	SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
	if(sockSrv==-1) {
		closesocket(sockSrv);
		WSACleanup();
		cout<<"创建 MyWebServer 服务器失败"<<"\n";
		return 0;
	}
	cout<<"创建 MyWebServer 服务器成功"<<"\n";
	SOCKADDR_IN addrSrv;
	//htonl函数把主机字节转化成网络字节的函数;u_long htonl(u_long hostlong);
	addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(8000);

	//绑定
	err = bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	if(err==-1) {
		closesocket(sockSrv);
		WSACleanup();
		cout<<"MyWebServer 服务器绑定失败"<<"\n";
		return 0;
	}
	cout<<"MyWebServer 服务器绑定成功"<<"\n";

	err = listen(sockSrv,5);
	if(err==-1) {
		closesocket(sockSrv);
		WSACleanup();
		cout<<"监听失败"<<"\n";
		return 0;
	}
	cout<<"监听成功"<<"\n";

	char name[100];
	//获取主机名
	gethostname(name,sizeof(name));
	printf("主机名:%s\n",name);
	hostent *p;
	//获取IP地址
	p=gethostbyname(name);
	printf("MyWebServer Ip:%d.%d.%d.%d,Port:%d\n",(p->h_addr_list[0][0]&0x00ff),(p->h_addr_list[0][1]&0x00ff),
	       (p->h_addr_list[0][2]&0x00ff),(p->h_addr_list[0][3]&0x00ff),htons(addrSrv.sin_port));
	cout<<"-------------------------------------------\n";

	SOCKADDR_IN addrClient;
	int len=sizeof(SOCKADDR);

	int sendCode,recvCode;
	char recvBuf[2048];
	char sendBuf[2048];
	vector<string> v;
	SOCKET tempSockConn;
	string sendStr="";
	while(1) {
		cout<<"等待客户端连接\n";
		SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
		cout<<"\nsockConn:"<<sockConn<<"\n\n";
//		cout<<"\n1sockConn:"<<sockConn<<",tempSockConn:"<<tempSockConn<<"\n\n";
		if(sockConn==tempSockConn) {
//			closesocket(tempSockConn);
			closesocket(sockConn);
			shutdown(sockConn,2);
			sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
		}
//		cout<<"\n2sockConn:"<<sockConn<<",tempSockConn:"<<tempSockConn<<"\n\n";
		tempSockConn=sockConn;
//		cout<<"\n3sockConn:"<<sockConn<<",tempSockConn:"<<tempSockConn<<"\n\n";
//		cout<<"\nsockConn:"<<sockConn<<"\n\n";
		if(sockConn==-1) {
			cout<<"\nxxxxxx客户端连接失败xxxxxx\n\n";
			closesocket(sockConn);
			closesocket(sockSrv);
			WSACleanup();
		} else {
//			clientList.push_back(sockConn);
//			thread t(handleClient,sockConn);
//			t.detach();
			memset(recvBuf,'\0',sizeof(recvBuf));
			sprintf(recvBuf,"%s",inet_ntoa(addrClient.sin_addr));
			cout<<"客户端"<<recvBuf<<"连接成功\n";
			memset(recvBuf,'\0',sizeof(recvBuf));
			recvCode= recv(sockConn,recvBuf,sizeof(recvBuf),0);
			if(recvCode==-1) {
				cout<<"\nxxxxxx接收数据异常xxxxxx!\n\n";
				closesocket(sockConn);
//				closesocket(sockSrv);
				shutdown(sockConn,2);
				WSACleanup();
				break;
			}
			cout<<"recvCode:"<<recvCode<<",接收的数据:\n"<<recvBuf<<"\n";
			Stringsplit(recvBuf,' ',v);
			sendStr = getContent(v);
			v.clear();

//			memset(sendBuf,'\0',sizeof(sendBuf));
			sprintf(sendBuf,sendStr.c_str());
			strcpy(sendBuf,sendStr.c_str());
//			int i;
//			for(i=0; i<strlen(sendStr.c_str()); i++) {
//				sendBuf[i]=sendStr[i];
//			}
//			cout<<"sendStrLen:"<<sendStr.length()<<",sendBufLen:"<<i<<"\n\n";
//			cout<<"\nsendBuf:";
//			for(i=0; i<100; i++) {
//				cout<<sendBuf[i];
//			}
//			cout<<"\n\n";
//			cout<<"xxx"<<"\n";
//			cout<<"\nsendStr:"<<sendStr<<"\n\n";
//			cout<<"\nsendBuf:"<<sendBuf<<"\n\n";
			sendCode = send(sockConn,sendStr.c_str(),sendStr.length(),0);
			sendStr[sendStr.length()]='\0';
			printf("\nsendCode:%d,发送的数据:%s\n\n",sendCode,sendStr.c_str()); 
//			cout<<"sendCode:"<<sendCode<<",发送的数据:\n"<<sendStr.c_str()<<"\n";
			cout<<"\nxxx"<<"\n";
			sendStr.clear();
			closesocket(sockConn);
//			memset(Header,'\0',sizeof(Header));
//			memset(sendBuf,'\0',sizeof(sendBuf));
//			sprintf((char*)sendBuf,
//			        html.c_str()); // Last-Modified
//			char *timeP = getRfcTime();
//			char timeBuf[30] = "\0";
//			strcpy(timeBuf,timeP);
//			sprintf((char*)Header,
//			        "HTTP/1.0 %s\r\nDate: %sContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
//			        "200",
//			        timeBuf, // Date
//			        "text/html;charset=UTF-8", // Content-Type
//			        123, // Content-length
//			        sendBuf); // Last-Modified
//			sendCode = send(sockConn,Header,strlen(Header)+1,0);
//			cout<<"发送的请求头:"<<Header<<"\n";
//			//sendCode = send(sockConn,sendBuf,strlen(sendBuf)+1,0);
//			cout<<"发送的数据:"<<sendBuf<<"\n";
		}
//		closesocket(sockConn);
//		shutdown(sockConn,2);
	}

	WSACleanup();
	return 0;
}

  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MyWebServer v3.1.29更新日志: 增加实时流量查看,完善NT服务功能,增加静默启动(加/s命令行启动时不显示主窗口),优化一些网络参数 MyWebServer一个高性能、易用、小巧、绿色的轻量级WEB服务器软件,是你快速建站及个人HTTP文件服务器的难得工具。支持HTTP/1.1、断点续传、大文件下载、正则表达式URL重写、虚拟目录、HTTP反向代理等,可通过ISAPI接口、FastCGI接口实现执行服务器脚本(如PHP,asp,asp.net等),性能完全超越IIS等很多主流WEB服务器软件。   MyWebServer使用说明: 使用FastCGI时,在映射设置中将映射模块设置为启动FastCGI的命令,且命令行中必须包含IP:port格式(如:127.0.0.1:8988)的服务器信息,当不需要WEB服务器启动FastCGI时,命令行中填入IP:port格式的FastCGI服务器信息即可。 如果使用ISAPI接口,指定ISAPI的DLL文件即可。   注(本服务器不集成任何动态脚本支持,要使用请自行安装):asp支持可安装IASP(该软件要求安装java运行环境)通过isapi接口实现;PHP通过isapi和FastCGI接口均可(isapi方式建议使用PHP 5.2,因为5.3以上版不再提供ISAPI支持);asp.net支持可安装mono然后通过FasctCGI接口实现。上述脚本已测试过可以运行。   URL重写命令(使用基于VBScript的正则表达式): ifsve  如果匹配指定的服务器变量则往下执行,否则执行下条exitr之后的规则。(目前仅支持HTTP_HOST REMOTE_ADDR HTTP_REFERER三个服务器变量) ifurl  如果匹配请求的URL则执行wrurl重写命令,否则执行下条exitr之后的规则。 wrurl  执行URL重写 exitr  结束url重写,不再往下执行。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值