• 配置web服务器+编写简单页面+分析交互过程


    目录

    配置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大体相同):

    1. // 接收客户端的连接请求
    2. while (true) {
    3. sockaddr_in addrClient;
    4. int len = sizeof(sockaddr_in);
    5. // 接收客户端的连接请求并创建新的套接字
    6. SOCKET sockConn = accept(sockServer, (SOCKADDR*)&addrClient, &len);
    7. if (sockConn != INVALID_SOCKET) {
    8. cout << "[ACCEPT CONNECTION REQUEST!]" << endl;
    9. cout << "---------------------------------------------------" << endl;
    10. char recvBuf[2652];
    11. memset(recvBuf, 0, sizeof(recvBuf));
    12. // 获取客户端的请求头
    13. int recvLen = recv(sockConn, recvBuf, 2652, 0);
    14. if (recvLen == -1) {
    15. cout << "Request error!" << endl;
    16. break;
    17. }
    18. else {
    19. cout << sizeof(recvBuf) << " bits received." << endl;
    20. cout << recvBuf << endl;
    21. cout << "---------------------------------------------------" << endl;
    22. // 响应报文
    23. char response[2652];
    24. memset(response, 0, sizeof(response));
    25. // 计算时间
    26. char tmp[32] = { NULL };
    27. time_t t = time(0);
    28. strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", localtime(&t));
    29. string str = "HTTP/1.1 200 OK\r\n""Date: ";
    30. str.append(tmp);
    31. str.append(" GMT\r\n");
    32. // 响应头,随便写了一点,并不完整,详细的分析看下文介绍
    33. str.append("Server: Roslin's web server(Windows)\r\n"
    34. "Content-length: 1583\r\n"
    35. "Content-Type: text/html; charset=UTF-8\r\n"
    36. "Keep-Alive: timeout=10, max=100\r\n"
    37. "Connection: Keep-Alive\r\n\r\n");
    38. // Conten-length一定要和消息体(html文件)的长度一致!!!否则无法正确显示
    39. // 打开html文件,参数r表示仅供读取
    40. FILE* file = fopen("WebPage.txt", "r");
    41. if (file == NULL) {
    42. cout << "Can not open the file!" << endl;
    43. return 0;
    44. }
    45. char html[1024] = "";
    46. do {
    47. memset(html, 0, sizeof(html));
    48. fgets(html, 1024, file);
    49. str.append(html);
    50. } while (!feof(file));
    51. // 发送响应报文,发送的消息长度必须和实际的一致,太长了会读入乱码!!!
    52. strcpy_s(response, str.c_str());
    53. send(sockConn, response, str.length(), 0);
    54. cout << str.length() << " bits response sent." << endl;
    55. cout << str << endl;
    56. cout << "---------------------------------------------------" << endl;
    57. }
    58. }
    59. // 关闭监听套接字
    60. closesocket(sockConn);
    61. }

    虽然我的响应头发送的连接方式是Keep-Alive,但是大家可以看到我发送完响应报文之后就关闭监听了套接字,相当于关闭了TCP连接,这是因为之后我们需要捕获TCP四次挥手的过程(其实是因为我一开始没想清楚)

    编写web页面

    我编写了一个简单的html文件,用来显示我的个人主页。主要内容有姓名、学号、专业、学院、学校、个人logo(一只可爱的小考拉)。还加入了网易云音乐的外链接和背景图片,使界面更具有观赏性。具体的代码如下:

    1. html>
    2. <html>
    3. <head>
    4. <meta charset="utf-8">
    5. <title>Roslin's Home Pagetitle>
    6. <style>
    7. body
    8. {
    9. background-image: url(https://img.zmtc.com/2019/0815/20190815113422287.jpg);
    10. background-size: cover;
    11. }
    12. style>
    13. <style>
    14. div.transbox
    15. {
    16. width: 1050px;
    17. height: 420px;
    18. margin: 90px 90px;
    19. background-color: #ffffff;
    20. border: 1px solid #FFFFFF;
    21. opacity:0.8;
    22. }
    23. style>
    24. <style>
    25. h1 {text-align: center;}
    26. h2.ex1 {margin-left:5cm;}
    27. h3.ex1 {margin-left:5cm;}
    28. p{text-align: right;}
    29. p.ex1{margin-right: 2cm;}
    30. style>
    31. <style>
    32. img.ex1{margin-left:5cm;border-radius:100%;overflow:hidden;position: absolute;top: 50;left: 150;}
    33. style>
    34. head>
    35. <body>
    36. <div class="background">
    37. <div class="transbox">
    38. <h1>个人简介h1>
    39. <h2 class="ex1">Roslinh2>
    40. <h3 class="ex1">学号:xxxh3>
    41. <h3 class="ex1">专业:xxxh3>
    42. <h3 class="ex1">学院:xxxh3>
    43. <h3 class="ex1">学校:xxxh3>
    44. <p class="ex1">xxxxxp>
    45. <img src="https://tupian.qqw21.com/article/UploadPic/2015-5/201552323315986462.jpg" height="150px" width="150px" class="ex1" title="logo"/>
    46. div>
    47. div>
    48. <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>
    49. body>
    50. 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挥手。

    三次握手

    1. 第一次握手:客户端发送序列号Seq=0的SYN报文到服务器,表示请求建立连接。
    2. 第二次握手:服务器收到客户端的SYN报文,发送序列号Seq=0、确认号Ack=1的SYN, ACK报文到客户端,表示同意建立连接。
    3. 第三次握手:客户端收到服务器的SYN, ACK报文,发送序列号Seq=1、确认号Ack=1的ACK报文到服务器,表示确认建立连接。

    请求报文

    1. GET / HTTP/1.1
    2. Host: localhost:81
    3. Connection: keep-alive
    4. Cache-Control: max-age=0
    5. sec-ch-ua: "Chromium";v="106", "Microsoft Edge";v="106", "Not;A=Brand";v="99"
    6. sec-ch-ua-mobile: ?0
    7. sec-ch-ua-platform: "Windows"
    8. Upgrade-Insecure-Requests: 1
    9. 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
    10. 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
    11. Sec-Fetch-Site: none
    12. Sec-Fetch-Mode: navigate
    13. Sec-Fetch-User: ?1
    14. Sec-Fetch-Dest: document
    15. Accept-Encoding: gzip, deflate, br
    16. 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请求,不包含请求体。

    响应报文

    1. HTTP/1.1 200 OK\r\n
    2. Content-Type: text/html\r\n
    3. Last-Modified: Thu, 27 Oct 2022 02:34:23 GMT\r\n
    4. Accept-Ranges: bytes\r\n
    5. ETag: "b7e928a0ace9d81:0"\r\n
    6. Server: Microsoft-IIS/10.0\r\n
    7. Date: Thu, 27 Oct 2022 02:34:43 GMT\r\n
    8. Content-Length: 1631\r\n
    9. \r\n
    10. [HTTP response 1/2]
    11. [Time since request: 0.003017000 seconds]
    12. [Request in frame: 5]
    13. [Next request in frame: 29]
    14. [Next response in frame: 31]
    15. [Request URI: http://127.0.0.1/favicon.ico]
    16. 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编程捕获的四次挥手:

    1. A向B发送FIN, ACK报文,请求终止连接。
    2. B收到A发送的FIN, ACK报文后,回复ACK报文,表示确认收到了终止连接的请求。
    3. B向A发送FIN, ACK报文,请求终止连接。
    4. A收到B发送的FIN, ACK报文后,回复ACK报文,表示确认收到了终止连接的请求,双方正式终止连接。

    客户端和服务器都可以主动发起挥手动作。在IIS服务器捕获时四次挥手非常标准,我关闭了浏览器,由客户端先向服务器发送终止连接的请求。

    但在C++ socket编程捕获的文件中, 只能看到三次挥手,但是观察Seq和Ack的值和连接的过程,一切正常。查阅相关资料后分析,这是因为我的服务器是通过socket编程实现的,发送完响应报文之后,调用close函数时就会向客户端发送终止连接的请求,这一步并不会在捕获文件中体现。之后客户端(64400)回复ACK报文确认收到了终止连接的请求也印证了这一点。

    实验中遇到的问题及分析

    1. 在使用C++搭建简单的web服务器时,由于对html的了解不够深刻,发送的响应报文不正确,一直无法正常打开网页。经过分析发现两个问题:第一,发现Content-length一定要和消息体(html文件)的长度一致,否则无法正确显示;第二,发送响应报文,发送的消息长度必须和实际的一致,太长了会读入乱码。虽然能够顺利完成实验,但是这个程序只是一个很简单的web服务器,没有对请求报文进行解析,响应报文也只有一种情况。希望后续能够在对http协议学习更深入的基础上编写一个完整的web服务器。
    2. 在编写web界面时,由于对于html语言了解不够深刻,出现了很多语法错误,也难以实现想要的效果。经过对html语言的初步学习后,能够实现预期的界面。但是这只是一个很简单的个人主页,希望以后能够改进。
    3. 在使用Wireshark捕获交互过程时,一开始对于软件不够熟悉,不清楚如何使用。后来可以正常捕获,但是捕获的内容很多,经过学习我发现可以使用过滤条件,就能快速定位到我们需要的交互过程。
    4. 在IIS服务器捕获的文件中,一开始我看到的响应报文总是304 Not Modified,但是依然能正常打开网页。后来发现是因为网页有缓存机制,如果第一次连接之后,html文件没有改变,就不会重新返回200 OK的响应报文。因此,我稍微修改了一下html文件,就成功捕获到了200 OK的响应报文。
    5. 在C++ socket编程捕获的文件中,我只观察到三次挥手,但是观察Seq和Ack的值和连接的过程,一切正常。查阅相关资料后分析,这是因为我的服务器是通过socket编程实现的,发送完响应报文之后,调用close函数时就会向客户端发送终止连接的请求,这一步并不会在捕获文件中体现。

    参考资料

    Sec-Fetch-*请求头,了解下? - 福禄网络研发团队 - 博客园

    使用 WireShark 分析 TCP/IP 三次握手 和 四次挥手 - bylijian - 博客园

  • 相关阅读:
    程序员周末都干些什么?
    Find My资讯|苹果Find My帮助美警察逮捕连环盗车嫌犯
    皕杰报表之性能管理
    Python学习笔记--字符、变量、数据类型与注释
    Spark Streaming
    PLSQL数据库Mybatis学习Day02
    pod生命周期回调prestop、poststart源码分析
    大学生竞赛指南 互联网+
    NaiveUI中看起来没啥用的组件(文字渐变)实现原来这么简单
    UVa 762 - We Ship Cheap
  • 原文地址:https://blog.csdn.net/Roslin_v/article/details/127540126