目录
使用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(一只可爱的小考拉)。还加入了网易云音乐的外链接和背景图片,使界面更具有观赏性。具体的代码如下:
- html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>Roslin's Home Pagetitle>
- <style>
- body
- {
- background-image: url(https://img.zmtc.com/2019/0815/20190815113422287.jpg);
- background-size: cover;
- }
- style>
- <style>
- div.transbox
- {
- width: 1050px;
- height: 420px;
- margin: 90px 90px;
- background-color: #ffffff;
- border: 1px solid #FFFFFF;
- opacity:0.8;
- }
- style>
- <style>
- h1 {text-align: center;}
- h2.ex1 {margin-left:5cm;}
- h3.ex1 {margin-left:5cm;}
- p{text-align: right;}
- p.ex1{margin-right: 2cm;}
- style>
- <style>
- img.ex1{margin-left:5cm;border-radius:100%;overflow:hidden;position: absolute;top: 50;left: 150;}
- style>
- head>
- <body>
- <div class="background">
- <div class="transbox">
- <h1>个人简介h1>
- <h2 class="ex1">Roslinh2>
- <h3 class="ex1">学号:xxxh3>
- <h3 class="ex1">专业:xxxh3>
- <h3 class="ex1">学院:xxxh3>
- <h3 class="ex1">学校:xxxh3>
- <p class="ex1">xxxxxp>
- <img src="https://tupian.qqw21.com/article/UploadPic/2015-5/201552323315986462.jpg" height="150px" width="150px" class="ex1" title="logo"/>
- div>
- div>
- <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=298 height=52 src="http://music.163.com/outchain/player?type=2&id=456370728&auto=1&height=32">iframe>
- body>
- html>
注意:
特别声明:本人没学过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 - 博客园