使用Nordic的nrf52832控制指定从机(一主多从)

news2024/11/15 21:50:34

一主多从

  • 1. 想要实现的功能
  • 2. 从机
  • 3. 主机
    • 3.1 主从机连接个数设置
    • 3.2 扫描过滤
    • 3.3 连接和断开连接
    • 3.4 按键处理
    • 3.5 从机读写
      • 3.5.1 写
      • 3.5.1 读
  • 4运行效果

1. 想要实现的功能

1.主机能连接多个从机(主机作为控制器,从机作为节点)。
2.主机能使用不同的按键控制不同的节点(按键和节点一一对应,与从机的连接顺序无关)。
3.主机扫描过滤器使用设备全称,记录和从机的连接句柄,并进行控制。

2. 从机

从机使用例程:nRF5_SDK_17.1.0_ddde560\examples\ble_peripheral\ble_app_blinky\pca10040\s132\arm5_no_packs
以使用两个从机为例,从机的蓝牙名称分别改为Nordic_BedRoom和Nordic_DrawingRoom。

3. 主机

主机使用例程:nRF5_SDK_17.1.0_ddde560\examples\ble_central\ble_app_multilink_central\pca10040\s132\arm5_no_packs

如果不记录连接句柄的话从机连接顺序的不同会导致不能控制指定的从机,所以.h文件需要定义一些自己需要的内容:

#ifndef _MY_BLE_DEVIVE_
#define _MY_BLE_DEVIVE_

typedef enum{
	INIT = 1,
	SCAN,
	CONN,
	READ,
	WRITE,
	DISCONN,
}PROC_TYPE;

typedef enum{
	bedroom_device = 0,			//从机1,由主机的按键1控制
	drawingroom_device,			//从机2,由主机的按键2控制
	init_device,				//无效,初始化句柄用
}control_device;

typedef struct{
	bool led_state;				//led状态
	uint8_t	my_scan_state :1;	//扫描到从机
	uint8_t	my_conn_state :1;	//连接到从机
	uint16_t my_conn_handle;	//连接句柄
}my_conn_record;				//从机记录结构体

void my_device_manage(control_device dev,PROC_TYPE type,uint16_t handle);	//从机操作管理函数

#endif

.c文件中定义:

BLE_LBS_C_ARRAY_DEF(m_lbs_c, NRF_SDH_BLE_CENTRAL_LINK_COUNT);

#define BedRoom_BUTTON				BSP_BUTTON_0	//主机按键1控制从机1LED状态
#define DrawingRoom_BUTTON          BSP_BUTTON_1 	//主机按键2控制从机2LED状态
#define BedRoomRead_BUTTON			BSP_BUTTON_2	//主机按键3读取从机1LED状态
#define DrawingRoomRead_BUTTON      BSP_BUTTON_3	//主机按键4读取从机2LED状态
my_conn_record	conn_record[NRF_SDH_BLE_CENTRAL_LINK_COUNT];	//从机记录结构体数组
																//conn_record结构体数组的顺序同control_device中枚举的设备顺序一致

void BedRoomProcessFunc(PROC_TYPE type)	//从机1处理函数
{
	ret_code_t err_code;
	switch(type)
	{
		case READ:
			led_status_read_from_device(bedroom_device);
			if (err_code == NRF_SUCCESS)
			{
				NRF_LOG_INFO("Read BedRoom LED state");
			}
			break;
		case WRITE:
			{
				conn_record[bedroom_device].led_state = !conn_record[bedroom_device].led_state;
				err_code = led_status_send_to_device(bedroom_device,conn_record[bedroom_device].led_state);
				if (err_code == NRF_SUCCESS)
				{
					NRF_LOG_INFO("BedRoom write LED state %d", conn_record[bedroom_device].led_state);
				}
			}break;
		default:
			break;
	}	
}

void DrawingRoomProcessFunc(PROC_TYPE type)	//从机2处理函数
{
	ret_code_t err_code;
	switch(type)
	{
		case READ:
			err_code = led_status_read_from_device(drawingroom_device);
			if (err_code == NRF_SUCCESS)
			{
				NRF_LOG_INFO("Read DrawingRoom LED state");
			}
			break;
		case WRITE:
			{
				conn_record[drawingroom_device].led_state = !conn_record[drawingroom_device].led_state;
				err_code = led_status_send_to_device(drawingroom_device,conn_record[drawingroom_device].led_state);
				if (err_code == NRF_SUCCESS)
				{
					NRF_LOG_INFO("DrawingRoom write LED state %d", conn_record[drawingroom_device].led_state);
				}
			}break;
		default:
			break;
	}	
}

void (*ProcessFunc[2])(PROC_TYPE type) =	//从机处理函数列表
{
	BedRoomProcessFunc,
	DrawingRoomProcessFunc,
};

void my_device_manage(control_device dev,PROC_TYPE type,uint16_t handle)	//从机操作管理函数
{
	switch(type)
	{
		case INIT:	//初始化,将conn_record结构体数组的句柄初始化为BLE_CONN_HANDLE_INVALID(0xFFFF)
			for (uint32_t i = 0; i< NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
			{
				conn_record[i].my_conn_handle = handle;
			}
			break;
		case SCAN:	//记录哪个从机被主机扫描到
			conn_record[dev].my_scan_state = true;
			conn_record[dev].my_conn_state = false;
			conn_record[dev].my_conn_handle = handle;
			break;
		case CONN:	//记录哪个从机被主机连接
			conn_record[dev].my_scan_state = false;
			conn_record[dev].my_conn_state = true;
			conn_record[dev].my_conn_handle = handle;
			break;
		case READ:
		case WRITE:	//从机状态读写处理
			if((conn_record[dev].my_conn_state)&&(conn_record[dev].my_conn_handle != BLE_CONN_HANDLE_INVALID))
			{
				ProcessFunc[dev](type);
			}
			else
			{
				NRF_LOG_INFO("%s device error", m_target_periph_name[dev]);
			}
			break;
		case DISCONN:	//记录哪个从机断开,并将其连接句柄设置为BLE_CONN_HANDLE_INVALID(0xFFFF)
			conn_record[dev].my_scan_state = false;
			conn_record[dev].my_conn_state = false;
			conn_record[dev].my_conn_handle = handle;
			break;
		default:
			break;
	}
}

主函数中执行my_device_manage()函数把conn_record数组成员的连接句柄my_conn_handle 全部设置为BLE_CONN_HANDLE_INVALID(0xFFFF)。my_device_manage()函数中状态机除了CONN都会把连接句柄设置为0xFFFF,目的是未连接的从机不对其进行读写,第一个从机连接后连接句柄会是0,这样的话就会出现在读写从机时使用conn_record中记录的连接句柄匹配m_lbs_c中的句柄时错误的匹配到0。
在这里插入图片描述

3.1 主从机连接个数设置

宏定义:
在这里插入图片描述
使用一个主机,两个从机,所以主机要支持两路连接,NRF_SDH_BLE_CENTRAL_LINK_COUNT设置为2。最大支持20路连接,也就是可以连接20个从机,在ble_gap.h中有相关定义,支持的最大角色数目是20(主机连接个数+从机连接个数)。
在这里插入图片描述

3.2 扫描过滤

由于两个从机的设备名称不一样,例程中扫描过滤器使用的是设备全称过滤,需要将设备名称改为从机的名称:

static char const m_target_periph_name[NRF_SDH_BLE_CENTRAL_LINK_COUNT][NRF_BLE_SCAN_NAME_MAX_LEN] = 
{{"Nordic_BedRoom"},{"Nordic_DrawingRoom"}};

要通过设备名称过滤的从机数:
在这里插入图片描述
在scan_init()函数中设置扫描过滤器:
在这里插入图片描述
nrf_ble_scan_filter_set()函数中调用nrf_ble_scan_name_filter_add()函数,里面会判断设备名称是否添加过滤器,没有添加的话就添加。函数中scan_filters.name_filter.name_cnt为已经添加过滤器的数量,scan_filters.name_filter.target_name[index]为具体的要连接的从机设备名称,过滤器添加顺序与control_device中枚举的从机设备顺序一致,因此也与conn_record结构体数组的从机设备顺序一致。

static ret_code_t nrf_ble_scan_name_filter_add(nrf_ble_scan_t * const p_scan_ctx,
                                               char           const * p_name)
{
    uint8_t   index;
    uint8_t * counter  = &p_scan_ctx->scan_filters.name_filter.name_cnt;
    uint8_t   name_len = strlen(p_name);

    // Check the name length.
    if ((name_len == 0) || (name_len > NRF_BLE_SCAN_NAME_MAX_LEN))
    {
        return NRF_ERROR_DATA_SIZE;
    }

    // If no memory for filter.
    if (*counter >= NRF_BLE_SCAN_NAME_CNT)
    {
        return NRF_ERROR_NO_MEM;
    }

    // Check for duplicated filter.
    for (index = 0; index < NRF_BLE_SCAN_NAME_CNT; index++)
    {
        if (!strcmp(p_scan_ctx->scan_filters.name_filter.target_name[index], p_name))
        {
            return NRF_SUCCESS;
        }
    }

    // Add name to filter.
    memcpy(p_scan_ctx->scan_filters.name_filter.target_name[(*counter)++],
           p_name,
           strlen(p_name));

    NRF_LOG_DEBUG("Adding filter on %s name", p_name);

    return NRF_SUCCESS;
}

scan_start()函数简单做一下修改:
在这里插入图片描述

主机扫描到从机后生成事件调用函数nrf_ble_scan_on_ble_evt(),然后执行函数nrf_ble_scan_on_adv_report(),会根据扫描过滤器设置去判断是不是要连接的从机。
在这里插入图片描述
在adv_name_compare函数中会按顺序比对扫描到的从机是不是要连接的从机,那么也就可以使用自己写的my_device_manage函数来记录哪个要连接的从机被扫描到了。

static bool adv_name_compare(ble_gap_evt_adv_report_t const * p_adv_report,
                             nrf_ble_scan_t     const * const p_scan_ctx)
{
    nrf_ble_scan_name_filter_t const * p_name_filter = &p_scan_ctx->scan_filters.name_filter;
    uint8_t                            counter       =
        p_scan_ctx->scan_filters.name_filter.name_cnt;
    uint8_t  index;
    uint16_t data_len;

    data_len = p_adv_report->data.len;

    // Compare the name found with the name filter.
    for (index = 0; index < counter; index++)
    {
        if (ble_advdata_name_find(p_adv_report->data.p_data,
                                  data_len,
                                  p_name_filter->target_name[index]))
        {
			my_device_manage((control_device)index,SCAN,BLE_CONN_HANDLE_INVALID);//记录哪个要连接的从机被扫描到
            return true;
        }
    }

    return false;
}

3.3 连接和断开连接

在主从机连接或者断开连接的时候会产生相应的事件,在ble_evt_handler()函数中也进行连接句柄的记录:

        case BLE_GAP_EVT_CONNECTED:
        {
            /*其他
            */
			for (uint32_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
			{
				if((conn_record[i].my_scan_state == true)&&(conn_record[i].my_conn_state == false))
				{
					my_device_manage((control_device)i,CONN,p_gap_evt->conn_handle);
				}
			}
            /*其他
            */
        } break; // BLE_GAP_EVT_CONNECTED
        case BLE_GAP_EVT_DISCONNECTED:
        {
            /*其他
            */
			for (uint32_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
			{
				if((p_gap_evt->conn_handle == conn_record[i].my_conn_handle)&&(conn_record[i].my_conn_handle != BLE_CONN_HANDLE_INVALID))
				{
					my_device_manage((control_device)i,DISCONN,BLE_CONN_HANDLE_INVALID);
				}
			}
            /*其他
            */
        } break;

3.4 按键处理

按键初始化:
在这里插入图片描述
按键事件处理函数,只在按键按下时动作:

static void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
    switch (pin_no)
    {
        case BedRoom_BUTTON://控制从机1LED状态
			if(button_action)
			{
				my_device_manage(bedroom_device,WRITE,BLE_CONN_HANDLE_INVALID);
			}
            break;
        case DrawingRoom_BUTTON://控制从机2LED状态
			if(button_action)
			{
				my_device_manage(drawingroom_device,WRITE,BLE_CONN_HANDLE_INVALID);
			}
            break;
        case BedRoomRead_BUTTON://读取从机1LED状态
			if(button_action)
			{
				my_device_manage(bedroom_device,READ,BLE_CONN_HANDLE_INVALID);
			}
            break;
        case DrawingRoomRead_BUTTON://读取从机2LED状态
			if(button_action)
			{
				my_device_manage(drawingroom_device,READ,BLE_CONN_HANDLE_INVALID);
			}
            break;
        default:
            APP_ERROR_HANDLER(pin_no);
            break;
    }
}

3.5 从机读写

在my_device_manage()函数中对从机读写之前会判断是不是处于连接状态,连接句柄是不是有效:
在这里插入图片描述
如果有效就执行相应的从机处理函数,以从机1的处理函数为例:
在这里插入图片描述

3.5.1 写

由于从机1模拟的是LED控制器,所以在写之前加了一行状态翻转,然后调用ble_lbs_led_status_send函数来对从机写。conn_record结构体数组的顺序是从机1(bedroom_device)、从机2(drawingroom_device),并且已经记录了相应的连接句柄,所以需要跟m_lbs_c数组的句柄比对,在m_lbs_c中找到要控制的从机1。

static ret_code_t led_status_send_to_device(control_device dev,uint8_t button_action)
{
    ret_code_t err_code;
	for (uint32_t i = 0; i< NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
    {
		if(conn_record[dev].my_conn_handle == m_lbs_c[i].conn_handle)
		{
			err_code = ble_lbs_led_status_send(&m_lbs_c[i], button_action);
			if (err_code != NRF_SUCCESS &&
				err_code != BLE_ERROR_INVALID_CONN_HANDLE &&
				err_code != NRF_ERROR_INVALID_STATE)
			{
				return err_code;
			}
		}
	}
	return NRF_SUCCESS;
}

3.5.1 读

在ble_lbs_c.h文件中ble_lbs_c_evt_t结构体的params成员中增加LED状态:
在这里插入图片描述
ble_lbs_c_evt_type_t中增加LED事件:
在这里插入图片描述
在lbs的GATT事件回调函数状态机中增加读事件:
在这里插入图片描述
在on_hvx()函数中除了按键通知处理之外增加LED读处理:

static void on_hvx(ble_lbs_c_t * p_ble_lbs_c, ble_evt_t const * p_ble_evt)
{
    // Check if the event is on the link for this instance.
    if (p_ble_lbs_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
    {
        return;
    }
    // Check if this is a Button notification.
    if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_lbs_c->peer_lbs_db.button_handle)//按键通知
    {
        if (p_ble_evt->evt.gattc_evt.params.hvx.len == 1)
        {
            ble_lbs_c_evt_t ble_lbs_c_evt;

            ble_lbs_c_evt.evt_type                   = BLE_LBS_C_EVT_BUTTON_NOTIFICATION;
            ble_lbs_c_evt.conn_handle                = p_ble_lbs_c->conn_handle;
            ble_lbs_c_evt.params.button.button_state = p_ble_evt->evt.gattc_evt.params.hvx.data[0];
            p_ble_lbs_c->evt_handler(p_ble_lbs_c, &ble_lbs_c_evt);
        }
    }
	
	if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_lbs_c->peer_lbs_db.led_handle)//led读
    {
        if (p_ble_evt->evt.gattc_evt.params.hvx.len == 1)
        {
            ble_lbs_c_evt_t ble_lbs_c_evt;

            ble_lbs_c_evt.evt_type                   = BLE_LBS_C_EVT_LED_READRESP;
            ble_lbs_c_evt.conn_handle                = p_ble_lbs_c->conn_handle;
			ble_lbs_c_evt.params.led.led_state		 = p_ble_evt->evt.gattc_evt.params.hvx.data[0];
            p_ble_lbs_c->evt_handler(p_ble_lbs_c, &ble_lbs_c_evt);
        }
    }
}

在lbs的客户端回调函数状态机中增加读LED事件处理,并记录至conn_record中:

static void lbs_c_evt_handler(ble_lbs_c_t * p_lbs_c, ble_lbs_c_evt_t * p_lbs_c_evt)
{
    switch (p_lbs_c_evt->evt_type)
    {
        case BLE_LBS_C_EVT_DISCOVERY_COMPLETE:
        {
            /*其他
            */
        } break; // BLE_LBS_C_EVT_DISCOVERY_COMPLETE

        case BLE_LBS_C_EVT_BUTTON_NOTIFICATION:
        {
            /*其他
            */
        } break; // BLE_LBS_C_EVT_BUTTON_NOTIFICATION
        case BLE_LBS_C_EVT_LED_READRESP:
        {
			for (uint32_t i = 0; i< NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
			{
				if(conn_record[i].my_conn_handle == p_lbs_c_evt->conn_handle)
				{
					NRF_LOG_INFO("%s curr state:%d", m_target_periph_name[i],p_lbs_c_evt->params.led.led_state);
					conn_record[i].led_state = p_lbs_c_evt->params.led.led_state;
				}
			} 
        } break; // BLE_LBS_C_EVT_LED_READRESP
        default:
            // No implementation needed.
            break;
    }
}

读LED状态函数为led_status_read_from_device(),在从机1处理函数BedRoomProcessFunc()中调用:

static ret_code_t led_status_read_from_device(control_device dev)
{
    ret_code_t err_code;
	for (uint32_t i = 0; i< NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
    {
		if(conn_record[dev].my_conn_handle == m_lbs_c[i].conn_handle)
		{
			err_code = ble_lbs_led_status_get(&m_lbs_c[i]);
			if (err_code != NRF_SUCCESS &&
				err_code != BLE_ERROR_INVALID_CONN_HANDLE &&
				err_code != NRF_ERROR_INVALID_STATE)
			{
				return err_code;
			}
		}
	}
	return NRF_SUCCESS;
}

仿照例程中的ble_lbs_led_status_send()函数写一个读LED状态的函数:

uint32_t ble_lbs_led_status_get(ble_lbs_c_t * p_ble_lbs_c)
{
    VERIFY_PARAM_NOT_NULL(p_ble_lbs_c);

    if (p_ble_lbs_c->conn_handle == BLE_CONN_HANDLE_INVALID)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    NRF_LOG_DEBUG("Read LED status");

    nrf_ble_gq_req_t read_req;

    memset(&read_req, 0, sizeof(nrf_ble_gq_req_t));
	
	read_req.type							= NRF_BLE_GQ_REQ_GATTC_READ;
	read_req.error_handler.cb				= gatt_error_handler;
	read_req.error_handler.p_ctx         	= p_ble_lbs_c;
	read_req.params.gattc_read.handle		= p_ble_lbs_c->peer_lbs_db.led_handle;
	read_req.params.gattc_read.offset		= 0;

    return nrf_ble_gq_item_add(p_ble_lbs_c->p_gatt_queue, &read_req, p_ble_lbs_c->conn_handle);
}

4运行效果

按照control_device枚举的顺序,conn_record结构体数组成员顺序对应从机1(bedroom_device)和从机2(drawingroom_device)。从机1先连接,从机2后连接:
在这里插入图片描述
在这里插入图片描述

从机2先连接,从机1后连接:
在这里插入图片描述
在这里插入图片描述

仅连接从机1,正常读写从机1LED状态,读写从机2LED状态提示错误:
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Java跨域问题

目录 1、跨域问题说明 ​2、跨域解决方案 2.1、局部跨域解决方案 2.1.1、CrossOrigin注解跨域 2.1.2、手动设置响应头 2.2、全局跨域解决方案 2.2.1、实现WebMvcConfigurer接口设置跨域 2.2.2、定义CorsFilter Bean实现跨域 2.2.3、重写ResponseBodyAdvice接口中的bef…

ros的launch文件知识

_node标签&#xff1a; 在指定机器上启动节点respawn"true|false"(可选) 如果节点退出&#xff0c;是否自动重启respawndelay"N"(可选) 如果 respawn 为 true,那么延迟N秒后启动节点required"true|false"(可选) 该节点是否必须&#xff0c;如果…

ZooKeeper 避坑实践:SnapCount 设置不合理导致磁盘爆满,服务不可用

作者&#xff1a;子葵 背景 在 ZooKeeper 的日常使用过程中&#xff0c;一个令人头疼的问题就是节点的磁盘容量问题&#xff0c;如果由于过大的 TPS 或者不适当的清理策略会导致集群中数据文件&#xff0c;日志文件的堆积&#xff0c;最终导致磁盘爆满&#xff0c;Server 宕机…

在线客服系统部署配置邮箱消息通知功能 - 唯一客服(v1kf.com) -开源私有化独立部署在线客服系统源码...

为在线客服系统设置邮件通知具有以下几个好处&#xff1a; 改善客户体验&#xff1a;邮件通知可以让客户实时收到新消息或更新通知&#xff0c;这有助于提高他们对您的服务的整体体验。 提高效率&#xff1a;邮件通知可以帮助提高客服团队的效率&#xff0c;因为它们会在新消息…

非专业人士如何完成数据采集?纯干货,一文看懂

写在前面&#xff1a; 本教程能够解决大部分人的数据采集及分析需求&#xff0c;实用、简单&#xff0c;尤其适合Excel大户、办公族、业务人员&#xff0c;或者不会编程、不懂数据分析理论的技术小白…… 来不及看的可以先点赞收藏&#xff01; 01 点对点的采集&#xff1a;…

吴恩达《机器学习》——欠拟合与过拟合

欠拟合与过拟合1. 方差与偏差模型的容量、过拟合和欠拟合2. Python代码实践2.1 拟合直线2.2 拟合多项式数据集、源文件可以在Github项目中获得 链接: https://github.com/Raymond-Yang-2001/AndrewNg-Machine-Learing-Homework 1. 方差与偏差 在数学上&#xff0c;估计的偏差…

Java基础漏洞(二)

继续填补自己的知识漏洞 1.&、&&、|、||之间的区别 &是逻辑与&#xff0c;而&&则是短路与。&和&&之间的区别是&#xff0c;在短路与&&的情况下&#xff0c;两个条件当第一个条件为假时&#xff0c;则不再执行第二个条件&#xf…

java学习之类方法

目录 一、基本介绍 二、类方法的调用 三、类方法的应用实例 代码 内存分析 运行结果 四、类方法的经典使用场景 五、类方法使用细节 第一条 第二条 第三条 第四条 第五条 第六条 六、练习 第一题 考察点 分析 结果 第二题 代码 考察点 结果 第三题 类方法 …

LeetCode498. 对角线遍历

LeetCode刷题记录 文章目录&#x1f4dc;题目描述&#x1f4a1;解题思路⌨C代码&#x1f4dc;题目描述 给你一个大小为 m x n 的矩阵 mat &#xff0c;请以对角线遍历的顺序&#xff0c;用一个数组返回这个矩阵中的所有元素。 示例1 输入&#xff1a;mat [[1,2,3],[4,5,6],[…

VUE2使用浏览器缓存的方法

分两种&#xff1a;localStorage和sessionStorage&#xff0c;它两统称webStorage 注意点1&#xff1a;localStorage对象和sessionStorage对象都是window对象下的&#xff0c;且方法都是一样的&#xff0c;默认”window.”可以省略,添加可用setItem(K,V)&#xff0c;查询可用ge…

数据库|scMethBank:单细胞全基因组 DNA 甲基化图谱数据库

甲基化是DNA的一种重要化学修饰&#xff0c;可调节基因的表达和关闭&#xff0c;与癌症、衰老、老年痴呆等许多疾病密切相关&#xff0c;是表观遗传学的重要研究内容之一。测序技术的发展&#xff0c;极大促进了单细胞DNA甲基化研究。然而大量数据的不断积累&#xff0c;对单细…

《HTTP权威指南》----HTTP报文

目录 报文流 报文的组成部分 报文语法 1.起始行 2.首部 通用首部&#xff0c;既可以出现在请求报文中也可以出现在响应报文中。 请求首部&#xff0c;提供更多有关请求的信息。 响应首部&#xff0c;提供更多有关响应的信息。 实体首部&#xff0c;描述主题的长度和内…

2022年,一个技术账号的年终独白,满篇都写着2个字:真难。

2022年&#xff0c;梦想橡皮擦这个账号经历了成长&#xff0c;突破&#xff0c;回归 2023年&#xff0c;适应改变 文章目录序2022年&#xff0c;梦想橡皮擦账号整体汇总原创博客KPI计划与完成总排名KPI计划与完成2022年&#xff0c;橡皮擦获得的荣誉2022年&#xff0c;做技术博…

日志收集系统架构

背景 应用服务器多&#xff0c;日志文件被分散在各个应用服务器上&#xff0c;需要依次登录每台设备才能查看日志&#xff0c;效率低下&#xff0c;且不利于服务器安全管控&#xff0c;加大生产服务器的风险&#xff1b;日志文件不统一&#xff0c;各项目日志没有统一的规范&a…

Python Django教程之实现天气应用程序

基本设置 将目录更改为天气 cd weather启动服务器 python manage.py runserver要检查服务器是否正在运行&#xff0c;请转到 Web 浏览器并输入为 URL。现在&#xff0c;您可以通过按以下命令停止服务器http://127.0.0.1:8000/ ctrl-c 实现 python manage.py startapp main…

Vehicle Speed Forecasting Based On GCN-LSTM Combined Model

GCN-LSTM模型预测道路交通车辆速度 Vehicle Speed Forecasting Based On GCN-LSTM Combined Model Summary This research offers a multistep traffic flow forecasting framework relying on interest spatial-temporal-graph neural network-long short-term memory neura…

【阅读】《MYSQL技术内幕:innodb》索引

概念 索引的类型 聚集索引&#xff1a;叶子节点包含行记录的全部数据辅助索引&#xff1a;叶子节点不包含行记录的全部数据&#xff0c;除了键值以外&#xff0c;还包含指向索引行的书签。 堆表和索引组织表 堆表 无论是主键索引还是普通索引都是辅助索引。数据是按照插入…

​力扣解法汇总2042. 检查句子中的数字是否递增

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 句子是由若干 token 组成的一个列表&#xff0c;token 间用 单个 空格分隔&…

微信小程序实战十五:Https服务搭建及Nginx配置

文章目录 1.最终效果预览2.后端jar包部署及启动3.前端管理系统部署4.Nginx的配置5.https证书申请6.小程序后台中配置子域名这篇文章重点介绍下微信小程序正式版上线前https服务的搭建及配置过程,之前整个流程都操作过,时隔一年再次从零开始操作有些地方的印象已经模糊了,好记…

Java Swing五子棋项目

一、项目简介 本项目为Java Swing五子棋项目&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse 确保可…