目录
3.2.1 利用Stringsplit函数将浏览器发送的请求头信息进行解析
3.2.2 利用getRfcTime函数将本地时间转换为浏览器需要的RFC时间格式
3.2.3 利用getHeader函数根据Stringsplit函数解析得到的请求信息生成成功响应头或失败响应头
3.2.4 利用getContent函数根据Stringsplit函数解析得到的请求路径生成相应的响应数据
前言
本实验为计算机网络课程设计内容,基本上所有代码都是根据指导书给的附录写出来的。有些实验需要实现图形界面,但是出于期末考试压力,我所有实验均是在控制台输入输出的,没有花额外时间去学习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;
}