Skip to content

[toc]

Socket编程

1 实验类型

验证型,2学时,必选实验

2 实验目的

掌握Socket编程中流套接字的技术;熟悉http消息结构;

3 实验内容与要求

使用Socket开发网络服务器,接收并保存请求原文;使用Socket开发网络客户端模拟浏览器向http服务器发送请求并记录响应结果;

4 实验环境

Microsoft Edge/Chrome/Firefox等浏览器,Visual Studio CodePython 3.4+,可用的互联网

步骤

  1. 创建工作目录学号,最终目录结构如下(包含创建的各类文件):
学号:.
    request.html
    request_client.py
    request_server.py

网络服务器

  1. 使用Socket技术编写网服务器(request_server.py),监听本机8080端口,将网络请求原文保存名为req_1.txt的文本文件,如:
request_server.py
import socket #导入socket模块

host = '127.0.0.1' #主机IP
port = 8080 #端口号
web = socket.socket() #创建 socket 对象
web.bind((host,port)) #绑定端口
web.listen(5) #设置最多连接数
print ('服务器等待客户端连接...')
req_index = 0 #请求序号

#开启死循环
while True: 
  conn, addr = web.accept() #建立客户端连接
  data = conn.recv(1024) #获取客户端请求数据
  print("{} connected!".format(addr))

  #记录请求信息
  req_index += 1
  file_name = "req_{}.txt".format(req_index)
  with open(file_name, 'w+', encoding='utf-8')as file:
    file.write(data.decode())

  #回应请求
  conn.sendall(b'HTTP/1.1 200 OK\r\n\r\nHello World') #向客户端发送数据
  conn.close() #关闭连接

Http请求分析

HTTP 请求和响应具有相似的结构,由以下部分组成︰ 一行起始行用于描述要执行的请求,或者是对应的状态,成功或失败。这个起始行总是单行的。 一个可选的 HTTP 头集合指明请求或描述消息正文。 一个空行指示所有关于请求的元数据已经发送完毕。 一个可选的包含请求相关数据的正文 (比如 HTML 表单内容), 或者响应相关的文档。正文的大小有起始行的 HTTP 头来指定。 起始行和 HTTP 消息中的 HTTP 头统称为请求头,而其有效负载被称为消息正文。 http

  1. 编写页面(request.html)模拟超链接表单等形式的网络请求,如:

request.html
<html>

<body>
  <!-- 超链接-->
  <a href="http://127.0.0.1:8080" target="_blank">纯粹的链接</a><br>
  <a href="http://127.0.0.1:8080/?name=ok&password=good" target="_blank">带参数的链接</a><br>
  <a href="http://127.0.0.1:8080/?name=o k&password=goo d" target="_blank">带空格的链接</a><br>

  <!--GET表单-->
  <form name="GET表单" action="http://127.0.0.1:8080" method="GET" target="_blank">
    <input type="text" name="name" value="ok"/>
    <input type="password" name="password" value="good"/>
    <input type="submit" value="提交"/>
  </form>

  <!--POST表单-->
  <form name="POST表单" action="http://127.0.0.1:8080" method="POST" target="_blank">
    <input type="text" name="name" value="ok"/>
    <input type="password" name="password" value="good"/>
    <input type="submit" value="提交"/>
  </form>

  <!--JS修改location-->
  <script>
    function jsonPack(){
      var form = document.forms["JSON表单"];
      var data = {
        name: form["name"].value, 
        password: form["password"].value
      };

      form["all"].value = JSON.stringify(data);
    }
  </script>

  <!--POST表单-->
  <form name="JSON表单" action="http://127.0.0.1:8080" method="POST" target="_blank" onsubmit="jsonPack()">
    <input type="hidden" name="all"/>
    <input type="text" name="name" value="ok"/>
    <input type="password" name="password" value="good"/>
    <input type="submit" value="提交"/>
  </form>
</body>

</html>
3. 在浏览器中打开步骤2中的页面,点击触发点向服务器发送请求

  1. 观察请求原文中消息结构并指出其中的差异

网络客户端

  1. 使用Socket开发网络客户端(request_client.py)模拟浏览器向http服务器发送请求并记录响应结果,如:
request_client.py
from datetime import datetime
import socket
from urllib.parse import urlparse

def get_url(url):
  # 通过socket请求Url
  url = urlparse(url)
  host = url.hostname
  path = url.path
  port = url.port
  if path == "":
    path = "/"

  if port is None:
    port = 80

  # 建立socket连接
  client = socket.socket()
  try:
    print("{}:{} Connecting...".format(host, port))
    client.connect((host, port))
  except Exception as e:
    print(e)

  # 发送请求
  client.send("GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n".format(path, host).encode('utf8'))

  # 接收数据
  recved = b""
  while True:
    bytes = client.recv(1024)
    if bytes:
      recved += bytes
    else:
      break

  data = recved.decode('utf8')

  # 记录响应信息
  time_wall = datetime(2022, 7, 16)
  elapsed = round((time_wall - datetime.now()).total_seconds())
  file_name = "res_{}.txt".format(-elapsed)
  with open(file_name, 'w+', encoding='utf-8')as file:
    file.write(data)

  client.close()

if __name__ == '__main__':
  #get_url("http://www.baidu.com/")  
  get_url("http://127.0.0.1:8080/")

Http响应分析

  1. 观察响应原文中消息的结构