python高阶编程(二十):socket网络编程

1、socket介绍

socket(简称 套接字 ) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:
它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的
例如我们每天浏览网页、QQ 聊天、收发 email 等等

import socket
socket.socket(Family, Type)

参数说明:
Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)

2、实现TCP服务器和客户端

服务端

import socket

# 1、创建一个tcp的套接字
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2、绑定ip和端口
server_sock.bind(('127.0.0.1', 9988))

# 3、开启监听,设置最大的连接数
server_sock.listen(10)

while True:
    # 4、等待客户端的连接
    cli, addr = server_sock.accept()

    # 5、接收客户端的请求数据
    res = cli.recv(1024)
    print("服务端接收到的数据为:", res.decode())

    # 6、返回数据给客户端
    cli.send('你好,客户端,我收到了你的数据'.encode())

    # 7、断开连接
    cli.close()

客户端

import socket

# 1、创建一个tcp的套接字
cli_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2、连接到服务器参数(ip,port)
cli_sock.connect(('127.0.0.1', 9988))

# 3、向服务端发送请求:
info = '你好,我是tcp客户端'.encode()
cli_sock.send(info)

# 4、接收服务端的返回数据
res = cli_sock.recv(1024)
print("服务端返回的数据为:", res.decode())

# 5、关闭客户端(断开连接)
cli_sock.close()


socket:创建一个套接字 socket(AF_INET,SOCK_STREAM)
bind:绑定ip和port
listen:使套接字变为可以被动链接
accept:等待客户端的链接
recv/send:接收发送数据

3、实现HTTP服务器和客户端

http基于tcp实现,底层通信层用的是tcp协议
使用http请求TCP服务器,服务器收到的请求数据如下:
postman
在这里插入图片描述
浏览器
!](https://img-blog.csdnimg.cn/8b69aca650e64d57834d05125c4063a0.png)
在这里插入图片描述客户端发送的数据只要是按照http格式的数据,那么客户端发送的请求就是http请求在这里插入图片描述
在这里插入图片描述

服务端

import socket

# 1、创建一个tcp的套接字
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2、绑定ip和端口
server_sock.bind(('127.0.0.1', 9988))

# 3、开启监听,设置最大的连接数
server_sock.listen(10)

while True:
    # 4、等待客户端的连接
    cli, addr = server_sock.accept()

    # 5、接收客户端的请求数据(解析http请求的请求报文,并进行解析)
    res = cli.recv(1024)
    print("服务端接收到的数据为:", res.decode())

    # 6、返回数据给客户端(返回http响应报文)
    header = 'HTTP/1.1 200 OK\r\n'
    header += 'Content-Type: text/html;charset=utf-8\r\n'
    header += '\r\n'
    body = '响应体:你好,客户端,我收到了你的数据'
    header += body
    cli.send(header.encode())

    # 7、断开连接
    cli.close()

客户端

import socket

# 1、创建一个tcp的套接字
cli_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2、连接到服务器参数(ip,port)
cli_sock.connect(('127.0.0.1', 9988))

# 3、向服务端发送请求:
headers = 'GET / HTTP/1.1\r\n'
headers += 'Host:127.0.0.1:9988\r\n'
headers += '\r\n'

cli_sock.send(headers.encode())

# 4、接收服务端的返回数据
res = cli_sock.recv(10240)
print("服务端返回的数据为:", res.decode())

# 5、关闭客户端(断开连接)
cli_sock.close()

"""
http:请求数据包:请求首行+ 请求头 + 请求体

GET / HTTP/1.1
Host: 127.0.0.1:9988
Connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";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/99.0.4844.51 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,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
Cookie: csrftoken=Jvmv4Gz4R9N4JeSqvh9Yjg6HNJjGyrxwRdNWbsdjpec3N2SFweVMkT76DMKmZJWJ; sessionid=ml7u785x5yjkmb3hmac7mazch63b57ln




http 响应数据包:响应首行+响应头+响应体

HTTP/1.1 200 OK
Server: nginx/1.13.7
Date: Wed, 06 Apr 2022 12:40:17 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Thu, 23 Aug 2018 06:57:43 GMT
Connection: keep-alive
ETag: "5b7e5ae7-264"
Accept-Ranges: bytes

响应体xxxxx

"""

![在这里插入图片描述](https://img-blog.csdnimg.cn/e3eece48a3c04d1e9e65dd9ee9612a20.png

在这里插入图片描述

在这里插入图片描述
返回html的服务端

# 1、创建一个tcp的套接字
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2、绑定ip和端口
server_sock.bind(('127.0.0.1', 9988))

# 3、开启监听,设置最大的连接数
server_sock.listen(10)

while True:
    # 4、等待客户端的连接
    cli, addr = server_sock.accept()

    # 5、接收客户端的请求数据(解析http请求的请求报文,并进行解析)
    res = cli.recv(1024)
    print("服务端接收到的数据为:", res.decode())
    # 6、返回数据给客户端(返回http响应报文)
    ##======== 返回html页面==============
    # header = 'HTTP/1.1 200 OK\r\n'
    # header += 'Content-Type: text/html;charset=utf-8\r\n'
    # header += '\r\n'
    # # 读取要返回的html页面
    # with open('report.html', 'rb') as f:
    #     html_page = f.read()
    # response = header.encode() + html_page
    ##======== 返回json数据==============
    header = 'HTTP/1.1 200 OK\r\n'
    header += 'Content-Type: application/json;charset=utf-8\r\n'
    header += '\r\n'
    data = {
        "code":100,
        "msg":"请求成功",
        "data":None
    }
    # 将响应头和响应体进行拼接
    response = header + json.dumps(data)
    # 转换为二进制返回
    cli.send(response.encode())

    # 7、断开连接
    cli.close()

4、多线程实现并发服务器

import json
import socket
from concurrent.futures.thread import ThreadPoolExecutor


def handel_request(cli):
    """处理用户请求的方法"""
    # 1、接收客户端的请求数据(解析http请求的请求报文,并进行解析)
    res = cli.recv(1024)
    print("服务端接收到的数据为:", res.decode())
    # 2、返回数据给客户端(返回http响应报文)
    ##======== 返回json数据==============
    header = 'HTTP/1.1 200 OK\r\n'
    header += 'Content-Type: application/json;charset=utf-8\r\n'
    header += '\r\n'
    data = {
        "code": 100,
        "msg": "请求成功",
        "data": None
    }
    # 将响应头和响应体进行拼接
    response = header + json.dumps(data)
    # 转换为二进制返回
    cli.send(response.encode())
    # 3、断开连接
    cli.close()


# 1、创建一个tcp的套接字
server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2、绑定ip和端口
server_sock.bind(('127.0.0.1', 9988))

# 3、开启监听,设置最大的连接数
server_sock.listen(100)

# 4、创建一个线程池,每个用户开启一个线程进行处理
with ThreadPoolExecutor(max_workers=100) as tps:
    while True:
        # 4、等待客户端的连接
        cli, addr = server_sock.accept()
        tps.submit(handel_request, cli)

5、封装http服务器

import json
import socket
from concurrent.futures.thread import ThreadPoolExecutor


class Server:

    def __init__(self, ip='127.0.0.1', port=9988):
        # 1、创建一个tcp的套接字
        self.server_sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
        # 2、绑定ip和端口
        self.server_sock.bind((ip, port))
        # 3、开启监听,设置最大的连接数
        self.server_sock.listen(100)

    def run(self):
        """服务启动的方法"""
        with ThreadPoolExecutor(max_workers=100) as tps:
            while True:
                # 4、等待客户端的连接
                cli, addr = self.server_sock.accept()
                tps.submit(self.handle_request, cli)

    def recv_data(self, cli):
        """循环接收请求报文,直到数据全部接受完"""
        data = b''
        while True:
            res = cli.recv(1024)
            if len(res) < 1024:
                data += res
                break
            else:
                data += res
        return data.decode()

    def parser_request(self, request_info):
        """
        解析请求报文
        :param request_info: 请求报文
        :return:
        {
            path:请求路径,
            method: 请求方法,
            headers:请求头(字典)

            params:查询字符串,
            data:表单参数
            json:json参数
        }
        """
        request_info_list = request_info.split('\r\n')
        # 获取请求方法
        method = request_info_list[0].split(' ')[0]
        # 获取请求路径
        path = request_info_list[0].split(' ')[1]
        # 获取请求头
        header_list = request_info.split('\r\n\r\n')[0].split('\r\n')[1:]
        headers = {i.split(':')[0]: i.split(':')[1].strip() for i in header_list}
        # 判断是否有查询字符串参数
        if "?" in path:
            path, params_str = path.split('?')
            params_ = {i.split('=')[0]: i.split('=')[1] for i in params_str.split('&')}
        else:
            params_ = {}

        json_params = {}
        form_params = {}
        # 判断是否有请求体
        if headers.get('Content-Type'):
            # 判断是否有json参数
            if headers.get('Content-Type') == 'application/json':
                request_body = request_info.split('\r\n\r\n')[1]
                json_params = json.loads(request_body)
            elif headers.get('Content-Type') == 'application/x-www-form-urlencoded':
                request_body = request_info.split('\r\n\r\n')[1]
                form_params = {i.split('=')[0]: i.split('=')[1] for i in request_body.split('&')}
        # 判断是否有表单参数
        return {
            'path': path,
            "method": method,
            "headers": headers,
            "params": params_,
            "json": json_params,
            'data': form_params
        }

    def handle_request(self, cli):
        """处理客户端请求的方法"""
        # 1、接收客户端的请求数据
        request_info = self.recv_data(cli)
        # 2、解析http请求报文
        request = self.parser_request(request_info)
        # 3、处理相关的业务(后端开发,重点是业务处理)
        # 判断请求路径,调用对应的处理函数去处理改请求
        if request['path'] == '/login':
            body = '登录页面'
        elif request['path'] == '/register':
            body = '注册页面'
        elif request['path'] == '/user':
            body = '用户页面'
        else:
            body = '你访问的路径不存在'
        # 4、返回http响应报文
        header = 'HTTP/1.1 200 OK\r\n'
        header += 'Content-Type: text/html;charset=utf-8\r\n'
        header += '\r\n'
        # 将响应头和响应体进行拼接
        response = header + body
        # 转换为二进制返回
        cli.send(response.encode())
        cli.close()



if __name__ == '__main__':
    app = Server()
    app.run()

"""
json格式:参数传递:
POST / HTTP/1.1
Host: 127.0.0.1:9988
User-Agent: python-requests/2.24.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 102
Content-Type: application/json

{"title": "\u7528\u4f8b1", "data": "\u7528\u4f8b\u53c2\u6570", "expected": "\u9884\u671f\u7ed3\u679c"}


# 表单参数 (key1=value1&key2=value3)
POST / HTTP/1.1
Host: 127.0.0.1:9988
User-Agent: python-requests/2.24.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 113
Content-Type: application/x-www-form-urlencoded

title=%E7%94%A8%E4%BE%8B1&data=%E7%94%A8%E4%BE%8B%E5%8F%82%E6%95%B0&expected=%E9%A2%84%E6%9C%9F%E7%BB%93%E6%9E%9C

# 查询字符串参数 (在url中?key1=value1&key2=value2)

POST /?title=%E7%94%A8%E4%BE%8B1&data=%E7%94%A8%E4%BE%8B%E5%8F%82%E6%95%B0&expected=%E9%A2%84%E6%9C%9F%E7%BB%93%E6%9E%9C HTTP/1.1
Host: 127.0.0.1:9988
User-Agent: python-requests/2.24.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 0



"""

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值