目录
配置web服务器
IIS配置web服务器
C++搭建简单的web服务器
编写web页面
程序测试
IIS配置测试
C++程序测试
Wireshark捕获交互过程及分析
三次握手
请求报文
请求行
请求头
请求体
响应报文
响应行
响应头
响应体
四次挥手
实验中遇到的问题及分析
参考资料
配置web服务器
IIS配置web服务器
使用Windows自带的IIS配置web服务器,具体配置情况如下:
网站名称可以自己取,物理路径是我们编写的html文件所在的地方。
IP地址我设置成了本地,端口默认为80,也可以自由更改,注意不要和其他被使用的端口重复了!
我没有修改默认文档,因此我的html文件必须被命名为index.html,大家也可以自行修改。
C++搭建简单的web服务器
在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四次挥手的过程(其实是因为我一开始没想清楚)。
编写web页面
我编写了一个简单的html文件,用来显示我的个人主页。主要内容有姓名、学号、专业、学院、学校、个人logo(一只可爱的小考拉)。还加入了网易云音乐的外链接和背景图片,使界面更具有观赏性。具体的代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Roslin's Home Page</title>
<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">Roslin</h2>
<h3 class="ex1">学号:xxx</h3>
<h3 class="ex1">专业:xxx</h3>
<h3 class="ex1">学院:xxx</h3>
<h3 class="ex1">学校:xxx</h3>
<p class="ex1">xxxxx</p>
<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文件的根目录之下,否则要放完整的地址。但是插入本地图片用C++编程的方式无法正确显示,用IIS配置的方式可以。
- 用电脑浏览器打开网易云音乐,选择想要的歌曲/歌单,点击生成外链接,复制html代码即可。但是有些歌曲因版权问题无法生成外链接!
特别声明:本人没学过html,因此界面非常简单。
程序测试
IIS配置测试
直接打开127.0.0.1(80端口为默认的,不用加),即可看到我编写的web页面,测试成功。
C++程序测试
运行WebServer.cpp,打开浏览器,输入localhost:81(程序中指定的本地ip及端口号),可以看到我编写的web页面,测试成功。
懒得放图...
Wireshark捕获交互过程及分析
使用Wireshark捕获交互过程,可以看到TCP握手、浏览器的GET请求、我编写的响应报文和TCP挥手。
三次握手
- 第一次握手:客户端发送序列号Seq=0的SYN报文到服务器,表示请求建立连接。
- 第二次握手:服务器收到客户端的SYN报文,发送序列号Seq=0、确认号Ack=1的SYN, ACK报文到客户端,表示同意建立连接。
- 第三次握手:客户端收到服务器的SYN, ACK报文,发送序列号Seq=1、确认号Ack=1的ACK报文到服务器,表示确认建立连接。
请求报文
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
请求行
- 请求方法:GET, POST, HEAD, PUT, DELETE。
- 请求url:在本实验中,由于是同一台主机,于是省略了。
- http版本:在本实验中是HTTP/1.1。
请求头
- Host:主机。
- Connection:连接方式,keep-alive说明服务器在发送响应报文后不关闭TCP连接。
- Cache-Control:缓存控制,max-age表示客户端希望接收资源的存在时间的最大值,在本实验中,意思是客户端希望接收一个新的资源。
- sec-ch-ua:浏览器的版本。
- sec-ch-ua-mobile:浏览器是否在移动设备上打开。
- sec-ch-ua-platform:浏览器所在的操作系统。
- Upgrade-Insecure-Requests:请求服务端支持以下升级机制。
- User-Agent:客户端请求的相关信息,实验如浏览器、操作系统等。
- Accept:客户端希望接收的响应体的数据类型。
- Sec-Fetch-Site:表示一个请求发起者的来源与目标资源来源之间的关系。
- cross-site:跨域请求;
- same-origin:发起和目标站点源完全一致;
- same-site:实验如一级请求二级域名;
- none:如果用户直接触发页面导航,实验如在浏览器地址栏中输入地址,点击书签跳转等。
- Sec-Fetch-Mode:请求的模式。
- cors:跨域请求;
- no-cors:限制请求,只能使用请求方法(get/post/put)和请求头(accept/accept-language/content-language/content-type);
- same-origin:如果使用此模式向另外一个源发送请求,显而易见,结果会是一个错误。
- navigate:表示这是一个浏览器的页面切换请求,仅在浏览器切换页面时创建,该请求应该返回html;
- websocket:建立websocket连接。
- Sec-Fetch-User:表示导航请求激发的原因。
- true(?1):导航请求由用户激活触发(鼠标点击/键盘);
- false(?0):导航请求由用户激活以外的原因触发;
- Sec-Fetch-Dest:请求的目的,表示客户端希望获取什么样的资源。
- Accept-Encoding:接受的编码方式。
- Accept-Language:接受的语言。
请求体
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搭建的服务器发送的响应报文。
响应行
- http版本:在本实验中,是HTTP/1.1,必须和请求报文的协议版本匹配。
- 状态码和解释:表示请求的结果。
- 200 OK:请求成功;
- 301 Moved Permanently:请求的对象被移走,新的位置在响应中通过Location: 给出;
- 400 Bad Request:服务器不能解释请求报文;
- 404 Not Found:服务器中找不到请求的文档;
- 505 HTTP Version Not Supported:服务器不支持相应的HTTP版本。
响应头
- Date:发送响应报文的时间。
- Server:服务器的版本和平台。
- Last-Modified:最近一次修改的时间。
- ETag:被请求变量的实体值,是一个可以与Web资源关联的记号。
- Accept-Ranges:服务器支持的可用于定义范围的单位。
- Content-Length:响应体的长度。
- Keep-Alive:TCP通道可以保持的时间和最多接受的请求次数。
- Connection:连接方式,同上。
- Content-Type:响应体的内容类型和编码方式。
响应体
本实验中,响应体就是我编写的html文件,在次不再赘述。
四次挥手
IIS服务器捕获的四次挥手:
C++ socket编程捕获的四次挥手:
- A向B发送FIN, ACK报文,请求终止连接。
- B收到A发送的FIN, ACK报文后,回复ACK报文,表示确认收到了终止连接的请求。
- B向A发送FIN, ACK报文,请求终止连接。
- A收到B发送的FIN, ACK报文后,回复ACK报文,表示确认收到了终止连接的请求,双方正式终止连接。
客户端和服务器都可以主动发起挥手动作。在IIS服务器捕获时四次挥手非常标准,我关闭了浏览器,由客户端先向服务器发送终止连接的请求。
但在C++ socket编程捕获的文件中, 只能看到三次挥手,但是观察Seq和Ack的值和连接的过程,一切正常。查阅相关资料后分析,这是因为我的服务器是通过socket编程实现的,发送完响应报文之后,调用close函数时就会向客户端发送终止连接的请求,这一步并不会在捕获文件中体现。之后客户端(64400)回复ACK报文确认收到了终止连接的请求也印证了这一点。
实验中遇到的问题及分析
- 在使用C++搭建简单的web服务器时,由于对html的了解不够深刻,发送的响应报文不正确,一直无法正常打开网页。经过分析发现两个问题:第一,发现Content-length一定要和消息体(html文件)的长度一致,否则无法正确显示;第二,发送响应报文,发送的消息长度必须和实际的一致,太长了会读入乱码。虽然能够顺利完成实验,但是这个程序只是一个很简单的web服务器,没有对请求报文进行解析,响应报文也只有一种情况。希望后续能够在对http协议学习更深入的基础上编写一个完整的web服务器。
- 在编写web界面时,由于对于html语言了解不够深刻,出现了很多语法错误,也难以实现想要的效果。经过对html语言的初步学习后,能够实现预期的界面。但是这只是一个很简单的个人主页,希望以后能够改进。
- 在使用Wireshark捕获交互过程时,一开始对于软件不够熟悉,不清楚如何使用。后来可以正常捕获,但是捕获的内容很多,经过学习我发现可以使用过滤条件,就能快速定位到我们需要的交互过程。
- 在IIS服务器捕获的文件中,一开始我看到的响应报文总是304 Not Modified,但是依然能正常打开网页。后来发现是因为网页有缓存机制,如果第一次连接之后,html文件没有改变,就不会重新返回200 OK的响应报文。因此,我稍微修改了一下html文件,就成功捕获到了200 OK的响应报文。
- 在C++ socket编程捕获的文件中,我只观察到三次挥手,但是观察Seq和Ack的值和连接的过程,一切正常。查阅相关资料后分析,这是因为我的服务器是通过socket编程实现的,发送完响应报文之后,调用close函数时就会向客户端发送终止连接的请求,这一步并不会在捕获文件中体现。
参考资料
Sec-Fetch-*请求头,了解下? - 福禄网络研发团队 - 博客园文章来源:https://www.toymoban.com/news/detail-716008.html
使用 WireShark 分析 TCP/IP 三次握手 和 四次挥手 - bylijian - 博客园文章来源地址https://www.toymoban.com/news/detail-716008.html
到了这里,关于配置web服务器+编写简单页面+分析交互过程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!