K230使用HTTP请求与大语言模型对话-手搓协议完成requests

HTTP请求?什么是HTTP请求?
HTTP(超文本传输协议,Hypertext Transfer Protocol) 是一种用于从网络传输超文本到本地浏览器的传输协议。它定义了客户端与服务器之间请求和响应的格式。HTTP 工作在 TCP/IP 模型之上,通常使用端口 80。是用于从万维网( WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP 是一个基于 TCP/IP 通信协议来传递数据(HTML 文件、图片文件、查询结果等)。

简单的来说,HTTP请求也就是TCP通信,只不过增加了一些通信协议而已(浏览器本质上也是向服务器发送HTTP请求的GET方法而已)

也就是说,我们只需要使用socket模块构建TCP通信,然后再根据服务器的通信协议发送对应的协议就可以收到对应的消息,步骤:

1.连接服务器
2.发送请求头和请求体
3.等待接收服务器消息

接下来重点讲解请求头:

image

一个完整的请求头是这样:

POST /api/paas/v4/chat/completions HTTP/1.1
Host: open.bigmodel.cn
Authorization: Bearer XXXXXXX
Content-Type: application/json
Content-Length: 77
(请求数据)

第一行的POST代表使用POST方法后面跟着的是请求路径POST方法可以换成其他方法:

在与大模型的通信中,我们使用的是HTTP请求的POST方法
第二行的Host:后面是请求的网址
第三行是附加数据,代表与大模型通信的API key
第四行也是附加数据,代表请求数据的类型是json格式的文本
第五行也是附加数据,代表请求数据的大小
请求头构造完成后,使用两个换行,代表结束,然后再发送请求数据
发送完数据后等着接收服务器消息即可,更多基础知识请查看: HTTP协议详解–请求与响应-CSDN博客

代码操作:
1.创建socket套接字:

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

2.连接服务器:

client_socket.connect(socket.getaddrinfo(host, port)[0][-1])#端口号port通常为80

3.构建HTTP请求头:

http_request = "
POST /api/paas/v4/chat/completions HTTP/1.1
Host: open.bigmodel.cn
Authorization: Bearer XXXXXXX
Content-Type: application/json
Content-Length: 77
(请求数据)
"

4.发送HTTP请求:

client_socket.sendall(http_request.encode('utf-8'))#encode()的作用是转为二进制数据,在网络传输的时候只能传输二进制数据

5.接收服务器响应

response = b""
while True:
    data = client_socket.recv(4096)
    if not data:
        break
    response += data

具体实现思路就是这样,接下来,想要与大模型通信(对话)我们需要选择一个大模型,这里我使用的是ChatGLM4,我们首先需要到官网上注册并创建一个API:
智谱AI开放平台 (bigmodel.cn)



这个API key需要牢记,不可随意告诉别人
接下来我们来到官方文档: 智谱AI开放平台 (bigmodel.cn)
这里详细告诉了我们如何发送HTTP请求:

第一行是请求的网址(URL)
第二,三行是请求头的附加数据
第四行是向大模型提问的数据,其中model代表调用的模型
所以具体请求数据如下:

http_request = "
POST /api/paas/v4/chat/completions HTTP/1.1
Host: open.bigmodel.cn
Authorization: Bearer XXXXXXX
Content-Type: application/json
Content-Length: 77
(请求数据)
"

完整代码:

import network,time,socket,json
from machine import Pin

WIFI_LED=Pin(52, Pin.OUT) #初始化WIFI指示灯

wlan = network.WLAN(network.STA_IF) #STA模式
wlan.active(True)                   #激活接口

def convert_chinese_to_unicode(text):#将中文转为Unicode码
    return ''.join(f'\\u{ord(char):04x}' if '\u4e00' <= char <= '\u9fff' else char for char in text)


def http_request(method, url, port=80, headers=None, body=None):
    # 分离 URL 获取主机名和路径
    url_without_protocol = url.split("://")[-1]
    host = url_without_protocol.split('/')[0]
    path = '/' + '/'.join(url_without_protocol.split('/')[1:])

    # 创建一个 socket 对象
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 连接服务器
    client_socket.connect(socket.getaddrinfo(host, port)[0][-1])

    # 构建 HTTP 请求
    request_line = f"{method} {path} HTTP/1.1\r\n"
    host_header = f"Host: {host}\r\n"

    # 构建 headers
    headers_section = ""
    if headers is not None:
        headers_section = ''.join(
            f"{convert_chinese_to_unicode(key)}: {convert_chinese_to_unicode(value)}\r\n"
            for key, value in headers.items()
        )

    # 空行表示 headers 结束
    end_of_headers = "\r\n"

    # 构建请求体
    body_section = ""
    if body is not None:
        body = json.dumps(body)  # 将 body 转换为 JSON 字符串
        body = convert_chinese_to_unicode(body)
        body_len = f"Content-Length: {len(body)}\r\n"
        body_section = f"{body}"
    else:
        body_len = ""

    # 完整的 HTTP 请求
    http_request = f"{request_line}{host_header}{headers_section}{body_len}{end_of_headers}{body_section}"
#    print(http_request)

    # 发送请求
    client_socket.sendall(http_request.encode('utf-8'))

    # 接收响应
    response = b""
    while True:
        data = client_socket.recv(4096)
        if not data:
            break
        response += data

    # 关闭连接
    client_socket.close()

    # 将响应转换为字符串并分割成 header 和 body
    response_str = response.decode('utf-8')
    try:
        header, body = response_str.split("\r\n\r\n", 1)
        # 解析状态行
        status_line = header.split("\r\n")[0]
        status_code = int(status_line.split()[1])
        # 只在状态码为200 OK时返回响应体
        if status_code == 200:
            return body
        else:
            return f"Error: Received status code {status_code}"

    except :
        # 如果找不到分隔符,可能是服务器没有返回正文
        return response_str




if not wlan.isconnected():

    print('开始连接WIFI...')

    #输入WIFI账号密码(仅支持2.4G信号), 连接超过10秒为超时
    wlan.connect('HiWiFi_5B4AC2', 'b8507123')

if wlan.isconnected(): #连接成功

    #LED蓝灯点亮
    WIFI_LED.value(1)

    HOST = wlan.ifconfig()[0]
    PORT = 8080

    #串口打印信息
    print(f'连接成功,IP:{HOST}')

    url = 'https://open.bigmodel.cn/api/paas/v4/chat/completions'
    headers = {
        'Authorization': 'Bearer XXXXX',
        'Content-Type': 'application/json'
    }
    data = {
        "model": "glm-4",
        "messages": [
            {
                "role": "user",
                "content": "你好"
            }
        ]
    }

    data = http_request('POST', url, port=80,headers=headers, body=data)
    try:
        result_dicty = json.loads(data)
        rec = result_dicty["choices"][0]["message"]["content"]
        print(rec)
    except Exception as e:
        print(f"错误: {e}")
        print(data)
#    print(http_request('GET', "https://www.baidu.com/",port=80))

else: #连接失败

    #LED闪烁3次提示
    for i in range(3):
        WIFI_LED.value(1)
        time.sleep_ms(300)
        WIFI_LED.value(0)
        time.sleep_ms(300)

    wlan.active(False)
    print('连接失败,尝试重启!')

实验现象:


值得一提的是,http_request函数也可以进行GET请求:

print(http_request('GET', "https://www.baidu.com/",port=80))

不过实测板子会卡死(我在电脑测试没问题)

1 Like

协议是好了,但好像是个跑不流畅得demo :rofl:

socket是阻塞的,只有完成这一行才会向下执行