一、设计题目
8、简单Web Server程序的设计与实现
二、设计内容
实现一个简单的 Web Server,能够响应客户端的请求将指定目录下的 HTML 或 text 件通过指定的TCP 端发送给客户端。具体编程要求是:
(1)服务器启动时可指定服务端口,默认为 8000。
(2)可指定 Web Server 的根目录。
(3)服务器应能够并发处理多个请求。要求至少能支持 Get 命令。鼓励增强 Web Server 的功能,如支持 Head、Post 以及 Delete 等命令。
(4)统计 Web Server 接收和发送的流量。
(5)在 Windows 平台实现,要求使用图形界面显示服务器的各种信息。
(6)不允许使用已有的 HTTP 库。
(7)编写必要的客户端测试程序用于发送 HTTP 请求并显示返回结果,也可使用一般的 Web 浏览器测试。
三、设计步骤
3.1原理分析
HTTP 协议工作流程:
1.首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP 的工作就开始了。
2.建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是 MIME 信息:包括请求修饰符、客户机信息和可能的内容。
3.服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是 MIME 信息包括服务器信息、实体信息和可能的内容。
4.客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。
3.2编程设计
案例的设计步骤:
Web Server 可以分为两个组成模块:客户请求处理模块和响应生成发送模块。其中客户请求处理模块的任务就是负责接收客户的连接,它监听系统的端口,以获取客户机到达本服务器的连接,在程序中线程 ListenThread 实现该部分功能。当获得一个连接请求时,就为该连接建立一个客户请求处理线程来处理这个请求,请求处理线程就是响应生成发送模块负责分析请求中的各个协议参数,根据客户请求的分析结果查找资源,生成响应和发送响应,在程序中线程 ClientThread 实现该部分功能。
数据结构
#define BUFFER_SIZE 1024 //缓冲区大小
#define HOST "192.168.43.253" //IP地址
#define PORT 8100 //端口
#define HEADER "\ //构造请求头
HTTP/1.1 200 OK\r\n\
Content-Type: text/html; charset=UTF-8\r\n\
Server: ZYhttp_v1.0.1\r\n\
Content-Length: %ld\r\n\r\n\
"
关键代码
1)读取文本
ifstream fin(strPath.c_str(), ios::in | ios::binary);
if (fin.is_open())
{
memset(buffer, 0, BUFFER_SIZE);
//每次读取一个字节
while (fin.read(buffer, BUFFER_SIZE - 1))
{
if (send(socketClient, buffer, strlen(buffer), 0) < 0)
{
printf("发送数据失败!");
break;
}
memset(buffer, 0, BUFFER_SIZE);
}
if (send(socketClient, buffer, strlen(buffer), 0) < 0)
{
printf("发送数据失败!");
break;
}
}
2)获取文件长度
long GetFileLength(string strPath)
{
ifstream fin(strPath.c_str(), ios::in | ios::binary);
fin.seekg(0, ios_base::end);
streampos pos = fin.tellg();//返回当前文件位置
long lSize = static_cast<long>(pos);
fin.close();
cout<<"content:"<<lSize<<endl;
return lSize;
}
程序流程图
四、调试过程
浏览器地址栏进行数据输入,分析数据传输的形式,一开始没搞明白。后来加深了认识。
五、结果及分析
往浏览器输入指定的IP地址及端口号,按F12进入控制台,查看数据传输情况。
8,WebServer Result
8,WebServer IP地址
六、心得体会、
随着最后一个课设题目的完成,对计算机网络的认识愈发清晰。不再是此前那种只存在于书本上的知识点,而是实实在在的、解决千家万户的网络相关问题的方案的集合,与生活并不遥远。
#include <cstdio>
#include <string>
#include <fstream>
#include <WinSock2.h>
#include <iostream>
using namespace std;
#define BUFFER_SIZE 1024
#define HOST "192.168.43.253"
#define PORT 8100
#define HEADER "\
HTTP/1.1 200 OK\r\n\
Content-Type: text/html; charset=UTF-8\r\n\
Server: ZYhttp_v1.0.1\r\n\
Content-Length: %ld\r\n\r\n\
"
const string strPath = "Web Server\bin\Debug\1.html";
#pragma comment(lib, "WS2_32")
// 获取文件的大小
long GetFileLength(string strPath);
// 单线程函数
int main(int argc, char **argv)
{
// 定义并且初始化一个服务器套接字
sockaddr_in addrServer;
addrServer.sin_family = AF_INET;
addrServer.sin_addr.S_un.S_addr = INADDR_ANY;
addrServer.sin_port = htons(PORT);
// 初始化
WSADATA wsaData;
WORD socketVersion = MAKEWORD(2, 2);
if (WSAStartup(socketVersion, &wsaData) != 0)
{
printf("初始化失败!");
exit(1);
}
// 创建套接字
SOCKET socketServer = socket(AF_INET, SOCK_STREAM, 0);
if (socketServer == SOCKET_ERROR)
{
printf("创建失败!");
exit(1);
}
// 绑定服务器套接字
if (bind(socketServer, (LPSOCKADDR)&addrServer, sizeof(addrServer)) == SOCKET_ERROR)
{
printf("绑定失败!");
exit(1);
}
// 监听
if (listen(socketServer, 10) == SOCKET_ERROR)
{
printf("监听失败!");
exit(1);
}
while (true)
{
printf("Listening ... \n");
sockaddr_in addrClient;
int nClientAddrLen = sizeof(addrClient);
//服务器端建立连接
SOCKET socketClient = accept(socketServer, (sockaddr*)&addrClient, &nClientAddrLen);
if (SOCKET_ERROR == socketClient)
{
printf("接收失败!");
break;
}
char buffer[BUFFER_SIZE];
memset(buffer, 0, BUFFER_SIZE);
//接收数据
if (recv(socketClient, buffer, BUFFER_SIZE, 0) < 0)
{
printf("接收数据失败!");
break;
}
printf("接收到的数据 : \n%s", buffer);
// response
// send header
memset(buffer, 0, BUFFER_SIZE);
sprintf(buffer, HEADER, GetFileLength(strPath));//把文件和头文件合并然后发送数据
if (send(socketClient, buffer, strlen(buffer), 0) < 0)
{
printf("发送数据失败!");
break;
}
//读取文本
ifstream fin(strPath.c_str(), ios::in | ios::binary);
if (fin.is_open())
{
memset(buffer, 0, BUFFER_SIZE);
//每次读取一个字节
while (fin.read(buffer, BUFFER_SIZE - 1))
{
if (send(socketClient, buffer, strlen(buffer), 0) < 0)
{
printf("发送数据失败!");
break;
}
memset(buffer, 0, BUFFER_SIZE);
}
if (send(socketClient, buffer, strlen(buffer), 0) < 0)
{
printf("发送数据失败!");
break;
}
}
//printf("发送!");
//关于网络连接与数据传递
fin.close();
closesocket(socketClient);
}
closesocket(socketServer);
WSACleanup();
return 0;
}
long GetFileLength(string strPath)
{
ifstream fin(strPath.c_str(), ios::in | ios::binary);
fin.seekg(0, ios_base::end);
streampos pos = fin.tellg();//返回当前文件位置
long lSize = static_cast<long>(pos);
fin.close();
cout<<"content:"<<lSize<<endl;
return lSize;
}