目录
配置web服务器
IIS配置web服务器
C++搭建简单的web服务器
编写web页面
程序测试
IIS配置测试
C++程序测试
Wireshark捕获交互过程及分析
三次握手
请求报文
请求行
请求头
请求体
响应报文
响应行
响应头
响应体
四次挥手
实验中遇到的问题及分析
参考资料
使用Windows自带的IIS配置web服务器,具体配置情况如下:
网站名称可以自己取,物理路径是我们编写的html文件所在的地方。
IP地址我设置成了本地,端口默认为80,也可以自由更改,注意不要和其他被使用的端口重复了!
我没有修改默认文档,因此我的html文件必须被命名为index.html,大家也可以自行修改。
在C++实现流式socket聊天程序的基础上,我们也可以采用流式socket的方式搭建一个简单的web服务器。主要的流程大体相同,但是发送和接收消息的部分改为接收客户端访问页面的请求头,并发送响应报文。
由于本实验的重点并非编程,我编写的程序中没有对客户的请求头进行分析,从而作出相应的处理,只是简单地接收请求头,并发送响应报文,响应体为我编写的html文件。
C++程序的主要代码如下(其他部分代码与实验1大体相同):
// 接收客户端的连接请求
while (true) {sockaddr_in addrClient;int len = sizeof(sockaddr_in);// 接收客户端的连接请求并创建新的套接字SOCKET sockConn = accept(sockServer, (SOCKADDR*)&addrClient, &len);if (sockConn != INVALID_SOCKET) {cout << "[ACCEPT CONNECTION REQUEST!]" << endl;cout << "---------------------------------------------------" << endl;char recvBuf[2652];memset(recvBuf, 0, sizeof(recvBuf));// 获取客户端的请求头int recvLen = recv(sockConn, recvBuf, 2652, 0);if (recvLen == -1) {cout << "Request error!" << endl;break;}else {cout << sizeof(recvBuf) << " bits received." << endl;cout << recvBuf << endl;cout << "---------------------------------------------------" << endl;// 响应报文char response[2652];memset(response, 0, sizeof(response));// 计算时间char tmp[32] = { NULL };time_t t = time(0);strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", localtime(&t));string str = "HTTP/1.1 200 OK\r\n""Date: ";str.append(tmp);str.append(" GMT\r\n");// 响应头,随便写了一点,并不完整,详细的分析看下文介绍str.append("Server: Roslin's web server(Windows)\r\n""Content-length: 1583\r\n""Content-Type: text/html; charset=UTF-8\r\n""Keep-Alive: timeout=10, max=100\r\n""Connection: Keep-Alive\r\n\r\n");// Conten-length一定要和消息体(html文件)的长度一致!!!否则无法正确显示// 打开html文件,参数r表示仅供读取FILE* file = fopen("WebPage.txt", "r");if (file == NULL) {cout << "Can not open the file!" << endl;return 0;}char html[1024] = "";do {memset(html, 0, sizeof(html));fgets(html, 1024, file);str.append(html);} while (!feof(file));// 发送响应报文,发送的消息长度必须和实际的一致,太长了会读入乱码!!!strcpy_s(response, str.c_str());send(sockConn, response, str.length(), 0);cout << str.length() << " bits response sent." << endl;cout << str << endl;cout << "---------------------------------------------------" << endl;}}// 关闭监听套接字closesocket(sockConn);
}
虽然我的响应头发送的连接方式是Keep-Alive,但是大家可以看到我发送完响应报文之后就关闭监听了套接字,相当于关闭了TCP连接,这是因为之后我们需要捕获TCP四次挥手的过程(其实是因为我一开始没想清楚)。
我编写了一个简单的html文件,用来显示我的个人主页。主要内容有姓名、学号、专业、学院、学校、个人logo(一只可爱的小考拉)。还加入了网易云音乐的外链接和背景图片,使界面更具有观赏性。具体的代码如下:
Roslin's Home Page
个人简介
Roslin
学号:xxx
专业:xxx
学院:xxx
学校:xxx
xxxxx
注意:
特别声明:本人没学过html,因此界面非常简单。
直接打开127.0.0.1(80端口为默认的,不用加),即可看到我编写的web页面,测试成功。
运行WebServer.cpp,打开浏览器,输入localhost:81(程序中指定的本地ip及端口号),可以看到我编写的web页面,测试成功。
懒得放图...
使用Wireshark捕获交互过程,可以看到TCP握手、浏览器的GET请求、我编写的响应报文和TCP挥手。
GET / HTTP/1.1
Host: localhost:81
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="106", "Microsoft Edge";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.47
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
POST和PUT方法包含请求体,本实验中是GET请求,不包含请求体。
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Last-Modified: Thu, 27 Oct 2022 02:34:23 GMT\r\n
Accept-Ranges: bytes\r\n
ETag: "b7e928a0ace9d81:0"\r\n
Server: Microsoft-IIS/10.0\r\n
Date: Thu, 27 Oct 2022 02:34:43 GMT\r\n
Content-Length: 1631\r\n
\r\n
[HTTP response 1/2]
[Time since request: 0.003017000 seconds]
[Request in frame: 5]
[Next request in frame: 29]
[Next response in frame: 31]
[Request URI: http://127.0.0.1/favicon.ico]
File Data: 1631 bytes
由于C++ socket编程的响应报文是我自己编写的,内容比较简单,不够完整,因此在这里分析使用IIS搭建的服务器发送的响应报文。
本实验中,响应体就是我编写的html文件,在次不再赘述。
IIS服务器捕获的四次挥手:
C++ socket编程捕获的四次挥手:
客户端和服务器都可以主动发起挥手动作。在IIS服务器捕获时四次挥手非常标准,我关闭了浏览器,由客户端先向服务器发送终止连接的请求。
但在C++ socket编程捕获的文件中, 只能看到三次挥手,但是观察Seq和Ack的值和连接的过程,一切正常。查阅相关资料后分析,这是因为我的服务器是通过socket编程实现的,发送完响应报文之后,调用close函数时就会向客户端发送终止连接的请求,这一步并不会在捕获文件中体现。之后客户端(64400)回复ACK报文确认收到了终止连接的请求也印证了这一点。
Sec-Fetch-*请求头,了解下? - 福禄网络研发团队 - 博客园
使用 WireShark 分析 TCP/IP 三次握手 和 四次挥手 - bylijian - 博客园