手写SOCKET进行HTTP通信

网络基础

我们电脑主板上都内置了多种网卡,一般主要有以下几类:

  • 虚拟网卡(loopback)
    注意,它是虚拟的,并不是物理网卡,也被称为是本地环回地址(或接口),一般将127.0.0.1作为本地环回地址。

  • 有线网卡/以太网卡(Ethernet)
    这是以太网(局域网)使用的,我们日常说的网卡指的就是这个,插入的就是网线。

  • 无线网卡(WLAN)
    这是无线局域网所使用的网卡,笔记本上常内置此网卡,它用的是无线电技术,不需要像以太网卡那样插网线。

以上这些网卡都会绑定一个本机IP。

127.0.0.1

  • 这是一个回环地址(loopback address),用于指代本地计算机自己。当你向这个地址发送数据时,数据实际上不会离开你的计算机,而是在同一台机器上被处理。这在测试网络服务或进行本地通信时非常有用。

本机IP(192.168.100.123)

当前电脑的IP地址,本机IP中的有线网IP和无线网IP都是需要联网后才能正常分配和访问的,它们是本机对外开放的IP地址。
在局域网中共享打印机或文件,本地网卡所拥有的地址对于别的电脑来说就是唯一的一个访问的IP地址!这个本机IP地址属于整个局域网甚至还能通过路由器NAT上网!同样对于本机来说使用这个IP也是可以访问本地的!

0.0.0.0

  • 这个地址通常用于表示“所有可用的网络接口”。在服务器编程中,如果你的服务端应用程序绑定到0.0.0.0,它将监听所有网络接口上的请求,包括局域网、无线网络以及可能的互联网连接。这使得服务器可以从任何网络接口接收客户端的连接请求。

Flask框架前言

默认情况下,Flask会将’Connection: close’头部设置为响应中,这意味着每个请求后都会关闭连接。这是因为Flask采用了短连接的方式,即每个请求都使用一个新的TCP连接。这样做的好处是可以更好地控制资源,并在每次请求之间隔离状态。 

所以我们在发送请求过程中发过去的数据是keep-alive但是接受到的是clsoe,所以每次发数据都需要重新建立连接发送数据,不能够复用之前的请求。

暂时没有找到如何设置Flask的Connection为keep-alive

请求头参数示例

参考资料

HTTP的两种请求:GET和POST的作用、区别与本质-CSDN博客

代码简单示例

flask_server.py(服务端)

使用flask框架写的服务端

# add_url_rule与装饰器的关系
from flask import Flask, render_template, Response, request,redirect,url_for
import json
# import setting #这是一个配置文件的py文件app=Flask(__name__,template_folder='templates')
app.config['ENV']='development'
app.config['DEBUG']=True# app.config.from_object(setting)@app.route('/',endpoint='s')
def index():print("index 展示开始内容")return render_template('index.html')
users=[1,2,3,4,5,"test","fadsf"]@app.route('/register',methods=['get','post'])
def register():print("register === method = ", request.method)print(request.method)if request.method == 'POST':  #注意这里大写。username = request.form.get('username')passwd = request.form.get('passwd')repasswd = request.form.get('repasswd')print(passwd,repasswd)#密码一致性验证。if passwd == repasswd and passwd != '':#保存用户user = {'用户名':username,"密码":passwd}users.append(user)#return '注册成功<a href="/">返回首页</a>'return redirect('/') #重定向 有两次响应:1 302状态码+location 2.返货localtion请求地址内容return '两次密码不一样'return render_template('test.html')@app.route('/show')
def show():print("show")#users ->strreturn json.dumps(users)
#  app.add_url_rule('/test',view_func=test)
@app.route('/test')
def a():url = url_for('s') #路径反向解析print(url)return url
if __name__ == '__main__':# app.run(host="0.0.0.0",port=5000) #指定ip  127.0.0.1通 本地ip192.168.0.103通# app.run(host="127.0.0.1",port=5000) #指定ip  127.0.0.1通 本地ip不通app.run(host="192.168.0.103",port=5000) #指定ip 本地ip通 127.0.0.1不通

request.py(请求端)

# import pandas as pd
import requestsdef main():print("main start ===========================================")# url = "http://www.baidu.com"# url = "127.0.0.1:5000/show"url = "http://192.168.0.103:5000/show"reqponse = requests.request("GET",url)  #测试发现每次调用request.request都会进行TCP三次握手,如果想要发送多个请求,需要使用requests.session.print(reqponse.text)print("core = ", reqponse.status_code)print("session = ",requests.session())# reqponse = requests.request("GET",url)# print("core = ", reqponse.status_code)# print("session = ",requests.session())## reqponse = requests.request("GET",url)# print("core = ", reqponse.status_code)# print("session = ",requests.session())# s = requests.session()# s.get(url)# s.get(url)print("main end ===========================================")if __name__ == '__main__':main()

send_http.py(socket进行请求案例)

import socket# 目标服务器的地址和端口
host = '127.0.0.1'
port = 5000# 创建 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 连接到服务器
s.connect(("192.168.0.103", port))# 构建 HTTP GET 请求
http_request = 'GET /show HTTP/1.1\r\nHost: 192.168.0.103:5000\r\nConnection: close\r\n\r\n'
# 发送请求
s.sendall(http_request.encode('utf-8'))# 接收响应头
response = s.recv(4096)
print(response.decode('utf-8'))# 接受真正的数据
response = s.recv(4096)
# 打印响应
print(response.decode('utf-8'))# 关闭连接
s.close()

深入学习

flask_server.py(服务端)

# add_url_rule与装饰器的关系
import timefrom flask import Flask, render_template, Response, request,redirect,url_for
import json
# import setting #这是一个配置文件的py文件app=Flask(__name__,template_folder='templates')
app.config['ENV']='development'
app.config['DEBUG']=True# app.config.from_object(setting)@app.route('/',endpoint='s')
def index():print("index 展示开始内容")return render_template('index.html')users=[1,2,3,4,5,"test","fadsf"]@app.route('/register',methods=['get','post'])
def register():print("register === method = ", request.method)print(request.method)if request.method == 'POST':  #注意这里大写。username = request.form.get('username')passwd = request.form.get('passwd')repasswd = request.form.get('repasswd')print(passwd,repasswd)#密码一致性验证。if passwd == repasswd and passwd != '':#保存用户user = {'用户名':username,"密码":passwd}users.append(user)#return '注册成功<a href="/">返回首页</a>'return redirect('/') #重定向 有两次响应:1 302状态码+location 2.返货localtion请求地址内容return '两次密码不一样'return render_template('test.html')@app.route('/show',methods=['get','post'])
def show():print("show = ",request.method, "content_type =", request.content_type)# 拿到发过来的数据if request.method == "GET":s = request.args  # 获取get请求参数 ,地址 后面跟 ? 后面的数据 多个字段用 && 进行分割print("args = ",s)s = request.values  # 获取所有参数print("vallues = ",s)s = request.args.get("content")print("args.get(content = )",s)elif request.method == "POST":print("============================================================================")s = request.form # 获取表单数据print("form = ",s)s = request.args  # 获取get请求参数 ,地址 后面跟 ? 后面的数据 多个字段用 && 进行分割 ,POST请求不用这个print("args = ",s)s = request.values  # 获取所有参数:form和args里都有print("values = ",s)try:s = request.json #这种获取方式只适用于JSON格式的数据print("json = ",s)except Exception as e:print("获取JSON格式出现了错误")print("============================================================================")if(request.content_type == None):print("没有内容类型")elif(request.content_type.startswith("text/html; charset=utf-8")):print("文本")elif(request.content_type.startswith("application/x-www-form-urlencoded")):s = request.values.get('key')print("values.get key = ",s)elif(request.content_type.startswith("application/json")  ):#获取JSON参数s = request.json.get('key')print("request.json.get key = ",s)#获取JSON原始参数s  = request.get_data()print("request.get_data = ",s)elif request.content_type.startswith('multipart/form-data'):s = request.form.get('key')print("form.get key = ",s)else:print("未找到匹配的类型头",request.content_type)#users ->str# Content-Length: 32\r\nreturn "{'name':'zs','key':{1,2{2}222{}2{}222,332,32,3,4} '}"# return json.dumps(users)
#  app.add_url_rule('/test',view_func=test)
@app.route('/test')
def a():url = url_for('s') #路径反向解析print(url)return url
if __name__ == '__main__':app.run(host="127.0.0.1",port=5000) #指定ip  127.0.0.1通 本地ip不通# app.run(host="192.168.0.103",port=5000) #指定ip 本地ip通 127.0.0.1不通

request.py(请求端)

# import pandas as pd
import jsonimport requests# 目标服务器的地址和端口
host = '127.0.0.1'
port = 5000def main():print("main start ===========================================")# url = "https://www.baidu.com"url = f"http://{host}:{port}/show"# url = " http://192.168.0.103:5000/show?key=value1&key2=value2"# GET发送请求的第一种方式:路径后面拼接命令:   http://192.168.0.103:5000/show?key1=value1&key2=value2# reqponse = requests.request("GET",url)print("get request ================================================================")header = {"Connection":"close"} # keep-alivereqponse = requests.request("GET",url,params={'key1': 'value1','key2':'value2'},timeout=10,headers=header)print("txt = ",reqponse.text)print("code = ", reqponse.status_code)print("session = ",requests.session())print("headers = ", reqponse.headers)  # 访问百度界面 Connection不显示 访问自己的服务 connection 为 clsoeprint("post request =============================================================")# header = {"Content-Type": "application/json"} #可以指定头的内容类型进行发送,但是使用data进行传参,传递头为json会请求失败。。# 使用POST 请求时,发送参数为 json ={} 时, 发送的请求头类型为:application/json# 发送参数为 data = [] 时,发送的请求头类型为:application/x-www-form-urlencoded#用 data 参数提交数据时, request.body 的内容则为 a=1&b=2 的这种形式,#用 json 参数提交数据时, request.body 的内容则为’{“a”: 1, “b”: 2}’ 的这种形式paylod = {'key': 'value', 'Source': 'Python'}reqponse = requests.request("POST", url, data = (paylod) ,headers=header ) # ,headers = headerprint("txt = ", reqponse.text)print("code = ", reqponse.status_code)print("headers = ", reqponse.headers)  # 访问百度界面 Connection为 keep-alive   访问自己的服务print("main end ===========================================")if __name__ == '__main__':main()

send_post_http.py(POST发送请求)

import socket
import json
import   select# 目标服务器的地址和端口
import timehost = '127.0.0.1'
port = 5000
MAX_BUFFER_SIZE = 1024# post请求第一种用法:先接受头,然后在根据头中的内容大小接受实际数据。
def post1():print("===========================================post1()=================================")my_data=""# 创建 socket 对象s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect((host, port))content = "source=python&key=value"# 构建 HTTP POST 请求http_request = f'POST /show?get=true HTTP/1.1\r\n' \f'Host: {host}:{port}\r\n' \f'Content-Type: application/x-www-form-urlencoded\r\n' \f'Content-Length:{ len(content) }\r\n' \f'Connection: close\r\n' \f'\r\n'  #格式化字符串输出# POST请求在发送过程中必须要先发送请求头在发送请求数据信息。#发送请求头s.sendall(http_request.encode('utf-8'))#发送数据体s.sendall(content.encode("utf-8"))# 如果发送数据之后使用了sleep,,那么响应的头和内容可能会连到一起发送过来。出现TCP粘包的现象time.sleep(1)# 接收响应头response = s.recv(1024)# 找到Content-Length字段拿到真正的数据长度hs = response.decode("utf-8")print("header = ",hs,"len = ",len(hs))# print(f"test sub = {hs[147:]}")temp_len = hs.find("Content-Length")print("temp_len =",temp_len)if temp_len != -1:start_pos = temp_len + len("Content-Length")+2end_pos = hs.find("\r",start_pos)print(f"start_pos = {start_pos},end_pos = {end_pos}")content_len_str = hs[start_pos:end_pos]print("content_len_str = ",content_len_str)method = Trueif method:# 接受数据方式一:response = s.recv( int(content_len_str) )# 打印响应my_data = response.decode("utf-8")print("接受数据方式一:",my_data)else:# 接受数据方式二:count = 0data = b""while(count != int(content_len_str)):response = s.recv(12)data+=responsecount += len(response)print(f"单次接受的数据 {response},len = {len(response)}")print(f"缓存的数据{data},len = {len(data)}")if len(response)==0:print("接受到数据为0")breakelif len(response)<0:s.close()return "网络断开"print("接受数据完成 ",data)print("body = ",data.decode('utf-8'))my_data = data# 关闭连接s.close()return my_data# post请求第二种用法:直接用while循环一直接受数据,直到结束
def post2():print("===========================================post2()=================================")my_data=""# 创建 socket 对象s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 连接到服务器s.connect((host, port))content = "source=python&key=value"# 构建 HTTP POST 请求http_request = f'POST /show?get=true HTTP/1.1\r\n' \f'Host: {host}:{port}\r\n' \f'Content-Type: application/x-www-form-urlencoded\r\n' \f'Content-Length:{ len(content) }\r\n' \f'Connection: keep-alive\r\n' \f'\r\n'  #格式化字符串输出# POST请求在发送过程中必须要先发送请求头在发送请求数据信息。#发送请求头s.sendall(http_request.encode('utf-8'))#发送数据体s.sendall(content.encode("utf-8"))# s.setblocking(False)  # 非阻塞模式# s.settimeout(0)  ## s.setblocking(True)  # 阻塞模式# s.settimeout(None)result=""# 死循环来接受数据num_stop = 1while num_stop:num_stop = 1  # 不加这个数据会结算数据错误response = s.recv(5)# 找到Content-Length字段拿到真正的数据长度recive_data = response.decode("utf-8")print("recive_data = ",recive_data,"len = ",len(recive_data))result += recive_data  # 将请求拼接在result中# print("result = ",result)temp_len = result.find("Content-Length")print("temp_len =",temp_len)if temp_len != -1:start_pos = temp_len + len("Content-Length")+2end_pos = result.find("\r",start_pos)# 找不到结束的位置就跳过继续接受数据if end_pos == -1:continueprint(f"start_pos = {start_pos},end_pos = {end_pos}")content_len_str = result[start_pos:end_pos]print("content_len_str = ",content_len_str)# 一般\r\n\r\n是请求的结束标记。text_ind = result.find("\r\n\r\n", start_pos) + len("\r\n\r\n")if text_ind != -1:# 参数不正确response_str = result[text_ind:]print(f"response_str = {response_str} ")end_len = 1#从数据的第一个位置开始,少计算一个{ 所以最后带上} 正好num_strop = 0for i in range(end_len,len(response_str)):end_len+=1if response_str[i] =="{":num_stop+=1elif response_str[i] == "}":num_stop-=1if(num_stop ==0):if int(content_len_str) != end_len:print("请求头长度和实际内容长度不一致")resp_data = response_str[:end_len]print("resp_data = ",resp_data)my_data = resp_databreakprint("===============num_stop = ",num_stop,"==================")if(len(recive_data)< 0):s.close()elif len(recive_data)==0:break# 关闭连接s.close()return my_datadef post3():print("===========================================post3()=================================")my_data = ""# 创建 socket 对象s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)if s.getblocking() == True:# 连接到服务器s.connect((host, port))else:# 尝试连接到服务器try:s.connect((host, port))except BlockingIOError:# 等待连接完成r, _, _ = select.select([s], [], [], 10)  # 等待最多10秒if s in r:print("连接成功")else:print("连接超时")# 非阻塞模式# s.setblocking(False)# s.settimeout(0)# 阻塞模式# s.setblocking(True)# s.settimeout(None)content = "source=python&key=value"# 构建 HTTP POST 请求http_request = f'POST /show?get=true HTTP/1.1\r\n' \f'Host: {host}:{port}\r\n' \f'Content-Type: application/x-www-form-urlencoded\r\n' \f'Content-Length:{len(content)}\r\n' \f'Connection: close\r\n' \f'\r\n'  # 格式化字符串输出# POST请求在发送过程中必须要先发送请求头在发送请求数据信息。# 发送请求头s.sendall(http_request.encode('utf-8'))# 发送数据体s.sendall(content.encode("utf-8"))# time.sleep(1)  # 睡眠1秒钟,模拟粘包的现场result = ""# 死循环来接受数据while 1:response = s.recv(MAX_BUFFER_SIZE)# 找到Content-Length字段拿到真正的数据长度recive_data = response.decode("utf-8")print("recive_data = ", recive_data, "len = ", len(recive_data))result += recive_data  # 将请求拼接在result中# print("result = ",result)temp_len = result.find("Content-Length")print("temp_len =", temp_len)if temp_len != -1:start_pos = temp_len + len("Content-Length") + 2end_pos = result.find("\r", start_pos)# 找不到结束的位置就跳过继续接受数据if end_pos == -1:continueprint(f"start_pos = {start_pos},end_pos = {end_pos}")content_len_str = result[start_pos:end_pos]print("content_len_str = ", content_len_str)if int(content_len_str)>= MAX_BUFFER_SIZE:print("超出最大的content_len_str")# return  "ERROR"# 一般\r\n\r\n是请求的结束标记。text_ind = result.find("\r\n\r\n", start_pos) + len("\r\n\r\n")if text_ind != -1:# 肯定一直都是成立的条件response_str = result[text_ind:]print(f"response_str = {response_str} ")if len(response_str) >= len(content_len_str):my_data = response_strs.close()return my_dataif (len(recive_data) < 0):s.close()elif len(recive_data) == 0:break# 关闭连接s.close()return my_dataif __name__ == '__main__':# s = post1()  # 方式一可能出现粘包的现场s = post2()  # 方式二没有问题# s = post3() # 方式三不知道在哪找到的版本有问题print("s = ",s)

send_get_http.py(GET发送请求)

import select
import socket# 目标服务器的地址和端口
import timehost = '127.0.0.1'
port = 5000
MAX_BUFFER_SIZE = 1024def get1():print("===========================================get1()=================================")# 创建 socket 对象s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 连接到服务器s.connect((host, port))# 构建 HTTP GET 请求# keep-alive 保持连接   close 关闭连接# 构建 HTTP GET 请求 get请求数据一般都在头里,不需要再次发送数据体http_request = f'GET /show?aaa=111 HTTP/1.1\r\nHost: {host}:{port}\r\nConnection: keep-alive\r\n\r\n'  #格式化字符串输出# 发送请求s.sendall(http_request.encode('utf-8'))# 接收响应头response = s.recv(4096)print(response.decode('utf-8'))# 接受真正的数据response = s.recv(4096)# 打印响应print(response.decode('utf-8'))# 关闭连接s.close()def get2():print("===========================================get2()=================================")my_data = ""# 创建 socket 对象s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 连接到服务器s.connect((host, port))# 非阻塞模式# s.setblocking(False)# s.settimeout(0)# 阻塞模式# s.setblocking(True)# s.settimeout(None)content = "source=python&key=value"# 构建 HTTP GET 请求 也可以发送数据体,但是一般不建议这样http_request = f'GET /show HTTP/1.1\r\n' \f'Host: {host}:{port}\r\n' \f'Content-Type: application/x-www-form-urlencoded\r\n' \f'Content-Length:{len(content)}\r\n' \f'Connection: close\r\n' \f'\r\n'  # 格式化字符串输出# GET请求发送数据可以直接拼接在路径头中。# 发送请求头s.sendall(http_request.encode('utf-8'))# 发送数据体s.sendall(content.encode("utf-8"))# time.sleep(1)  # 睡眠1秒钟,模拟粘包的现场result = ""# 死循环来接受数据while 1:response = s.recv(MAX_BUFFER_SIZE)# 找到Content-Length字段拿到真正的数据长度recive_data = response.decode("utf-8")print("recive_data = ", recive_data, "len = ", len(recive_data))result += recive_data  # 将请求拼接在result中# print("result = ",result)temp_len = result.find("Content-Length")print("temp_len =", temp_len)if temp_len != -1:start_pos = temp_len + len("Content-Length") + 2end_pos = result.find("\r", start_pos)# 找不到结束的位置就跳过继续接受数据if end_pos == -1:continueprint(f"start_pos = {start_pos},end_pos = {end_pos}")content_len_str = result[start_pos:end_pos]print("content_len_str = ", content_len_str)if int(content_len_str) >= MAX_BUFFER_SIZE:print("超出最大的content_len_str")return  "ERROR"# 一般\r\n\r\n是请求的结束标记。text_ind = result.find("\r\n\r\n", start_pos) + len("\r\n\r\n")if text_ind != -1:# 肯定一直都是成立的条件response_str = result[text_ind:]print(f"response_str = {response_str} ")if len(response_str) >= len(content_len_str):my_data = response_strs.close()return my_dataif (len(recive_data) < 0):s.close()elif len(recive_data) == 0:break# 关闭连接s.close()return my_datadef get3():print("===========================================get2()=================================")"""my_data = ""# 创建 socket 对象s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 连接到服务器s.connect((host, port))# 非阻塞模式# s.setblocking(False)# s.settimeout(0)# 阻塞模式# s.setblocking(True)# s.settimeout(None)content = "source=python&key=value"# 构建 HTTP GET 请求http_request = f'GET /show HTTP/1.1\r\n' \f'Host: {host}:{port}\r\n' \f'Content-Type: application/x-www-form-urlencoded\r\n' \f'Content-Length:{len(content)}\r\n' \f'Connection: close\r\n' \f'\r\n'  # 格式化字符串输出# GET请求发送数据可以直接拼接在路径头中。# 发送请求头s.sendall(http_request.encode('utf-8'))# 发送数据体s.sendall(content.encode("utf-8"))# time.sleep(1)  # 睡眠1秒钟,模拟粘包的现场result = ""# 死循环来接受数据# 等待 socket 变为可读while 1:ready = select.select([s], [], [], None)if ready[0]:response = s.recv(MAX_BUFFER_SIZE)print("response = ",response)# 关闭连接s.close()return my_data"""# 假设我们有一个 socket 对象 ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 连接到服务器(假设 host 和 port 已经定义)s.connect((host, port))content = "source=python&key=value"# 构建 HTTP GET 请求http_request = f'GET /show HTTP/1.1\r\n' \f'Host: {host}:{port}\r\n' \f'Content-Type: application/x-www-form-urlencoded\r\n' \f'Content-Length:{len(content)}\r\n' \f'Connection: close\r\n' \f'\r\n'  # 格式化字符串输出# GET请求发送数据可以直接拼接在路径头中。# 发送请求头s.sendall(http_request.encode('utf-8'))# 发送数据体s.sendall(content.encode("utf-8"))while 1:print("Waiting for data...")r, w, x = select.select([s], [], [])if s in r:print("Socket is readable.")# 读取数据data = s.recv(1024)print("Received:", data)if not data:print("The connection has been closed by the other party.")breakreturnif __name__ == '__main__':# get1()# get2()get3()

使用QT的HTTP进行发送请求

#include <QCoreApplication>
#include <QDebug>#include <QDebug>
#include <QtNetwork>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QTextCodec>
#include <QFile>
#include <QTextStream>
#include <QObject>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);qDebug()<< "start ============================";QUrl url = QUrl("http://www.baidu.com");url = QUrl("http://127.0.0.1:5000/show?source=qt");QNetworkAccessManager* manage = new QNetworkAccessManager;QObject::connect(manage, &QNetworkAccessManager::finished,[&](QNetworkReply *reply){qDebug()<<"manage finisned"<<reply;});QNetworkRequest req;req.setUrl(url);req.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("application/x-www-form-urlencoded"));//    QNetworkReply*reply =  manage->get(req);QNetworkReply*reply =manage->post(req,"name=zs&method=qtpost");qDebug()<<" reply = "<<reply;qDebug()<<"readall = "<<reply->readAll();QEventLoop loop;QObject::connect(reply,&QNetworkReply::finished,&loop,&QEventLoop::quit);QObject::connect(reply,&QNetworkReply::finished,[&](){int statusCode  = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();qDebug() << "statusCode:" << statusCode;qDebug()<<reply->readAll(); //使用http直接发数据的话直接relayAll就可以了qDebug()<<"header = "<<reply->header(QNetworkRequest::ContentTypeHeader);qDebug()<<reply->rawHeaderPairs(); //可以拿到所有的头部数据});loop.exec();qDebug()<<"end================================";return 0;
//    return a.exec();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/893403.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MFC程序设计(二)基于对话框编程

从现在开始&#xff0c;我们将以基于对话框的MFC应用程序来讲解MFC应用 向导生成基于对话框MFC应用程序 对话框是一种特殊类型的窗口&#xff0c;绝大多数Windows程序都通过对话框与用户进行交互。在Visual C中&#xff0c;对话框既可以单独组成一个简单的应用程序&#xff0…

Flink Gauss CDC:深度剖析存量与增量同步的创新设计

目录 设计思路 1.为什么不直接用FlinkCDC要重写Flink Gauss CDC 2.存量同步的逻辑是什么 2.1、单主键的切片策略是什么 2.2、​​​​​复合主键作切片&#xff0c;怎么保证扫描到所有的数据 3、增量同步的逻辑是什么 4、存量同步结束之后如何无缝衔接增量同步 5、下游…

idea新增java快捷键代码片段

最近在写一些算法题&#xff0c;有很多的List<List这种编写&#xff0c;想着能否自定义一下快捷键 直接在写代码输入&#xff1a;lli&#xff0c;即可看见提示

深度学习-91-大语言模型LLM之基于langchain的模型IO的提示模板

文章目录 1 Model的输入输出2 提示模板2.1 提示模板的特点2.2 提示模板的类型3 使用提示模板3.1 设置环境变量3.2 PromptTemplate提示模板3.2.1 通过from_template方法3.2.2 直接生成提示模板3.2.3 使用提示模板3.2.4 复用提示模板3.3 ChatPromptTemplate聊天提示模板3.3.1 通过…

stm8s单片机(二)外部中断实验

中断优先级 stm8的中断优先级不是固定不变的&#xff0c;stm8的中断分为硬件优先级与软件优先级&#xff1b;当多个中断发生时&#xff0c;cpu会先响应软件优先级高的中断&#xff0c;若软件优先级相同会先响应硬件优先级高的&#xff1b; 其中软件优先级有四个 /*** brief …

社区版Dify实现文生视频 LLM+ComfyUI+混元视频

社区版Dify实现文生视频 LLMComfyUI混元视频 一、 社区版Dify实现私有化混元视频效果二、为什么社区版Dify可以在对话框实现文生视频&#xff1f;LLMComfyUI混元视频 实现流程图&#xff08;重点&#xff09;1. 文生视频模型支持ComfyUI2. ComfyUI可以轻松导出API实现封装3. Di…

helm推送到harbor私有库--http: server gave HTTP response to HTTPS client

harbor私有库访问的是http模式 harbor 2.8版本以上可以存储helm镜像 docker镜像推送的时候需要docker端配置insecure-registries 发现helm推送只能在harbor部署的本机使用localhost才能推送成功&#xff0c;即 helm push xxx.tgz oci://localhost:80/library 使用helm pus…

transformers使用过程问题

transfomers新旧版本冲突&#xff0c;和accelerate、datasets、evaluate这些库直接也经常会发生冲突 我使用了下面的版本&#xff0c;暂时没有冲突&#xff0c;如果有冲突再更新 transformers4.41.2 datasets2.20.0 accelerate0.31.0 evaluate0.4.2pip install transformers安…

svn tag

一般发布版本前&#xff0c;需要在svn上打个tag。步骤如下&#xff1a; 1、空白处右击&#xff0c;选择TortoiseSVN->Branch/tag; 2、填写To path&#xff0c;即tag的路基以及tag命名&#xff08;一般用版本号来命名&#xff09;&#xff1b;填写tag信息&#xff1b;勾选cr…

【JavaSE】(8) String 类

一、String 类常用方法 1、构造方法 常用的这4种构造方法&#xff1a;直接法&#xff0c;或者传参字符串字面量、字符数组、字节数组。 在 JDK1.8 中&#xff0c;String 类的字符串实际存储在 char 数组中&#xff1a; String 类也重写了 toString 方法&#xff0c;所以可以直…

【理解工具调用的流程,本质体现了大模型智能性】

1、工具调用 调用完结果看里面tool_calls 是否为空&#xff0c;不为空就调用工具函数处理&#xff0c; 如果为空就中断循环。大模型返回的message结果智能判断是否继续调用 输入输出如下&#xff1a; 请输入&#xff1a;深圳西安天气 ------------------------------------…

excel实用工具

持续更新… 文章目录 1. 快捷键1.1 求和 2. 命令2.1 查找 vloopup 1. 快捷键 1.1 求和 windows: alt mac : command shift T 2. 命令 2.1 查找 vloopup vlookup 四个入参数 要查找的内容 &#xff08;A2 6xx1&#xff09;查找的备选集 &#xff08;C2:C19&#xff09;…

【C++】模板(进阶)

本篇我们来介绍更多关于C模板的知识。模板初阶移步至&#xff1a;【C】模板&#xff08;初阶&#xff09; 1.非类型模板参数 1.1 非类型模板参数介绍 模板参数可以是类型形参&#xff0c;也可以是非类型形参。类型形参就是我们目前接触到的一些模板参数。 //类型模板参数 …

一文学会YOLO系列算法(从V3到11)实现遥感图像目标检测

目录 前言 数据集介绍 数据集转换 YOLO代码的下载 YOLO的配置 1.数据集的配置 2.模型的配置 YOLO11模型的训练 其它版本YOLO的训练 前言 遥感技术的快速发展&#xff0c;特别是在高分辨率遥感图像的获取能力上的显著提升&#xff0c;已经大大拓宽了遥感数据在环境监测…

图解Git——分布式Git《Pro Git》

分布式工作流程 Centralized Workflow&#xff08;集中式工作流&#xff09; 所有开发者都与同一个中央仓库同步代码&#xff0c;每个人通过拉取、提交来合作。如果两个开发者同时修改了相同的文件&#xff0c;后一个开发者必须在推送之前合并其他人的更改。 Integration-Mana…

【高阶数据结构】布隆过滤器(BloomFilter)

1. 概念 1.1 背景引入 背景&#xff1a;在计算机软件中&#xff0c;一个常见的需求就是 在一个集合中查找一个元素是否存在 &#xff0c;比如&#xff1a;1. Word 等打字软件需要判断用户键入的单词是否在字典中存在 2. 浏览器等网络爬虫程序需要保存一个列表来记录已经遍历过…

【json_object】mysql中json_object函数过长,显示不全

问题&#xff1a;json只显示部分 解决&#xff1a; SET GLOBAL group_concat_max_len 1000000; -- 设置为1MB&#xff0c;根据需要调整如果当前在navicat上修改&#xff0c;只有效本次连接和后续会话&#xff0c;重新连接还是会恢复默认值1024 在my.ini配置文件中新增或者修…

计算机网络 (52)秘钥分配

一、重要性 在计算机网络中&#xff0c;密钥分配是密钥管理中的一个核心问题。由于密码算法通常是公开的&#xff0c;因此网络的安全性主要依赖于密钥的安全保护。密钥分配的目的是确保密钥在传输过程中不被窃取或篡改&#xff0c;同时确保只有合法的用户才能获得密钥。 二、方…

第35天:安全开发-JavaEE应用原生反序列化重写方法链条分析触发类类加载

时间轴&#xff1a; 序列化与反序列化图解&#xff1a; 演示案例&#xff1a; Java-原生使用-序列化&反序列化 Java-安全问题-重写方法&触发方法 Java-安全问题-可控其他类重写方法 Java-原生使用-序列化&反序列化 1.为什么进行序列化和反序列化&#xff1…

MindAgent:基于大型语言模型的多智能体协作基础设施

2023-09-18 &#xff0c;加州大学洛杉矶分校&#xff08;UCLA&#xff09;、微软研究院、斯坦福大学等机构共同创建的新型基础设施&#xff0c;目的在评估大型语言模型在游戏互动中的规划和协调能力。MindAgent通过CuisineWorld这一新的游戏场景和相关基准&#xff0c;调度多智…