☀️苏州程序大白用万字解析Python网络编程与Web编程☀️《❤️记得收藏❤️》

目录

🏳️‍🌈开讲啦!!!!🏳️‍🌈苏州程序大白🏳️‍🌈

在这里插入图片描述

🌟博主介绍

💂 个人主页:苏州程序大白

🤟作者介绍:中国DBA联盟(ACDU)成员,CSDN全国各地程序猿(媛)聚集地管理员。目前从事工业自动化软件开发工作。擅长C#、Java、机器视觉、底层算法等语言。2019年成立柒月软件工作室。

💬如果文章对你有帮助,欢迎关注、点赞、收藏(一键三连)和C#、Halcon、python+opencv、VUE、各大公司面试等一些订阅专栏哦

🎗️ 承接各种软件开发项目

💅 有任何问题欢迎私信,看到会及时回复

👤 微信号:stbsl6,微信公众号:苏州程序大白

🎯 想加入技术交流群的可以加我好友,群里会分享学习资料

计算机网络基础

IP地址的介绍

IP地址的概念:

  • IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。

  • IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
    在这里插入图片描述
    IP地址的表现形式:

  • IP 地址分为两类: IPv4 和 IPv6。

  • IPv4 是普遍正在被广泛使用的IP协议。

  • IPv6 是现阶段为了解决IPv4地址不够用的情况而正在普及的下一代IP协议。

  • IPv4 是由点分十进制组成。

  • IPv6 是由冒号十六进制组成。

IP地址的作用:

  • IP 地址的作用是标识网络中唯一的一台设备的,也就是说通过IP地址能够找到网络中某台设备。
    在这里插入图片描述
    查看IP地址:

  • Linux 和 mac OS 使用 ifconfig 这个命令。

在这里插入图片描述

  • Windows 使用 ipconfig 这个命令
    在这里插入图片描述

端口和端口号

什么是端口、什么是端口号

  • 即为数据传输的通道,若将IP地址比作一座房子的地址 ,那么端口就是出入房子的门;

  • 然而真正的房子只有几个门,但是一个IP地址的端口可以有65536个;

  • 端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535。

端口号的分类

  • 知名端口:0 - 1023。

  • 动态端口:1024 - 65535。

协议

TCP协议

  • TCP的概念

  • 传输控制协议(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

TCP通信流程

1、建立连接(三次握手)。

2、传输数据。

3、关闭连接(四次挥手)。

TCP的特点

  • 面向连接。

  • 可靠传输。

UDP协议

  • 概念:用户数据报协议(User Datagram Protocol)是OSI参考模型中一种无连接的传输层协议。

UDP的特点

  • 面向报文。

  • 无连接。

  • 吞吐量不受拥挤控制算法的调节。

socket

  • 什么是socket?
    网络套接字(英语:Network socket;又译网络套接字、网络接口、网络插槽)在计算机科学中是电脑网络中进程间数据流的端点,是一种操作系统提供的进程间通信机制。

  • socket的作用
    进程之间网络数据传输。

在这里插入图片描述

TCP网络开发流程

在这里插入图片描述

TCP客户端程序开发流程

  • 流程梳理:

1、创建服务端套接字对象。

2、绑定监听端口。

3、设置监听。

4、等待客户端的连接请求。

5、接受数据。

6、返回数据。

7、关闭套接字。

TCP服务端程序开发流程

  • 流程梳理:

1、创建客户端套接字对象。

2、和服务端套接字建立连接。

3、发送数据。

4、接受数据。

5、关闭客户端套接字。

TCP网络开发

socket类

Python 中,我们用 socket()函数来创建套接字,语法格式如下:

import socket

socket.socket([family[, type[, proto]]])
  • 参数:
参数描述
family套接字家族可以使AF_UNIX或者AF_INET
family套接字家族可以使AF_UNIX或者AF_INET
type套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAMSOCK_DGRAM
protocol一般不填默认为0
  • Socket 类型:
类型描述
socket.AF_UNIX只能够用于单一的Unix系统进程间通信
socket.AF_INET服务端与客户端之间通讯协议(IPv4)
socket.AF_INET6服务端与客户端之间通讯协议(IPv6)
socket.SOCK_STREAM使用TCP传输协议进行数据传输(流式socket)
socket.SOCK_DGRAM使用UDP传输协议进行数据传输(数据报式socket)
socket.SOCK_RAW原始套接字;可以处理普通套接字无法处理的ICMP,IGMP等特殊的网络报文
socket.SOCK_RDM提供可靠的UDP数据报连接,即保证交付数据报但不保证数据
socket.SOCK_SEQPACKET提供连续可靠的数据包连接
  • socket类方法:
方法描述
_socket.bind(address)将套接字绑定到地址;在AF_INET下,以元组(host,port)的形式表示地址。
_socket.listen(backlog)开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
_socket.setblocking(bool)是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。
_socket.accept()接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是客户端的地址。
_socket.connect(address)连接到address处的套接字。一般情况下address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
_socket.connect_ex(address)同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回错误代码
_socket.close()关闭套接字连接
_socket.recv(bufsize[,flag])接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。
_socket.recvfrom(bufsize[.flag])与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
_socket.send(string[,flag])将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
_socket.sendall(string[,flag])将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。内部通过递归调用send,将所有内容发送出去。
_socket.sendto(string[,flag],address)将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
_socket.settimeout(timeout)设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。
_socket.getpeername()返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
_socket.getsockname()返回套接字自己的地址。通常是一个元组(ipaddr,port)
_socket.fileno()套接字的文件描述符

TCP客户端程序开发

import socket   # 导入socket包
if __name__ == '__main__':
    # 创建socket套接字   AF_INET -> 采用IPv4 ;SOCK_STREAM -> 采用TCP传输协议
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 与服务端建立连接
    client_socket.connect(('127.0.0.1', 9091))
    # 准备需要发送的数据,使用UTF-8进行编码
    _data = 'Connect Succces!'.encode('utf-8')
    # 发送数据
    client_socket.send(_data)
    # 获取服务端返回数据
    _recv = client_socket.recv(1024)
    # 打印服务端返回的原始数据
    print('获得来自服务器的原始数据:', _recv)
    # 对数据进行解码
    _decode = _recv.decode('utf-8')
    print('获得来自服务器的数据:', _decode)
    # 关闭socket套接字
    client_socket.close()

TCP服务端程序开发

单任务版:

import socket # 导入socket包
if __name__ == '__main__':
    # 创建socket套接字   AF_INET -> 采用IPv4 ;SOCK_STREAM -> 采用TCP传输协议
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置启用端口复用,当程序结束时,立即释放端口号
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 绑定监听端口号
    server_socket.bind(("0.0.0.0", 9091))
    # 配置监听最大等待连接个数
    server_socket.listen(128)
    # 等待客户端建立连接请求,返回(conn,info),若无连接则会一直保持阻塞状态
    # 其中conn由service_socket接收,是与客户端建立连接的套接字
    # info由client_info接收,是客户端的地址与端口信息
    service_socket, client_info = server_socket.accept()
    print('客户端的IP地址和端口号:', client_info)
    # 获取客户端发送的原始数据
    _renv = service_socket.recv(1024)
    # 获取原始数据的长度
    _length = len(_renv)
    print('接收数据的长度为:', _length)
    # 对原始数据进行解码
    _decode = _renv.decode('utf-8')
    print('接收客户端的数据为:', _decode)
    # 准备需要返回的数据,使用UTF-8进行编码
    _data = '问题处理中...'.encode('utf-8')
    # 发送数据
    service_socket.send(_data)
    # 关闭服务端与客户端的套接字
    service_socket.close()
    # 关闭服务端套接字
    server_socket.close()

多任务版:

在现实生产环境中,一个服务端不可能只就服务于一个客户端;通常一个服务端是要能服务多个客户端,以下是多任务的实现思路:

1、编写一个TCP服务端程序,循环等待接受客户端的连接请求。

2、当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞。

3、把创建的子线程设置成为守护主线程,防止主线程无法退出。

import socket
import threading
# 客户端服务处理函数
def handle_client_request(_socket, _info):
    while True:
        # 获取客户端发送的原始数据
        _data = _socket.recv(1024)
        # 容器类型判断是否有数据可以直接使用if语句进行判断,如果容器类型里面有数据表示条件成立,否则条件失败
        # 容器类型: 列表、字典、元组、字符串、set、range、二进制数据
        if _data:
            print(_data.decode("utf-8"), _info)
            # 回复
            _socket.send("数据接收正常...".encode("utf-8"))
        else:
            print("客户端下线了:", _info)
            break
    # 关闭服务端与客户端的套接字
    _socket.close()

if __name__ == '__main__':
    # 创建socket套接字   AF_INET -> 采用IPv4 ;SOCK_STREAM -> 采用TCP传输协议
    tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置启用端口复用,当程序结束时,立即释放端口号
    tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 绑定监听端口号
    tcp_server_socket.bind(("", 9090))
    # 配置监听最大等待连接个数
    tcp_server_socket.listen(128)
    # 循环等待接收客户端的连接请求
    while True:
        # 等待客户端建立连接请求,返回(conn,info),若无连接则会一直保持阻塞状态
        # 其中conn由service_socket接收,是与客户端建立连接的套接字
        # info由client_info接收,是客户端的地址与端口信息
        service_socket, client_info = tcp_server_socket.accept()
        print("客户端连接成功:", client_info)
        # 当客户端和服务端建立连接成功以后,创建一个子线程处理接下来的客户端讯息
        client_thread = threading.Thread(target=handle_client_request, args=(service_socket, client_info))
        # 设置守护主线程,当主线程退出时自动终止子线程
        client_thread.setDaemon(True)
        # 启动子线程
        client_thread.start()

网络开发注意点

1、当 TCP 客户端程序想要和 TCP 服务端程序进行通信的时候必须要先建立连接。

2、TCP 客户端程序一般不需要绑定端口号,因为客户端是主动发起建立连接的。

3、TCP 服务端程序必须绑定端口号,否则客户端找不到这个 TCP 服务端程序。

4、listen 后的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息。

5、当 TCP 客户端程序和 TCP 服务端程序连接成功后, TCP 服务器端程序会产生一个新的套接字,收发客户端消息使用该套接字。

6、关闭 accept 返回的套接字意味着和这个客户端已经通信完毕。

7、关闭 listen 后的套接字意味着服务端的套接字关闭了,会导致新的客户端不能连接服务端,但是之前已经接成功的客户端还能正常通信。

8、当客户端的套接字调用 close 后,服务器端的 recv 会解阻塞,返回的数据长度为0,服务端可以通过返回数据的长度来判断客户端是否已经下线,反之服务端关闭套接字,客户端的 recv 也会解阻塞,返回的数据长度也为0。

socket中 send 与 recv原理剖析

在这里插入图片描述
send原理

Q:send是不是直接把数据发给服务端?

A:不是,要想发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡 。

recv原理

Q:renv是不是直接从客户端接收数据?

A:不是,应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间),应用程序再从接收缓存区获取客户端发送的数据。

HTTP协议

什么是HTTP协议

  • HTTP协议介绍:

1、HTTP 协议的全称是(HyperText Transfer Protocol),翻译过来就是超文本传输协议。

2、超文本是超级文本的缩写,是指超越文本限制或者超链接,比如:图片、音乐、视频、超链接等等都属于超文本。

3、HTTP 协议的制作者是蒂姆·伯纳斯-李,1991年设计出来的,HTTP 协议设计之前目的是传输网页数据的,现在允许传输任意类型的数据。

4、传输 HTTP 协议格式的数据是基于 TCP 传输协议的,发送数据之前需要先建立连接。

  • HTTP协议的作用:

1、规定浏览器和web服务器通信的数据格式。

  • 浏览器访问Web服务器的通讯过程

在这里插入图片描述

什么是URL

什么是URL:

1、URL的英文全拼是(Uniform Resoure Locator),表达的意思是统一资源定位符,通俗理解就是网络资源地址,也就是我们常说的网址。

  • URL的组成

URL的样子:
在这里插入图片描述
URL的组成部分:

1、协议部分: https://http://ftp://

2、域名部分: news.163.com。

3、资源路径部分: /18/1122/10/E178J2O4000189FH.html。

域名:

  • 域名就是IP地址的别名,它是用点进行分割使用英文字母和数字组成的名字,使用域名目的就是方便的记住某台主机IP地址。

URL的扩展:

  • model遵守Codable协议。

  • 用JSONEncoder进行解析。

定义ConvertToStringable协议
protocol ConvertToStringable {
    associatedtype Result: Codable
    var valueString: String { get }
}

extension ConvertToStringable {
    func toString(result: Result) -> String {
        let data = try? JSONEncoder().encode(result)
        guard let da = data else { return "" }
        guard let st = String.init(data: da, encoding: .utf8) else { return "" }
        return st
    }
}

我们使用关联类型来匹配不同的模型实例,然后我们在每个需要model转为JSON格式字符串的model里扩展一下model:

struct UserInfo: Codable {
    var name: String
    var age: Int
    var avator: String
}

extension UserInfo: ConvertToStringable {
    typealias Result = UserInfo
    var valueString: String { return toString(result: self) }
}

查询参数部分: ?page=1&count=10

  • 参数说明:

? 后面的 page 表示第一个参数,后面的参数都使用 & 进行连接。

HTTP请求报文

GET请求报文

---- 请求行 ----
GET / HTTP/1.1  # GET请求方式 请求资源路径 HTTP协议版本
---- 请求头 -----
Host: www.smartfox.cc  # 服务器的主机地址和端口号,默认是80
Connection: keep-alive # 和服务端保持长连接
Upgrade-Insecure-Requests: 1 # 让浏览器升级不安全请求,使用https请求
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36  # 用户代理,也就是客户端的名称
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 # 可接受的数据类型
Accept-Encoding: gzip, deflate # 可接受的压缩格式
Accept-Language: zh-CN,zh;q=0.9 #可接受的语言
Cookie: csrftoken=ZIVKMSEdmdJbowTnXtRPXByIqxK1WF1ronGQXKWdp51WnSvmlRyqsKzZFPAojcLF; sessionid=as3sop6t2igilg76zll45m045udfsoa7; # 登录用户的身份标识

---- 空行 ----

也就是说GET请求报文是由以下部分组成的:

  • 请求行。

  • 请求头。

  • 空行。

POST请求报文

---- 请求行 ----
POST /admin.php?next=index.php HTTP/1.1 # POST请求方式 请求资源路径 HTTP协议版本
---- 请求头 ----
Host: www.smartfox.cc # 服务器的主机地址和端口号,默认是80
Connection: keep-alive # 和服务端保持长连接
Content-Type: application/x-www-form-urlencoded  # 告诉服务端请求的数据类型
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36 # 客户端的名称
---- 空行 ----
---- 请求体 ----
username=admin&pass=admin # 请求参数

也就是说POST报文是由以下部分组成:

  • 请求行

  • 请求头

  • 空行

  • 请求体

POST与GET之间的区别

在这里插入图片描述

  • 一个HTTP请求报文可以由请求行、请求头、空行和请求体4个部分组成。

  • GET方式的请求报文没有请求体,只有请求行、请求头、空行组成。

  • POST方式的请求报文可以有请求行、请求头、空行、请求体四部分组成。

注意:POST方式可以允许没有请求体,但是这种格式很少见。

HTTP响应报文

HTTP响应报文

--- 响应行/状态行 ---
HTTP/1.1 200 OK # HTTP协议版本 状态码 状态描述
--- 响应头 ---
Server: Tengine # 服务器名称
Content-Type: text/html; charset=UTF-8 # 内容类型
Transfer-Encoding: chunked # 发送给客户端内容不确定内容长度,发送结束的标记是0\r\n, Content-Length表示服务端确定发送给客户端的内容大小,但是二者只能用其一。
Connection: keep-alive # 和客户端保持长连接
Date: Fri, 23 Nov 2018 02:01:05 GMT # 服务端的响应时间
--- 空行 ---
--- 响应体 ---
<!DOCTYPE html><html lang=“en”></html> # 响应给客户端的数据

所以一个成熟的HTTP响应报文是由以下部分组成的:

在这里插入图片描述

常见HTTP状态码

状态码状态说明
200OK请求成功
201Created请求已经被实现,而且所需资源已建立,且其URI已经随头部信息返回。
202Accepted服务器已接受请求,但尚未处理。
307Temporary Redirect重定向
400Bad Request错误的请求,请求地址或者参数有误
403Forbidden服务器已经理解请求,但是拒绝执行它。
404Not Found请求资源在服务器不存在
500Internal Server Error服务器内部源代码出现错误
502Bad Gateway作为网关或代理的服务器尝试执行请求时,从上游服务接到无效的响应。

使用Python自带的HTTP服务器

静态web服务器是什么

  • 可以为发出请求的浏览器提供静态文档的程序。

  • 平时我们浏览百度新闻数据的时候,每天的新闻数据都会发生变化,那访问的这个页面就是动态的,而我们开发的是静态的,页面的数据不会发生变化。

如何搭建Python自带的静态Web服务器

在Python3的模块中,官方加入了http模块,我们可以直接调用运行它,让他作为提供静态Web的服务。

  • 语法格式:python3 -m http.server [PORT]

  • -m表示运行包里面的模块。

  • 执行这个命令的时候,需要进入你自己指定静态文件的目录,然后通过浏览器就能访问对应的静态文件了。

python3 -m http.server 8080

在这里插入图片描述

自制静态Web服务器

返回固定页面数据

import socket

if __name__ == '__main__':
    _server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    _server.bind(("", 9091))

    _server.listen(10)

    while True:
        _client, _client_info = _server.accept()

        _request_data = _client.recv(4096)

        print(_request_data)

        with open("static/index.html", 'rb') as _file:
            file_data = _file.read()
            print(file_data)

        response_line = "HTTP/1.1 200 OK\r\n"

        response_head = "Server: NGINX/14.8\r\n"

        response_body = file_data

        response_data = (response_line + response_head + "\r\n").encode('utf-8') + response_body
        print(response_data)
        _client.send(response_data)

        _client.close()

返回指定页面数据

import socket


def init_server():
    _server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    _server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    _server.bind(("", 9091))
    print("Server Listen : 0.0.0.0:9091")
    _server.listen(128)
    return _server


def service_logic(_server):
    _service, _info = _server.accept()
    renv_data = _service.recv(4096)
    if len(renv_data) == 0:
        print('客户端', _info, '离线')
        _service.close()
        return

    _data = renv_data.decode('utf-8')
    url = _data.split(" ", maxsplit=2)

    uri = url[1]
    print(uri)

    if uri == "/":
        uri = '/index.html'

    try:
        with open("static" + uri, "rb") as _file:
            response_body = _file.read()
    except Exception as e:
        response_line = "HTTP/1.1 404 Not Found\r\n"

        response_header = "Server: PythonWeb1.0\r\n"
        with open("static/error.html", "rb") as _file:
            response_body = _file.read()

        response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body

        _service.send(response_data)
    else:
        response_line = "HTTP1.1 200 OK\r\n"

        response_header = "Server: PythonWeb1.0\r\n"
        response_data = (response_line + response_header + "\r\n").encode('utf-8') + response_body

        _service.send(response_data)
    finally:
        _service.close()


if __name__ == '__main__':
    server = init_server()
    while True:
        service_logic(server)

多任务版

import socket
import threading


def init_server():
    _server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    _server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    _server.bind(("", 9091))
    print("Server Listen : 0.0.0.0:9091")
    _server.listen(128)
    return _server


def handle_client(_service, _info):
    renv_data = _service.recv(4096)
    if len(renv_data) == 0:
        print('客户端', _info, '离线')
        _service.close()
        return

    _data = renv_data.decode('utf-8')
    url = _data.split(" ", maxsplit=2)

    uri = url[1]
    print(uri)

    if uri == "/":
        uri = '/index.html'

    try:
        with open("static" + uri, "rb") as _file:
            response_body = _file.read()
    except Exception as e:
        response_line = "HTTP/1.1 404 Not Found\r\n"

        response_header = "Server: PythonWeb1.0\r\n"
        with open("static/error.html", "rb") as _file:
            response_body = _file.read()

        response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body

        _service.send(response_data)
    else:
        response_line = "HTTP1.1 200 OK\r\n"

        response_header = "Server: PythonWeb1.0\r\n"
        response_data = (response_line + response_header + "\r\n").encode('utf-8') + response_body

        _service.send(response_data)
    finally:
        _service.close()


def service_logic(_server):
    _services, _info = _server.accept()

    handle_service = threading.Thread(target=handle_client, args=(_services, _info))
    handle_service.daemon = True
    handle_service.start()


if __name__ == '__main__':
    server = init_server()
    while True:
        service_logic(server)

面向对象版

import socket
import threading


class WebServer(object):
    def __init__(self, addr: str = '0.0.0.0', port: int = 9091) -> None:
        self._server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        self._server.bind((addr, port))
        print(f"Server Listen!{addr}:{port}")
        self._server.listen(128)

    def service_logic(self) -> None:
        while True:
            _services, _info = self._server.accept()
            handle_service = threading.Thread(target=self.handle_client, args=(_services, _info))
            handle_service.daemon = True
            handle_service.start()

    @staticmethod
    def handle_client(_service: socket.socket, _info: tuple) -> None:
        renv_data = _service.recv(4096)
        if len(renv_data) == 0:
            print('客户端', _info, '离线')
            _service.close()
            return

        _data = renv_data.decode('utf-8')
        url = _data.split(" ", maxsplit=2)

        uri = url[1]
        print(uri)

        if uri == "/":
            uri = '/index.html'

        try:
            with open("static" + uri, "rb") as _file:
                response_body = _file.read()
        except Exception as e:
            response_line = "HTTP/1.1 404 Not Found\r\n"

            response_header = "Server: PythonWeb1.0\r\n"
            with open("static/error.html", "rb") as _file:
                response_body = _file.read()

            response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body

            _service.send(response_data)
        else:
            response_line = "HTTP1.1 200 OK\r\n"

            response_header = "Server: PythonWeb1.0\r\n"
            response_data = (response_line + response_header + "\r\n").encode('utf-8') + response_body

            _service.send(response_data)
        finally:
            _service.close()


if __name__ == '__main__':
    server = WebServer()
    server.service_logic()

命令行启动动态绑定端口号

import socket
import threading
import sys


class WebServer(object):
    def __init__(self, addr: str = '0.0.0.0', port: int = 9091) -> None:
        self._server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        self._server.bind((addr, port))
        print(f"Server Listen!{addr}:{port}")
        self._server.listen(128)

    def service_logic(self) -> None:
        while True:
            _services, _info = self._server.accept()
            handle_service = threading.Thread(target=self.handle_client, args=(_services, _info))
            handle_service.daemon = True
            handle_service.start()

    @staticmethod
    def handle_client(_service: socket.socket, _info: tuple) -> None:
        renv_data = _service.recv(4096)
        if len(renv_data) == 0:
            print('客户端', _info, '离线')
            _service.close()
            return

        _data = renv_data.decode('utf-8')
        url = _data.split(" ", maxsplit=2)

        uri = url[1]
        print(uri)

        if uri == "/":
            uri = '/index.html'

        try:
            with open("static" + uri, "rb") as _file:
                response_body = _file.read()
        except Exception as e:
            response_line = "HTTP/1.1 404 Not Found\r\n"

            response_header = "Server: PythonWeb1.0\r\n"
            with open("static/error.html", "rb") as _file:
                response_body = _file.read()

            response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body

            _service.send(response_data)
        else:
            response_line = "HTTP1.1 200 OK\r\n"

            response_header = "Server: PythonWeb1.0\r\n"
            response_data = (response_line + response_header + "\r\n").encode('utf-8') + response_body

            _service.send(response_data)
        finally:
            _service.close()


if __name__ == '__main__':
    args = sys.argv
    if len(sys.argv) != 2:
        print("执行命令如下: python3 xxx.py 8000")
        exit(0)

        # 判断字符串是否都是数字组成
    if not sys.argv[1].isdigit():
        print("执行命令如下: python3 xxx.py 8000")
        exit(0)

    port = int(sys.argv[1])

    server = WebServer(port=port)
    server.service_logic()

🌟作者相关的文章、资源分享🌟

🌟让天下没有学不会的技术🌟

学习C#不再是难问题

🌳《C#入门到高级教程》🌳

有关C#实战项目

👉C#RS232C通讯源码👈

👉C#委托数据传输👈

👉C# Modbus TCP 源代码👈

👉C# 仓库管理系统源码👈

👉C# 欧姆龙通讯Demo👈

👉C#+WPF+SQL目前在某市上线的车管所摄像系统👈

👉2021C#与Halcon视觉通用的框架👈

👉2021年视觉项目中利用C#完成三菱PLC与上位机的通讯👈

👉VP联合开源深度学习编程(WPF)👈

✨有关C#项目欢迎各位查看个人主页✨

🌟机器视觉、深度学习🌟

学习机器视觉、深度学习不再是难问题

🌌《Halcon入门到精通》🌌

🌌《深度学习资料与教程》🌌

有关机器视觉、深度学习实战

👉2021年C#+HALCON视觉软件👈

👉2021年C#+HALCON实现模板匹配👈

👉C#集成Halcon的深度学习软件👈

👉C#集成Halcon的深度学习软件,带[MNIST例子]数据集👈

👉C#支持等比例缩放拖动的halcon WPF开源窗体控件👈

👉2021年Labview联合HALCON👈

👉2021年Labview联合Visionpro👈

👉基于Halcon及VS的动车组制动闸片厚度自动识别模块👈

✨有关机器视觉、深度学习实战欢迎各位查看个人主页✨

🌟Java、数据库教程与项目🌟

学习Java、数据库教程不再是难问题

🍏《JAVA入门到高级教程》🍏

🍏《数据库入门到高级教程》🍏

有关Java、数据库项目实战

👉Java经典怀旧小霸王网页游戏机源码增强版👈

👉js+css类似网页版网易音乐源码👈

👉Java物业管理系统+小程序源码👈

👉JavaWeb家居电子商城👈

👉JAVA酒店客房预定管理系统的设计与实现SQLserver👈

👉JAVA图书管理系统的研究与开发MYSQL👈

✨有关Java、数据库教程与项目实战欢迎各位查看个人主页✨

🌟分享Python知识讲解、分享🌟

学习Python不再是难问题

🥝《Python知识、项目专栏》🥝

🥝《Python 检测抖音关注账号是否封号程》🥝

🥝《手把手教你Python+Qt5安装与使用》🥝

🥝《用一万字给小白全面讲解python编程基础问答》🥝

🥝《Python 绘制Android CPU和内存增长曲线》🥝

有关Python项目实战

👉Python基于Django图书管理系统👈

👉Python管理系统👈

👉2021年9个常用的python爬虫源码👈

👉python二维码生成器👈

✨有关Python教程与项目实战欢迎各位查看个人主页✨

🌟分享各大公司面试题、面试流程🌟

面试成功不是难事

🍏《2021年金九银十最新的VUE面试题☀️《❤️记得收藏❤️》》🍏

🍏《只要你认真看完一万字☀️Linux操作系统基础知识☀️分分钟钟都吊打面试官《❤️记得收藏❤️》》🍏

🍏《❤️用一万字给小白全面讲解python编程基础问答❤️《😀记得收藏不然看着看着就不见了😀》》🍏

✨有关各大公司面试题、面试流程欢迎各位查看个人主页✨

在这里插入图片描述

❤️关注苏州程序大白公众号❤️


👇 👇👇

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏州程序大白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值