当前位置: 首页 > news >正文

手写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();
}

相关文章:

手写SOCKET进行HTTP通信

网络基础 我们电脑主板上都内置了多种网卡&#xff0c;一般主要有以下几类&#xff1a; 虚拟网卡&#xff08;loopback&#xff09; 注意&#xff0c;它是虚拟的&#xff0c;并不是物理网卡&#xff0c;也被称为是本地环回地址(或接口)&#xff0c;一般将127.0.0.1作为本地环回…...

深入理解 Java 的并发容器

目录 一、为何需要并发容器 二、Java 中的主要并发容器 1. ConcurrentHashMap 2. CopyOnWriteArrayList 3. ConcurrentLinkedQueue 4. BlockingQueue及其实现类 三、并发容器的应用场景 1. 缓存系统 2. 任务队列 3. 数据共享与传递 四、使用并发容器的注意事项 1. …...

四、CSS效果

一、box-shadow box-shadow:在元素的框架上添加阴影效果 /* x 偏移量 | y 偏移量 | 阴影颜色 */ box-shadow: 60px -16px teal; /* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影颜色 */ box-shadow: 10px 5px 5px black; /* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影扩散半…...

每日OJ_牛客_DP44兑换零钱_C++_Java

目录 牛客_DP44兑换零钱 题目解析 C代码 Java代码 牛客_DP44兑换零钱 兑换零钱_牛客题霸_牛客网 描述&#xff1a; 给定数组arr&#xff0c;arr中所有的值都为正整数且不重复。每个值代表一种面值的货币&#xff0c;每种面值的货币可以使用任意张&#xff0c;再给定一个a…...

Linux——入门基本指令汇总

目录 1. ls指令2. pwd3. whoami指令4. cd指令5. clear指令6. touch指令7. mkdir指令8. rm指令9. man指令10. cp指令11. mv指令12. cat指令13. tac指令14. more指令15. less指令16. head指令17. tail指令18. date指令19. cal指令20. find指令21. which指令22. alias指令23. grep…...

VOSK实现【离线中文语音】识别

Vosk是一款开源的离线语音识别工具包&#xff0c;具有以下功能&#xff1a; 多语言支持&#xff1a;能够对20多种语言和方言进行语音识别&#xff0c;如中文、英语、德语、法语、西班牙语等&#xff0c;可满足不同用户的语言需求。 模型轻量化&#xff1a;每种语言的模型大小仅…...

Qt 控件与布局管理

1. Qt 控件的父子继承关系 在 Qt 中&#xff0c;继承自 QWidget 的类&#xff0c;通常会在构造函数中接收一个 parent 参数。 这个参数用于指定当前空间的父控件&#xff0c;从而建立控件间的父子关系。 当一个控件被设置为另一控件的子控件时&#xff0c;它会自动成为该父控…...

Checkbutton组件

在这个案例中,我们将添加三个复选框(Checkbutton)组件,每个组件都有不同的初始状态。 准备就绪 本文案例是在上一篇《Combobox组件》上的扩展,你可以从这里下载其中的代码。 如何操作 我们正在创建三个状态不同的复选框组件。 第一个组件是禁用状态,并且其中有一个勾…...

Markdown Viewer 浏览器, vscode

使用VS Code插件打造完美的MarkDown编辑器&#xff08;插件安装、插件配置、markdown语法&#xff09;_vscode markdown-CSDN博客 右键 .md 文件&#xff0c;选择打开 方式 &#xff08;安装一些markdown的插件) vscode如何预览markdown文件 | Fromidea GitCode - 全球开发者…...

【QNX】QNX侧查看CPU的信息

目录 一 工具 ① top ▲ 使用top查看CPU信息 ▲ 输出 ② hogs ▲ 使用hogs查看CPU信息 ▲ 输出 ③ pidin ▲ 使用pidin查看CPU信息 ▲ 输出 二 对比 在QNX实时操作系统中&#xff0c;可查看CPU信息的方法有top、hogs以及pidin。 一 工具 ① top top命令不仅能够显…...

Android中关于View的几种属性赋值方式

我们以给TextView组件设置颜色属性展开讲解 1、xml中直接定义&#xff08;设定TextView为黑色&#xff09; 2、xml 中 引用style&#xff08;设定TextView为蓝色&#xff09; 3、在theme 中直接定义&#xff08;设定TextView紫色&#xff09; 4、在主题中添加对样式资源的引用…...

JavaScript网页基于tesseract.js提取图片中的文字,识别车牌,识别快递单号等

Tesseract是我们的老盆友了&#xff0c;以前写过基于JavaCV版本的Tesseract字符识别&#xff0c;现在tesseract终于可以在网页上面用了&#xff08;tesseract.js&#xff09;&#xff0c;让我们一起来试试效果。 注意&#xff1a;本章使用Vue模块化调用tesseract.js方式&#x…...

智慧金融合集:财税资金数据管理一体化大屏

随着科技的快速进步和数字化转型的加速&#xff0c;金融、税务等机构和企业面临的数据量呈现出爆炸式增长。传统的数据分析方法早已无法胜任现代业务的需求。为此&#xff0c;许多机构开始尝试创新的软件工具来更好的管理繁琐的数据。 通过图扑软件的数据可视化大屏&#xff0c…...

Android SystemUI——最近任务应用列表(十七)

对于最近任务应用列表来说,在 Android 原生 SystemUI 中是一个单独的组件。 <string-array name="config_systemUIServiceComponents" translatable="false">……<item>com.android.systemui.recents.Recents</item> </string-arra…...

工业相机 SDK 二次开发-Halcon 插件

本文介绍了 Halcon 连接相机时插件的使用。通过本套插件可连接海康 的工业相机。 一. 环境配置 1. 拷贝动态库 在 用 户 安 装 MVS 目 录 下 按 照 如 下 路 径 Development\ThirdPartyPlatformAdapter 找到目录为 HalconHDevelop 的文 件夹&#xff0c;根据 Halcon 版本找到对…...

AI Agent:数字文明的暗物质,如何悄然改变我们的世界?

AI Agent&#xff1a;数字文明的暗物质&#xff0c;如何悄然改变我们的世界&#xff1f; 引言 在人类文明的漫长黑夜中&#xff0c;人工智能&#xff08;Artificial Intelligence, AI&#xff09;如同第一缕曙光&#xff0c;正在撕裂数字与现实的边界。这是一个技术奇点临近的…...

vscode 自用插件

vscode按住ctrl鼠标左键无法跟踪跳转方法名&#xff0c;装这些插件就可以 vscode-elm-jump:常规的代码跳转定义 Vue CSS Peek:跳转css定义 vue-helper:变量函数只跳转定义 Vetur 代码提示 Baidu Comate 自动帮你写console.log Turbo Console Log: ctrl alt l 选中变量之后&am…...

Ragas-RAG能力评测

Ragas是一个框架&#xff0c;它可以帮助你从不同的方面评估你的问答&#xff08;QA&#xff09;流程。它为你提供了一些指标来评估你的问答系统的不同方面&#xff0c;具体包括&#xff1a; 评估检索&#xff08;context&#xff09;的指标&#xff1a;提供了上下文相关性&…...

【PCL】Segmentation 模块—— 条件欧几里得聚类(Conditional Euclidean Clustering)

1、简介 1.1 条件欧几里得聚类&#xff08;Conditional Euclidean Clustering&#xff09; 本文介绍了如何使用 pcl::ConditionalEuclideanClustering 类&#xff1a;这是一种基于欧几里得距离和用户自定义条件的点云聚类分割算法。 该类使用了与欧几里得聚类提取(Euclidean…...

#HarmonyOS篇:build-profile.json5里面配置productsoh-package.json5里面dependencies依赖引入

oh-package.json5 用于描述包名、版本、入口文件和依赖项等信息。 {"license": "","devDependencies": {},"author": "","name": "entry","description": "Please describe the basic…...

《探秘:人工智能如何为鸿蒙Next元宇宙网络传输与延迟问题破局》

在元宇宙的宏大愿景中&#xff0c;流畅的网络传输和低延迟是保障用户沉浸式体验的关键。鸿蒙Next结合人工智能技术&#xff0c;为解决这些问题提供了一系列创新思路和方法。 智能网络监测与预测 人工智能可以实时监测鸿蒙Next元宇宙中的网络状况&#xff0c;包括带宽、延迟、…...

java中的泛型

文章目录 java中的泛型泛型的使用1 快速入门2 泛型的介绍&#xff08;1&#xff09;使用泛型的好处&#xff08;2&#xff09;泛型的理解&#xff08;3&#xff09;泛型的语法&#xff08;4&#xff09;泛型使用的注意事项 3 自定义泛型&#xff08;1&#xff09;自定义泛型类&…...

PCF8563一款工业级、低功耗多功能时钟/日历芯片

PCF8563是PHILIPS&#xff08;现NXP&#xff09;公司生产的一款工业级、内含I2C总线接口功能的低功耗多功能时钟/日历芯片。以下是对该芯片的详细介绍&#xff1a; 一、主要特性 低功耗&#xff1a;典型值为0.25μA&#xff08;VDD3.0V&#xff0c;Tamb25℃&#xff09;。宽电…...

Servlet快速入门

Servlet 由于目前主流使用SpringBoot进行开发Servlet可以说是时代的眼泪&#xff0c;这篇文章主要介绍我基于SpringBoot对应Servlet的浅薄认知&#xff0c;有利于更好的理解前端界面和java服务器的数据交换过程 快速入门 我比较推荐这篇文章来对Servlet有一个大概的了解 都2…...

C语言初阶牛客网刷题——JZ17 打印从1到最大的n位数【难度:入门】

1.题目描述 牛客网OJ题链接 题目描述&#xff1a; 输入数字 n&#xff0c;按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3&#xff0c;则打印出 1、2、3 一直到最大的 3 位数 999。 用返回一个整数列表来代替打印n 为正整数&#xff0c;0 < n < 5 示例1 输入&…...

【组件分享】商品列表组件-最佳实践

商品列表组件 商品列表组件用于展示商品信息列表&#xff0c;支持多种布局方式和自定义配置。 基础用法 <template><ProGoodsList :goods-list"goodsList" :layout"layout" item-click"handleItemClick" /> </template>&l…...

【JAVA】BOSS系统发版艺术:构建高效、优雅的微服务部署策略

在现代软件开发领域&#xff0c;微服务架构与容器化部署已迅速成为行业新趋势。微服务架构通过将应用拆分成多个小型、自治的服务单元&#xff0c;每个服务承担某项特定的业务功能。而容器化部署则以其轻量级和高度可移植的特性&#xff0c;为这些微服务的有效打包、分发和运行…...

React的响应式

在 React 中&#xff0c;useState 是一个 Hook&#xff0c;用于在函数组件中定义和管理状态。 setCount 是由 useState 返回的第二个值&#xff0c;用于更新状态并触发组件重新渲染。它的本质是一个状态更新函数&#xff0c;背后是 React 的状态管理和调度机制。下面是对 setCo…...

deep face cam 部署报错解决

这里写自定义目录标题 使用conda创建py3.9环境使用按照readme.txt安装所有依赖后遇到的报错2.最后一个模块insightface安装报错3.运行run.py又报错原因:解决方法:4.缺少模块解决方法:pip命令安装5.AttributeError: NoneType object has no attribute configure解决方法:安装…...

图像处理基础(4):高斯滤波器详解

本文主要介绍了高斯滤波器的原理及其实现过程 高斯滤波器是一种线性滤波器&#xff0c;能够有效的抑制噪声&#xff0c;平滑图像。其作用原理和均值滤波器类似&#xff0c;都是取滤波器窗口内的像素的均值作为输出。其窗口模板的系数和均值滤波器不同&#xff0c;均值滤波器的…...

outlook附件限制最大5m如何解决

Outlook 附件大小限制为 5MB&#xff0c;通常由邮件服务器&#xff08;如 Exchange、Office 365、Gmail 等&#xff09;或本地 Outlook 配置决定。可以采取以下几种方法来解决该限制问题&#xff1a; 解决方案 1&#xff1a;调整服务器端限制&#xff08;管理员权限&#xff09…...

Word常见问题:嵌入图片无法显示完整

场景&#xff1a;在Word中&#xff0c;嵌入式图片显示不全&#xff0c;一部分图片在文字下方。如&#xff1a; 问题原因&#xff1a;因段落行距导致 方法一 快捷方式 选中图片&#xff0c;通过"ctrl1"快捷调整为1倍行距 方法二 通过工具栏调整 选中图片&#xff0…...

【面试题】java基础概念

以下是关于这道面试题的回答&#xff0c;包括JDK中一些相关概念的区别以及JIT的原理&#xff1a; JDK中相关概念区别 JDK、JRE和JVM JDK&#xff08;Java Development Kit&#xff09;&#xff1a;是Java开发工具包&#xff0c;它包含了JRE和一系列开发工具。JDK提供了编译、…...

WebSocket实现私聊私信功能

目录 后端pom.xmlConfig配置类Controller类DTO 前端安装相关依赖websocketService.js接口javascripthtmlCSS 效果展示简单测试连接&#xff1a; 报错解决方法1、vue3 使用SockJS报错 ReferenceError: global is not defined 后面将继续完善&#xff0c;待更新... 后端 pom.xml…...

进程的哪些内存类型容易引起内存泄漏

相信你在平时的工作中&#xff0c;应该遇到过下面这些场景&#xff1a; 伴随着服务器中的后台任务持续地运行&#xff0c;系统中可用内存越来越少&#xff1b; 应用程序正在运行时忽然被 OOM kill 掉了&#xff1b; 进程看起来没有消耗多少内存&#xff0c;但是系统内存就是不够…...

用着很顺手的电脑亮度随心随意调节

一、功能介绍 显示高级设置&#xff0c;可以调节屏幕RGB色彩。 娱乐亮度&#xff0c;一键娱乐亮度调节。 护眼亮度&#xff0c;保护眼睛&#xff0c;减少蓝光。 恢复正常&#xff0c;一键恢复到默认模块。 二、问题解答 1、亮度更改后显示器无变化&#xff01;软件根本都没…...

图片生成Prompt编写技巧

1. 图片情绪&#xff08;场景氛围&#xff09; 一张图片一般都会有一个情绪基调&#xff0c;因为作画本质上也是在传达一些情绪&#xff0c;一般都会借助图片的氛围去转达。例如&#xff1a;比如家庭聚会一般是欢乐、喜乐融融。断壁残垣一般是悲凉。还有萧瑟、孤寂等。 2.补充细…...

博客之星2024年度总评选——我的年度创作回顾与总结

2024年&#xff0c;是我在CSDN博客上持续耕耘、不断成长的一年。在此&#xff0c;与大家分享一下我的年度创作回顾与总结。 一、创作成长与突破 在人工智能领域&#xff0c;技术迭代迅速&#xff0c;知识更新频繁。为了保持自己的竞争力&#xff0c;在今年&#xff0c;我始终…...

前端Vue2项目使用md编辑器

项目中有一个需求&#xff0c;要在前端给用户展示内容&#xff0c;内容有 AI 生成的&#xff0c;返回来的是 md 格式&#xff0c;所以需要给用户展示 md 格式&#xff0c;并且管理端也可以编辑这个 md 格式的文档。 使用组件库 v-md-editor。 https://code-farmer-i.github.i…...

Docker核心命令与Yocto项目的高效应用

随着软件开发逐渐向分布式和容器化方向演进&#xff0c;Docker 已成为主流的容器化技术之一。它通过标准化的环境配置、资源隔离和高效的部署流程&#xff0c;大幅提高了开发和构建效率。Yocto 项目作为嵌入式 Linux 系统构建工具&#xff0c;与 Docker 的结合进一步增强了开发…...

kong 网关和spring cloud gateway网关性能测试对比

该测试只是简单在同一台机器设备对spring cloud gateway网关和kong网关进行对比&#xff0c;受限于笔者所拥有的资源&#xff0c;此处仅做简单评测。 一、使用spring boot 的auth-service作为服务提供者 该服务提供了一个/health接口&#xff0c;接口返回"OK"&…...

DDoS攻击防护能力测试:Python版脚本

引言 在互联网服务日益复杂和流量持续增长的今天&#xff0c;确保服务器能够应对高并发请求并具备良好的抗DDoS攻击的能力至关重要。声明以下代码仅在合法的前提下使用。 工具设计原理 脚本的核心在于它能够创建多个线程来并发执行不同的攻击方法&#xff0c;从而实现对目标…...

白玉微瑕:闲谈 SwiftUI 过渡(Transition)动画的“口是心非”(下)

概述 秃头小码农们都知道&#xff0c;SwiftUI 不仅仅是一个静态 UI 构建框架那么简单&#xff0c;辅以海量默认或自定义的动画和过渡&#xff08;Transition&#xff09;特效&#xff0c;我们可以将 App 界面的绚丽升华到极致。 不过&#xff0c;目前 SwiftUI 中的过渡&#x…...

5.4 解锁 OpenAI - Translator:模块设计,构建翻译系统的 “基石”

OpenAI-Translator 模块设计 OpenAI-Translator 作为一款基于 OpenAI GPT 模型的智能翻译助手,其模块设计至关重要。为了保证翻译的高效性、准确性与可扩展性,系统需要一个结构清晰、功能强大的模块化设计。本文将对 OpenAI-Translator 的各个模块进行详细解析,涵盖其核心功…...

数据分析 变异系数

目录 变异系数的应用场景包括&#xff1a; 特点&#xff1a; 注意事项&#xff1a; np.nanvar——方差&#xff0c;np.sanstd标准差 简单来讲就是平均值/标准差 变异系数&#xff08;Coefficient of Variation, CV&#xff09;是一种相对量的变异指标&#xff0c;常用于衡…...

C语言——编译与链接

目录 前言 一程序的两种环境 1翻译环境 2执行环境 二预处理 1预处理符号 2#define 2.1#define 定义标识符 2.2#define 定义宏 2.2.1#和## 2.3带副作用的宏参数 2.4宏和函数的比较 2.5命名约定 3#undef 4命令行定义 5条件编译 5.1单分支 5.2多分支 5.3判断是…...

NewStar CTF week1 web wp

谢谢皮蛋 做这题之前需要先去学习一些数据库的知识 1 order by 2 1可以理解为输入的id&#xff0c;是一个占位符&#xff0c;按第二列排序用来测试列数&#xff0c;如果没有两列则会报错-1 union select 1,2 -1同样是占位符&#xff0c;union的作用是将注入语句合并到原始语句…...

Android BitmapShader简洁实现马赛克,Kotlin(一)

Android BitmapShader简洁实现马赛克&#xff0c;Kotlin&#xff08;一&#xff09; 这一篇&#xff0c; Android使用PorterDuffXfermode模式PorterDuff.Mode.SRC_OUT橡皮擦实现马赛克效果&#xff0c;Kotlin&#xff08;3&#xff09;-CSDN博客 基于PorterDuffXfermode实现马…...

NavVis手持激光扫描帮助舍弗勒快速打造“数字孪生”工厂-沪敖3D

在全球拥有近100家工厂的舍弗勒&#xff0c;从2016年开启数字化运营进程&#xff0c;而当前制造、库存、劳动力和物流的数字化&#xff0c;已无法支持其进一步简化工作流程&#xff0c;亟需数字化物理制造环境&#xff0c;打造“数字孪生”工厂。 NavVis为其提供NavVis VLX 3…...

web服务器 网站部署的架构

WEB服务器工作原理 Web web是WWW(World Wide Web)的简称&#xff0c;基本原理是&#xff1a;请求(客户端)与响应(服务器端)原理&#xff0c;由遍布在互联网中的Web服务器和安装了Web浏览器的计算机组成 客户端发出请求的方式&#xff1a;地址栏请求、超链接请求、表单请求 …...