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

C++小白实习日记——Pollnet,Efvi,UDP,数据类型转换(下)

内容太多了,这篇记录UDP接收端

一,UDP接收端接收数据

有了pollnet这个开源项目的支持,接收端的步骤为:1)初始化硬编码的参数:接口,IP和端口

2)创建接收文件.csv

3)读取UDP包并将其写入文件中

std::atomic<bool> active = {true};
int main(int argc, char *argv[]) {//硬编码的参数:接口、IP和端口const char* interface = "enp8s0f1";  // 网口const char* local_ip = "10.100.100.161";  // 本地IP地址int local_port = 12346;  // 本地端口
//  const char* sub_ip = "10.100.100.162";  // 可选的订阅IPEfviUdpReceiver receiver;if (!receiver.init(interface, local_ip, local_port)) {cout << receiver.getLastError() << endl;return 1;}std::string fname1 = "接收文件.csv";//bool ret1 = isFileExists_ifstream(fname1);if (ret1){//文件存在cout<<"File is exit\n"<<endl;} else {std::ofstream outFile1(fname1);if (outFile1){std::cout <<"Files created successful.\n" << std::endl;}else {std::cout <<"Failed to create files.\n" << std::endl;return 1;}//关闭文件outFile1.close();}ofstream outFile1(fname1, ios::out);outFile1 << "DataTimeStamp"<<endl;while (active.load(std::memory_order_acquire)) {receiver.read(outFile1, outFile2, outFile3, outFile4);}return 0;
}

声明一个原子布尔类型的变量 active,并初始化为 true。这是为了控制接收器的运行状态。使用 std::atomic 类型确保在多线程环境下对 active 的操作是安全的。 

std::atomic<bool> active = {true};

 c++的标准流函数,用于检查指定路径的文件是否存在

bool isFileExists_ifstream(string& name) {ifstream f(name.c_str());return f.good();
}
  • ofstream outFile1(fname1, ios::out);:使用 ofstream 打开 fname1 文件,并准备写入数据。ios::out 表示以输出模式打开文件。
  • outFile1 << "DataTimeStamp" << endl;:写入一个标题 DataTimeStamp 到文件,之后换行。这为后续的接收数据做准备。
ofstream outFile1(fname1, ios::out);
outFile1 << "DataTimeStamp" << endl;

输出模式(ios::out)的含义:

  • ios::out 表示文件被以写入模式打开,也就是用于向文件写入数据。
  • 如果文件已经存在,ios::out 会清空文件内容(覆盖文件)。
  • 如果文件不存在,ios::out 会创建一个新文件。
  • ios::in:表示以输入模式打开文件,用于从文件读取数据。
  • ios::app:表示以追加模式打开文件,写入的数据将被添加到文件末尾,而不是覆盖原文件内容。
  • ios::ate:表示打开文件后,文件指针将移动到文件的末尾,但仍然可以进行读写操作。
  • ios::trunc:表示打开文件时,如果文件已经存在,则清空文件内容(这个模式是 ios::out 的默认行为)。
  • ios::binary:表示以二进制模式打开文件,而不是文本模式。

二,pollnet项目

这个项目是git的一个开源项目:https://github.com/MengRao/pollnet/tree/master(MIT许可证)

包括一些TCP,UDP的发送接收的封装类,Efvi与Socket的发送方式demo等等

我主要学习了Efvi.h这个库文件,其中包含EfviUdpSender和EfviReceiver两个大类,其中EfviReceiver包括EfviUdpReceiver,EfviEthReceiver,可以看到这个库函数主要是封装了UDP通信的发送接收类。

1,EfviUdpSender

这个类中主要有公有函数init,write,close,私有函数getMacFromARP,getGW,init_udp_pkt,ci_ip4_hdr_init,saveError,hexchartoi等等

1)init

函数 init 的目的是初始化网络接口、配置地址和端口,设置虚拟接口并准备内存以便发送数据。它的主要功能是准备好一个高效的网络通信环境,具体来说是通过 EFVi 驱动(一个与高速网络适配器或类似硬件接口的驱动相关的工具)进行 UDP 数据包的发送。

struct sockaddr_in local_addr;
struct sockaddr_in dest_addr;
uint8_t local_mac[6];
uint8_t dest_mac[6];
local_addr.sin_port = htons(local_port);
inet_pton(AF_INET, local_ip, &(local_addr.sin_addr));
dest_addr.sin_port = htons(dest_port);
inet_pton(AF_INET, dest_ip, &(dest_addr.sin_addr));
  • local_addrdest_addr 是用于本地和目标地址的结构体(sockaddr_in);
  • 使用 inet_pton 将 IP 地址(字符串)转换为二进制格式,存储到 sin_addr 中;
  • 端口号使用 htons 函数转换为网络字节序。
if ((0xff & dest_addr.sin_addr.s_addr) < 224) {char dest_mac_addr[64];if (!getMacFromARP(interface, dest_ip, dest_mac_addr)) {char gw[64];if (!getGW(dest_ip, gw) || !getMacFromARP(interface, gw, dest_mac_addr)) {saveError("Can't find dest ip from arp cache, please ping dest ip first", 0);return false;}}if (strlen(dest_mac_addr) != 17) {saveError("invalid dest_mac_addr", 0);return false;}for (int i = 0; i < 6; ++i) {dest_mac[i] = hexchartoi(dest_mac_addr[3 * i]) * 16 + hexchartoi(dest_mac_addr[3 * i + 1]);}
}
  • 检查目标地址是否为单播地址(dest_ip 的最后一个字节是否小于 224,如果小于则是单播地址)。
  • 如果是单播地址,调用 getMacFromARP 函数获取目标 IP 的 MAC 地址。如果 ARP 表中没有目标 IP 的 MAC 地址,尝试从网关获取。
  • hexchartoi 是一个将十六进制字符转换为整数的辅助函数。
if ((0xff & dest_addr.sin_addr.s_addr) < 224) {char dest_mac_addr[64];if (!getMacFromARP(interface, dest_ip, dest_mac_addr)) {char gw[64];if (!getGW(dest_ip, gw) || !getMacFromARP(interface, gw, dest_mac_addr)) {saveError("Can't find dest ip from arp cache, please ping dest ip first", 0);return false;}}if (strlen(dest_mac_addr) != 17) {saveError("invalid dest_mac_addr", 0);return false;}for (int i = 0; i < 6; ++i) {dest_mac[i] = hexchartoi(dest_mac_addr[3 * i]) * 16 + hexchartoi(dest_mac_addr[3 * i + 1]);}
}
  • 如果目标地址是组播地址(即目标地址的第一字节大于等于 224),则根据组播地址生成目标 MAC 地址。
  • 组播地址的 MAC 地址计算规则是:以 01:00:5e 开头,后面三字节根据 IP 地址最后三个字节生成。
else {dest_mac[0] = 0x1;dest_mac[1] = 0;dest_mac[2] = 0x5e;dest_mac[3] = 0x7f & (dest_addr.sin_addr.s_addr >> 8);dest_mac[4] = 0xff & (dest_addr.sin_addr.s_addr >> 16);dest_mac[5] = 0xff & (dest_addr.sin_addr.s_addr >> 24);
}
  • 打开驱动程序并初始化虚拟接口句柄(dh)。ef_driver_open 是一个操作系统或驱动接口的函数,用来初始化虚拟接口。
  • ef_pd_alloc_by_name 用于分配一个数据包描述符(Packet Descriptor,简称 PD),该描述符用于管理网络数据包。
int rc;
if ((rc = ef_driver_open(&dh)) < 0) {saveError("ef_driver_open failed", rc);return false;
}
if ((rc = ef_pd_alloc_by_name(&pd, dh, interface, EF_PD_DEFAULT)) < 0) {saveError("ef_pd_alloc_by_name failed", rc);return false;
}
  • 配置虚拟接口的标志和能力,检查虚拟接口是否支持 CTPIO(Contiguous Transmission Protocol Input/Output)。如果支持 CTPIO,则在虚拟接口标志中启用相应的标志。
int vi_flags = EF_VI_FLAGS_DEFAULT;
int ifindex = if_nametoindex(interface);
unsigned long capability_val = 0;
if (ef_vi_capabilities_get(dh, ifindex, EF_VI_CAP_CTPIO, &capability_val) == 0 && capability_val) {use_ctpio = true;vi_flags |= EF_VI_TX_CTPIO;
}
  • 使用 ef_vi_alloc_from_pd 从数据包描述符分配虚拟接口(VI)。该接口用于管理虚拟网络设备并发送/接收数据包。
  • ef_vi_get_mac 获取分配的虚拟接口的 MAC 地址。
size_t alloc_size = N_BUF * PKT_BUF_SIZE;
buf_mmapped = true;
pkt_bufs = (uint8_t*)mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0);
if (pkt_bufs == MAP_FAILED) {buf_mmapped = false;rc = posix_memalign((void**)&pkt_bufs, 4096, alloc_size);if (rc != 0) {saveError("posix_memalign failed", -rc);return false;}
}
  • 为网络数据包分配内存缓冲区。首先尝试使用 mmap 将内存映射到进程地址空间,如果失败则使用 posix_memalign 来对齐内存。
if ((rc = ef_memreg_alloc(&memreg, dh, &pd, dh, pkt_bufs, alloc_size)) < 0) {saveError("ef_memreg_alloc failed", rc);return false;
}
  • 使用 ef_memreg_alloc 为缓冲区注册内存,确保其可以通过网络适配器访问
for (int i = 0; i < N_BUF; i++) {struct pkt_buf* pkt = (struct pkt_buf*)(pkt_bufs + i * PKT_BUF_SIZE);pkt->post_addr = ef_memreg_dma_addr(&memreg, i * PKT_BUF_SIZE) + sizeof(ef_addr);init_udp_pkt(&(pkt->eth), local_addr, local_mac, dest_addr, dest_mac);
}
  • 为每个数据包初始化缓冲区,并填充 UDP 包的相关信息,如源地址、目标地址和 MAC 地址。
uint16_t* ip4 = (uint16_t*)&((struct pkt_buf*)pkt_bufs)->ip4;
ipsum_cache = 0;
for (int i = 0; i < 10; i++) {ipsum_cache += ip4[i];
}
ipsum_cache = (ipsum_cache >> 16u) + (ipsum_cache & 0xffff);
ipsum_cache += (ipsum_cache >> 16u);
  • 计算数据包的校验和(ipsum_cache),确保数据在传输过程中的完整性。

2)write

  bool write(const void* data, uint32_t size) {// 为缓冲区分配内存
//    uint8_t* pkt_bufs = new uint8_t[N_BUF * PKT_BUF_SIZE];struct pkt_buf* pkt = (struct pkt_buf*)(pkt_bufs + buf_index_ * PKT_BUF_SIZE);struct ci_ether_hdr* eth = &pkt->eth;struct ci_ip4_hdr* ip4 = (struct ci_ip4_hdr*)(eth + 1);struct ci_udp_hdr* udp = (struct ci_udp_hdr*)(ip4 + 1);uint16_t iplen = htons(28 + size);// 假设IP头部为28字节ip4->ip_tot_len_be16 = iplen;udp->udp_len_be16 = htons(8 + size);// 将 data 数据复制到数据包中
//    memcpy(pkt + 1, data, size);// 将数据复制到数据包中memcpy(reinterpret_cast<void*>(pkt + 1), data, size);  // 正确的内存拷贝uint32_t frame_len = 42 + size;int rc;if (use_ctpio) {uint32_t ipsum = ipsum_cache + iplen;ipsum += (ipsum >> 16u);ip4->ip_check_be16 = ~ipsum & 0xffff;// 使用CTPIO方式发送数据ef_vi_transmit_ctpio(&vi, &pkt->eth, frame_len, frame_len);rc = ef_vi_transmit_ctpio_fallback(&vi, pkt->post_addr, frame_len, buf_index_);}else {rc = ef_vi_transmit(&vi, pkt->post_addr, frame_len, buf_index_);}// 更新缓冲区索引buf_index_ = (buf_index_ + 1) % N_BUF;// 处理传输事件ef_event evs[EF_VI_EVENT_POLL_MIN_EVS];ef_request_id ids[EF_VI_TRANSMIT_BATCH];int events = ef_eventq_poll(&vi, evs, EF_VI_EVENT_POLL_MIN_EVS);for (int i = 0; i < events; ++i) {if (EF_EVENT_TYPE_TX == EF_EVENT_TYPE(evs[i])) {ef_vi_transmit_unbundle(&vi, &evs[i], ids);}}// 释放内存
//    delete[] pkt_bufs;return true;}
  • 通过 pkt_bufsbuf_index_ 来访问一个缓冲区中的数据包。pkt_bufs 是之前分配的一个内存区域,buf_index_ 是当前正在使用的缓冲区索引。
  • pkt 是一个指向当前缓冲区的指针,类型是 pkt_buf,它包含以太网头、IP头和UDP头。

  • 通过指针 ethip4udp 来访问数据包中的以太网头、IP头和UDP头。
  • iplen 计算IP头部和数据部分的总长度。这里假设IP头部是28字节,数据部分的大小是 size
  • ip_tot_len_be16udp_len_be16 分别是IP和UDP头中的长度字段,它们需要以网络字节序(大端序)来表示。
  • 将传入的 data 数据复制到数据包中的适当位置(在以太网头、IP头和UDP头之后的位置)。这里 pkt + 1 是指向数据部分的指针。
  • memcpy 将原始数据复制到数据包中的正确位置。
  • frame_len 计算整个数据包的长度,42字节是以太网头、IP头和UDP头的总长度。
  • 这里分为两种发送模式:

    • CTPIO模式:使用 CTPIO 方式发送数据。这是一种直接传输模式,通过 ef_vi_transmit_ctpio 发送数据。如果 CTPIO 发送失败,则会使用回退方式 ef_vi_transmit_ctpio_fallback
    • 普通发送模式:如果不使用 CTPIO,则直接通过 ef_vi_transmit 发送数据包。
  • ipsum_cache 是一个缓存的校验和,用来计算IP头部的校验和。校验和计算方法是将所有IP头的16位部分加起来,并进行取反操作,确保数据的完整性。

  • 在发送完当前数据包后,更新 buf_index_,使得缓冲区索引在 0N_BUF-1 之间循环。

  • 使用 ef_eventq_poll 检查是否有传输事件。这通常是在传输过程中可能发生的事件(如传输成功、失败等)。
  • 如果是发送事件(EF_EVENT_TYPE_TX),则通过 ef_vi_transmit_unbundle 处理事件,并返回请求ID。

2,EfviUdpReceiver

包含init函数与read函数

1)Init

  bool init(const char* interface, const char* dest_ip, uint16_t dest_port, const char* subscribe_ip = "") {if (!EfviReceiver::init(interface)) {return false;}udp_prefix_len = 64 + ef_vi_receive_prefix_len(&vi) + 14 + 20 + 8;int rc;ef_filter_spec filter_spec;struct sockaddr_in sa_local;sa_local.sin_port = htons(dest_port);inet_pton(AF_INET, dest_ip, &(sa_local.sin_addr));ef_filter_spec_init(&filter_spec, EF_FILTER_FLAG_NONE);if ((rc = ef_filter_spec_set_ip4_local(&filter_spec, IPPROTO_UDP, sa_local.sin_addr.s_addr, sa_local.sin_port)) <0) {std::cerr << "ef_filter_spec_set_ip4_local failed" << rc << std::endl;return false;}if ((rc = ef_vi_filter_add(&vi, dh, &filter_spec, NULL)) < 0) {std::cerr << "ef_vi_filter_add failed" << rc << std::endl;return false;}if (subscribe_ip[0]) {if ((subscribe_fd_ = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {std::cerr << "socket failed" << -errno << std::endl;return false;}struct ip_mreq group;inet_pton(AF_INET, subscribe_ip, &(group.imr_interface));inet_pton(AF_INET, dest_ip, &(group.imr_multiaddr));if (setsockopt(subscribe_fd_, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&group, sizeof(group)) < 0) {std::cerr << "setsockopt IP_ADD_MEMBERSHIP failed" << -errno << std::endl;return false;}}return true;}

 初始化网络通信,配置和设置了接收UDP数据包的过滤器并加入多播组。

  • interface:指定要用于接收数据包的网络接口。
  • dest_ip:目标 IP 地址,数据包的目的地址。
  • dest_port:目标端口,接收数据的 UDP 端口号。
  • subscribe_ip:可选的多播地址。如果设置了此参数,函数将加入到指定的多播组。
udp_prefix_len = 64 + ef_vi_receive_prefix_len(&vi) + 14 + 20 + 8;

 udp_prefix_len 是 UDP 数据包的前缀长度,包含:

  • 64:可能是与硬件或特定网络库相关的长度。

  • ef_vi_receive_prefix_len(&vi):获取网络接口接收前缀长度。

  • 14:以太网头部长度(Ethernet header)。

  • 20:IPv4头部长度(IP header)。

  • 8:UDP头部长度。

ef_filter_spec filter_spec;
struct sockaddr_in sa_local;
sa_local.sin_port = htons(dest_port);
inet_pton(AF_INET, dest_ip, &(sa_local.sin_addr));
ef_filter_spec_init(&filter_spec, EF_FILTER_FLAG_NONE);
  • 创建一个 filter_spec 过滤器规格对象,和一个 sockaddr_in 地址结构体 sa_local,它用来设置目标 IP 和端口。

  • inet_pton 将目标 IP 地址转换为网络字节序的地址格式。

  • htons 将目标端口号转换为网络字节序。

if ((rc = ef_filter_spec_set_ip4_local(&filter_spec, IPPROTO_UDP, sa_local.sin_addr.s_addr, sa_local.sin_port)) < 0) {std::cerr << "ef_filter_spec_set_ip4_local failed" << rc << std::endl;return false;
}
  • 设置过滤器,指定目标协议为 UDP,目标 IP 和端口为 dest_ipdest_port
  • 如果配置过滤器失败,输出错误并返回 false
if ((rc = ef_vi_filter_add(&vi, dh, &filter_spec, NULL)) < 0) {std::cerr << "ef_vi_filter_add failed" << rc << std::endl;return false;
}
  • 将刚刚配置好的过滤器添加到接收网络接口中。vi 是接收接口对象,dh 是设备句柄,filter_spec 是过滤器规格。
  • 如果添加失败,输出错误并返回 false
if (subscribe_ip[0]) {if ((subscribe_fd_ = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {std::cerr << "socket failed" << -errno << std::endl;return false;}struct ip_mreq group;inet_pton(AF_INET, subscribe_ip, &(group.imr_interface));inet_pton(AF_INET, dest_ip, &(group.imr_multiaddr));if (setsockopt(subscribe_fd_, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&group, sizeof(group)) < 0) {std::cerr << "setsockopt IP_ADD_MEMBERSHIP failed" << -errno << std::endl;return false;}
}
  • 如果传入了 subscribe_ip 参数,表示需要加入一个多播组。
  • 创建一个 socket 用于接收多播数据。
  • 使用 inet_pton 转换多播地址和本地网络接口的 IP 地址。
  • 使用 setsockopt 函数将本地接口加入到指定的多播组。
  • 如果多播加入失败,输出错误并返回 false

2) read

这个和原来的有一些修改:

  • ef_eventq_poll:从事件队列中获取接收到的数据包。
  • pkt_buf:指向接收到的网络数据包的缓冲区。
  • printPacket:打印数据包的内容。
  void read(ofstream &outFile1, ofstream &outFile2, ofstream &outFile3, ofstream &outFile4) {ef_event evs;if (ef_eventq_poll(&vi, &evs, 1) == 0) return;int id = EF_EVENT_RX_RQ_ID(evs);pkt_buf = (struct pkt_buf*)(pkt_bufs + id * PKT_BUF_SIZE);if (EF_EVENT_TYPE(evs) == EF_EVENT_TYPE_RX) {const char* buf = (const char*)pkt_buf + 64;const int bufLen = EF_EVENT_RX_BYTES(evs);if(bufLen != 0){// memcpy(udp_pkt_data, buf, bufLen);//TODO: 此处是否可以直接从DMA缓冲区里读取?TORALEV2API::CTORATstpLev2MarketDataField temp_MarketDataField = {0};//快照TORALEV2API::CTORATstpLev2OrderDetailField temp_OrderDetailField = {0};//逐笔委托TORALEV2API::CTORATstpLev2TransactionField temp_TransactionField = {0};//逐笔成交TORALEV2API::CTORATstpLev2XTSmergeField temp_SmergeField = {0};//合并主笔PARASE_CTORATstpLev2MarketDataField(buf+42, temp_MarketDataField, outFile1);
//            PARASE_CTORATstpLev2OrderDetailField(buf+42, temp_OrderDetailField, outFile2);
//            //行情快照  662
//            if (buf[42]==0x30&&buf[43]==0x00&&buf[44]==0x00&&buf[45]==0x05) {
//                PARASE_CTORATstpLev2MarketDataField(buf+42, temp_MarketDataField, outFile1);
//            }
//            //逐笔委托  110bytes
//            if (buf[42]==0x03&&buf[43]==0x00&&buf[44]==0x00&&buf[45]==0x08){
//                PARASE_CTORATstpLev2OrderDetailField(buf+42, temp_OrderDetailField, outFile2);
//            }
//            //逐笔成交  118bytes
//            if (buf[42]==0x03&&buf[43]==0x00&&buf[44]==0x00&&buf[45]==0x07){
//                PARASE_CTORATstpLev2TransactionField(buf+42, temp_TransactionField, outFile3);
//            }
//            //合并主笔  118bytes
//            if (buf[42]==0x30&&buf[43]==0x00&&buf[44]==0x00&&buf[45]==0x13){            //固定填值:0x13000030
//                PARASE_CTORATstpLev2XTSmergeField(buf+42, temp_SmergeField, outFile4);
//            }printPacket(buf, bufLen);}}ef_vi_receive_post(&vi, pkt_buf->post_addr, id);return;}private:int udp_prefix_len;struct pkt_buf* pkt_buf;int subscribe_fd_ = -1;};
if (ef_eventq_poll(&vi, &evs, 1) == 0) return;

 这一行从网络事件队列中获取事件,vief_vi 设备实例,evs 存储事件信息。ef_eventq_poll 的返回值为 0 时表示没有事件可处理。

我曾在接收端修改了缓冲区,在write函数中手动new了pkt_bufs,直接release了pkt_bufs,导致在接收部分ef_eventq_poll 的返回值为 0 时表示没有事件可处理。

pkt_buf = (struct pkt_buf*)(pkt_bufs + id * PKT_BUF_SIZE);

 根据事件的 id,从 pkt_bufs 数组中获取对应的接收缓冲区。

const char* buf = (const char*)pkt_buf + 64;
const int bufLen = EF_EVENT_RX_BYTES(evs);
if (bufLen != 0) {TORALEV2API::CTORATstpLev2MarketDataField temp_MarketDataField = {0};TORALEV2API::CTORATstpLev2OrderDetailField temp_OrderDetailField = {0};TORALEV2API::CTORATstpLev2TransactionField temp_TransactionField = {0};TORALEV2API::CTORATstpLev2XTSmergeField temp_SmergeField = {0};PARASE_CTORATstpLev2MarketDataField(buf+42, temp_MarketDataField, outFile1);
}
  • buf 指向接收到的数据包,从第 64 字节开始(可能是去除掉了 Ethernet 和 IP 头部),然后通过 EF_EVENT_RX_BYTES 获取数据包长度。之后,解析不同的字段数据并将其写入对应的输出流(例如 outFile1)。

printPacket(buf, bufLen);

 调用 printPacket 函数,打印数据包的内容,通常用于调试。

ef_vi_receive_post(&vi, pkt_buf->post_addr, id);

 调用 ef_vi_receive_post 完成数据包的处理,并将接收缓冲区重新发布。

 

 

 

 

相关文章:

C++小白实习日记——Pollnet,Efvi,UDP,数据类型转换(下)

内容太多了&#xff0c;这篇记录UDP接收端 一&#xff0c;UDP接收端接收数据 有了pollnet这个开源项目的支持&#xff0c;接收端的步骤为&#xff1a;1&#xff09;初始化硬编码的参数&#xff1a;接口&#xff0c;IP和端口 2&#xff09;创建接收文件.csv 3&#xff09;读…...

pytorch bilstm crf的教程,注意 这里不支持批处理,要支持批处理 用torchcrf这个。

### Bi-LSTM Conditional Random Field ### pytorch tutorials https://pytorch.org/tutorials/beginner/nlp/advanced_tutorial.html ### 模型主要结构&#xff1a; ![title](sources/bilstm.png) pytorch bilstm crf的教程&#xff0c;注意 这里不支持批处理 Python version…...

C# OpenCV机器视觉:畸变矫正

在一个阳光明媚的早晨&#xff0c;阿强决定去拍照。他拿起相机&#xff0c;穿上他最喜欢的羊毛大衣&#xff0c;准备记录下生活中的美好瞬间。可是&#xff0c;当他兴奋地查看照片时&#xff0c;发现自己拍的每一张都像是被外星人用变形金刚的力量扭曲过一样&#xff01;“这是…...

Java 密封类 (Sealed Classes) 深度解析

文章目录 语法说明定义密封类定义子类 使用场景探讨实际应用示例与其他语言特性的结合使用 Java 作为一种面向对象编程语言&#xff0c;提供了继承机制来实现代码复用和扩展。然而&#xff0c;无限制的继承可能导致代码库变得难以维护&#xff0c;甚至引入安全隐患。为了应对这…...

conda和pip源

conda 地址 ~/.condarc中科大源 channels:- conda-forge- https://mirrors.ustc.edu.cn/anaconda/pkgs/main/- https://mirrors.ustc.edu.cn/anaconda/pkgs/r/- https://mirrors.ustc.edu.cn/anaconda/pkgs/msys2/- defaults show_channel_urls: true查看 conda config --s…...

【c++数学】解一元一次方程

注意&#xff1a;没有合并多项式的功能&#xff0c;且未知数必须只在左边&#xff0c;右边是常数&#xff01;&#xff01;&#xff01; 好用记得点赞&#xff01; 代码如下&#xff1a; #include<iostream> #include<stdio.h>using namespace std;string q,f;d…...

【超详细实战攻略】Jmeter逻辑控制器中if控制器、模块控制器、测试片段的使用方法

【超详细实战攻略】Jmeter逻辑控制器中if控制器、模块控制器、测试片段的使用方法 1 搭建测试对象1.1 禅道下载1.2 禅道安装1.3 运行禅道1.4 接口查看1.5 接口选择 2 Jmeter前置操作2.1 创建Jmeter线程组2.2 创建信息头管理器和请求默认值2.3 添加获取token接口2.4 添加监听器 …...

采用qL-MPC技术进行小型固定翼无人机的路径跟随控制

来自论文"Predictive Path-Following Control for Fixed-Wing UAVs Using the qLMPC Framework in the Presence of Wind Disturbances" 控制架构 采用的是 ULTRA-Extra无人机&#xff0c;相关参数如下&#xff1a; 这里用于guidance law的无人机运动学模型为&#…...

比特币是否会取代美元(以及其他主权货币)

上图是 Olivier Blanchard 宏观经济学第八版的英文版内容。这里用中文解释。 1. 背景与现状&#xff1a; 比特币的规模与美元相比仍然很小&#xff1a; 截至 2018 年 12 月&#xff0c;比特币的总流通量为 1730 万枚&#xff0c;每枚价值 $3,900&#xff0c;总市值约 $670 亿…...

safe area helper插件

概述 显示不同机型的必能显示的区域 实现步骤 引入safearea&#xff0c;引入其中的safearea的csharp 为cancas加入gameobject gameobject中加入safearea脚本 将UI作为这个gameobject的子物体&#xff0c;就可以完成显示...

融云分享基于 Rust 的鸿蒙 SDK 开发实践

12 月 5 日&#xff0c;以“同心聚力&#xff0c;共建共享鸿蒙新生态”为主题的“鸿蒙生态伙伴 SDK 开发者论坛”在京举行。 融云凭借对鸿蒙生态的率先适配和创新贡献&#xff0c;荣获华为鸿蒙生态“HarmonyOS NEXT SDK 星河奖”。 本次论坛邀请了多位行业领导者参与&#xff…...

golang中的值传递与引用传递,如何理解结构体的方法?

先从一个例子说起 type Counter struct {count int }func (c Counter) Inc() {c.count }func test1() {c : Counter{}do : func() {for i : 0; i < 10; i {c.count}fmt.Println("done")}go do()go do()time.Sleep(3 * time.Second)fmt.Println(c.count) }func te…...

TON游戏现状一览:区块链与Telegram的完美融合

TON&#xff08;The Open Network&#xff09;作为一款基于区块链的开放平台&#xff0c;因其超高的交易处理速度和与Telegram的紧密集成&#xff0c;成为游戏开发者和用户的新兴乐土。TON不仅仅是一个去中心化的网络&#xff0c;它还通过其核心加密货币——Toncoin&#xff0c…...

《变分法·吴迪光1987年》复习汇总

目录 第一章 变分法的概念1 泛函和泛函的极值例1 捷线&#xff08;最速降线&#xff09;问题例2 等周问题例3 极小曲面问题极值必要条件 2 基本引理&#xff08;考反证法&#xff09;基本引理1基本引理2 第二章 固定边界的变分问题 ♠ \spadesuit ♠ 欧拉方程&#xff08;一&am…...

使用IP自签名SSL证书

最近需要创建WebSocket服务器并使用SSL证书&#xff0c;由于是内网测试&#xff0c;所以需要使用指定IP的自签SSL证书。 其实笔者前面博文 使用nexus3作为Docker镜像仓库 解决nexus3登录x509: certificate has expired or is not yet valid 中有创建过相应的证书&#xff0c;这…...

如何持续优化呼叫中心大模型呼出机器人的性能?

如何持续优化呼叫中心大模型呼出机器人的性能&#xff1f; 原作者&#xff1a;开源呼叫中心FreeIPCC&#xff0c;其Github&#xff1a;https://github.com/lihaiya/freeipcc 持续优化呼叫中心大模型呼出机器人的性能是一个复杂而持续的过程&#xff0c;涉及多个层面的策略和措…...

3D,点云平滑法线估计

需要平滑的情况 1、用激光扫描仪等设备扫描物体(尤其是比较小的物体)时,往往会有测量误差。这些误差所造成的不规则数据如果直接拿来曲面重建的话,会使得重建的曲面不光滑或者有漏洞,而且这种不规则数据很难用统计分析等滤波方法消除; 2、后处理过程中,对同一个物体从…...

windows下pyenv与宝塔python冲突解决

windows下安装pyenv后与宝塔python环境冲突 1、将C:\Program Files\python\Scripts中的pip3.exe改名(pip3-.exe) 2、将C:\用户\{用户名}\.pyenv\pyenv-win\shims中的pip、pip.bat、python、python.bat改名(pip-、pip-.bat、python-、python-.bat)&#xff0c;然后使用pip3和p…...

C# 探险之旅:第二十五节 - 类型class之字段Fields大冒险

嘿&#xff0c;探险家们&#xff01;欢迎再次搭乘C#的魔法列车&#xff0c;今天我们要深入探索class里的宝藏——字段&#xff08;Fields&#xff09;&#xff01;想象一下&#xff0c;字段就像是类里的秘密小房间&#xff0c;里面藏着对象的小秘密和宝藏。我们不仅要看看这些小…...

如何更新项目中的 npm 或 Yarn 依赖包至最新版本

要升级 package.json 文件中列出的包&#xff0c;你可以使用 npm&#xff08;Node Package Manager&#xff09;或 yarn。以下是两种工具的命令来更新你的依赖项&#xff1a; 使用 npm 更新所有包到最新版本 npm update如果你想将所有依赖项更新到其各自最新的大版本&#xf…...

【Linux系列】使用 watch 命令实时查看容器状态

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

电脑技巧:Everything 1.5 版本重大更新​支持拼音搜索+全文搜索

目录 一、软件介绍 二、主要更新亮点 更快的搜索速度和拼音搜索 全文搜索功能 智能推荐功能 增强的过滤选项 改进的用户界面 更好的多语言支持 增强的安全性和隐私保护 三、总结 Everything 作为一款备受推崇的文件搜索工具&#xff0c;以其卓越的性能和简洁的用户界…...

MySQL八股-全局锁,表级锁,表锁,元数据锁,意向锁,行级锁,行锁,间隙锁,临键

文章目录 全局锁表级锁表锁(表级锁)元数据锁(MDL&#xff0c;Meta Data Lock&#xff0c;表级锁)元数据锁演示元数据锁兼容的情况元数据锁互相阻塞的情况 意向锁&#xff08;Intention lock&#xff0c;表级锁&#xff09;意向锁分类意向锁演示&#xff1a;意向共享锁(**IS**)与…...

电子应用设计方案-53:智能AI打印机系统方案设计

智能 AI 打印机系统方案设计 一、引言 随着人工智能技术的不断发展&#xff0c;将其应用于打印机系统可以显著提升打印效率、优化打印质量&#xff0c;并提供更智能化的用户体验。本方案旨在设计一款融合 AI 技术的智能打印机系统。 二、系统概述 1. 系统目标 - 实现高精度、…...

docker—私有仓库搭建

docker—私有仓库搭建 HTTP 部署 docker run -d \-p 5000:5000 \--restartalways \--name registry \-v /opt/data/registry:/var/lib/registry \registry:2使用官方的 registry​ 镜像来启动私有仓库。默认情况下&#xff0c;仓库会被创建在容器的 /var/lib/registry​ 目录…...

马斯克Neuralink:未来的人机交互先锋,将会挑战传统通讯方式

Neuralink&#xff0c;由埃隆马斯克于2016年创立&#xff0c;专注于研发脑机接口技术。该技术通过植入大脑的芯片&#xff0c;实现人类与机器的“无缝连接”。2024年&#xff0c;Neuralink取得了突破性进展&#xff0c;成功在人体中植入了脑芯片。首位植入者Noland Arbaugh通过…...

[Maven]IDEA父工程创建子工程后父工程不可运行

IDEA在使用maven构建项目时&#xff0c;如果你在当前工程下创建一个子工程&#xff0c;那么原有的工程(变为父工程的工程)原有的代码通常会变得不可运行。 这是因为&#xff0c;使用maven创建父子工程关系后&#xff0c;IDEA会自动变更项目的模块相关配置。 比如这是我maven工程…...

Linux DNS 协议概述

1. DNS 概述 互联网中&#xff0c;一台计算机与其他计算机通信时&#xff0c;通过 IP 地址唯一的标志自己。此时的 IP 地址就类似于我们日常生活中的电话号码。但是&#xff0c;这种纯数字的标识是比较难记忆的&#xff0c;而且数量也比较庞大。例如&#xff0c;每个 IPv4 地址…...

Web前端技术宝典:期末冲刺指南

本文将为大家整理一份 Web 前端期末复习资料&#xff0c;内容涵盖 HTML、CSS、JavaScript 和常用的前端框架等方面的知识&#xff0c;帮助大家高效复习。 Web前端技术宝典&#xff1a;期末冲刺指南 1. HTML基础2. CSS基础3. JavaScript基础4. 前端框架5. 常见考试题型结语 1. …...

JVM 双亲委派模型以及垃圾回收机制

目录 1. JVM 内存区域划分 2. JVM 中类加载的过程 1) 类加载的基本流程 2) 双亲委派模型 3. JVM 中垃圾回收机制 1) 找到垃圾 a) 引用计数 b) 可达性分析 2) 释放垃圾 1. JVM 内存区域划分 一个运行起来的 Java 进程&#xff0c;其实就是一个 JVM 虚拟机。 而进程是…...

Linux编译Kernel时的文件zImage、文件dtb(dtbs)、核心模块分别是什么东西?

zImage文件的介绍 在编译Linux内核时&#xff0c;zImage 是一种内核映像文件&#xff0c;它是内核的压缩版本&#xff0c;通常用于引导嵌入式设备或其他资源有限的环境。 zImage 的具体含义 zImage 是 “Compressed Kernel Image” 的缩写。它是通过压缩原始的内核映像&…...

《计算机视觉证书:开启职业发展新航道》

一、引言 在当今科技飞速发展的时代&#xff0c;计算机视觉技术正以惊人的速度改变着我们的生活和工作方式。从智能手机的人脸识别解锁到自动驾驶汽车的环境感知&#xff0c;计算机视觉技术的应用无处不在。而计算机视觉证书作为这一领域的专业认证&#xff0c;其作用愈发凸显…...

4.Python 数字类型

Python 数字类型总结 文章目录 Python 数字类型总结1. 数字类型概述特点 2. 数字类型的创建与赋值3. 数字类型转换4. 数学运算与函数math 模块cmath 模块 5. 随机数生成6. 三角函数7. 数学常量 总结 Python 提供了多种数字类型来存储和操作数值数据。这些类型包括整数、浮点数、…...

火焰传感器与C++编程:精准检测火灾的技术实现

火灾是我们日常生活中一个不可忽视的安全隐患&#xff0c;而火灾报警系统的实现可以大大提高我们的安全保障。通过嵌入式技术和传感器&#xff0c;我们能够在第一时间识别火灾隐患并发出警报。火焰传感器作为一种专门用于火灾监测的传感器&#xff0c;能高效地通过红外线&#…...

纯前端实现一个精致的中英文挖空提示功能

前言 这两天给我的学习卡盒小程序新增了一个提示功能&#xff0c;在卡片正面的时候&#xff0c;点击左下角的小灯泡&#xff0c;就会弹出背面内容的提示&#xff0c;这个提示是挖了空的&#xff0c;这种方式可以帮助我们循序渐进的回忆内容&#xff0c;而不是直接看答案。 实现…...

秘塔搜索AI多线程批量生成TXT原创文章软件

秘塔AI搜索是秘塔科技旗下的搜索产品&#xff0c;其产品是简单、无广告、直接的搜索答案。 秘塔AI搜索写出来的文章无AI味及无AI痕迹&#xff0c;在如今AI文章泛滥时代&#xff0c;搜索引擎喜欢抓取收录这样无AI味原创文章。 秘塔搜索AI多线程批量生成TXT原创文章软件介绍&am…...

鸿蒙生态的崛起:开发实践、认证路径与激励策略

目录 前言 鸿蒙生态能力和行业解决方案 1、鸿蒙创新能力 2、鸿蒙行业解决方案 中软鸿蒙生态业务布局 1、深度参与鸿蒙生态建设 2、提供一站式鸿蒙生态服务 &#xff08;1&#xff09;服务目录 &#xff08;2&#xff09;改造过程的关键点 &#xff08;3&#xff09;鸿…...

python调用matlab函数(内置 + 自定义) —— 安装matlab.engine

文章目录 一、简介二、安装matlab.engine2.1、基于 CMD 安装2.2、基于 MATLAB 安装&#xff08;不建议&#xff09; 三、python调用matlab函数&#xff08;内置 自定义&#xff09; 一、简介 matlab.engine&#xff08;MATLAB Engine API for Python&#xff09;&#xff1a;…...

【数据分享】2014-2024年我国POI兴趣点数据(免费获取/来源于OSM地图)

POI是Point of Interest的简称&#xff0c;意为“兴趣点”&#xff0c;是互联网电子地图中用于表示特定位置的地理实体的核心数据类型。POI通常用于标注具体地点&#xff0c;例如餐厅、商场、学校、医院、景点等。这些数据以点的形式呈现&#xff0c;并附带详细属性信息&#x…...

【设计模式】如何用C++实现观察者模式【发布订阅机制】

【设计模式】如何用C实现观察者模式【发布订阅机制】 一、问题背景 代码质量影响生活质量。最近工作中频繁接触各种设计模式&#xff0c;深刻体会到优秀的设计模式不仅能显著降低后续维护的压力&#xff0c;还能提升开发效率。观察者模式作为一种降低耦合度、提高扩展性的利器…...

Qt编写RK3588视频播放器/支持RKMPP硬解/支持各种视音频文件和视频流/海康大华视频监控

一、前言 用ffmpeg做硬解码开发&#xff0c;参考自带的示例hw_decode.c即可&#xff0c;里面提供了通用的dxva2/d3d11va/vaapi这种系统层面封装的硬解码&#xff0c;也就是无需区分用的何种显卡&#xff0c;操作系统自动调度&#xff0c;基本上满足了各种场景的需要&#xff0…...

深入了解IPv6——光猫相关设定:DNS来源、DHCPv6服务、前缀来源等

光猫IPv6设置后的效果对比图&#xff1a; 修改前&#xff1a; 修改后&#xff1a; 一、DNS来源 1. 网络连接 来源&#xff1a; 从上游网络&#xff08;如运营商&#xff09;获取 IPv6 DNS 信息&#xff0c;通过 PPPoE 或 DHCPv6 下发。 特点&#xff1a; DNS 服务器地址直…...

Deepmotion技术浅析(五):运动追踪

运动追踪是 DeepMotion 动作捕捉和 3D 重建流程中的核心模块之一。该模块的主要任务是在视频序列中跟踪人体的运动轨迹&#xff0c;捕捉人体各部分随时间的变化&#xff0c;并生成连续的 3D 运动数据。DeepMotion 的运动追踪技术结合了计算机视觉、深度学习和物理模拟等方法&am…...

【html网页页面012】html+css制作品牌主题宏宝莱网页含视频、留言表单(7页面附效果及源码)

品牌主题宏宝莱网页制作 &#x1f964;1、写在前面&#x1f367;2、涉及知识&#x1f333;3、网页效果完整效果(7页)&#xff1a;代码目录结构&#xff1a;page1、首页page2、衍生品page3、包装设计page4、视频介绍page5、留言板page6、联系我们page7、详情页&#xff08;三层页…...

Java的栈与队列以及代码实现

Java栈和队列 栈的概念&#xff08;Stack&#xff09;栈的实现代码队列&#xff08;Queue&#xff09;模拟实现队列(双链表实现)循环队列&#xff08;循环数组实现&#xff09;用队列实现栈用栈来实现队列总结 栈的概念&#xff08;Stack&#xff09; 栈是常见的线性数据结构&…...

华为HarmonyOS NEXT 原生应用开发:鸿蒙中组件的组件状态管理、组件通信 组件状态管理小案例(好友录)!

文章目录 组件状态管理一、State装饰器1. State装饰器的特点2. State装饰器的使用 二、Prop装饰器&#xff08;父子单向通信&#xff09;1. Prop装饰器的特点2. Prop装饰器的使用示例 三、Link装饰器&#xff08;父子双向通信&#xff09;1. Link装饰器的特点3. Link使用示例 四…...

LeetCode:150. 逆波兰表达式求值

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;150. 逆波兰表达式求值 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表…...

LLM模型的generate和chat函数区别

在 Hugging Face 的 transformers 库中&#xff0c;GPT&#xff08;Generative Pre-trained Transformer&#xff09;类的模型有两个常用的生成文本的方法&#xff1a;generate 和 chat。这两个方法在使用上有一些区别。通常公司发布的 LLM 模型会有一个基础版本&#xff0c;还…...

Vulhub:Fastjson[漏洞复现]

1.2.24-rce(CVE-2017-18349-Fastjson反序列化) 对于 Fastjson 来说&#xff0c;该漏洞的主要问题在于其1.2.24版本中autotype特性允许任意类的反序列化&#xff0c;因此攻击者通过type指定自定义类并实例化&#xff0c;在特定条件下调用这些类的公共方法。如果一个不受信任的 J…...

C++学习日记---第19天

笔记复习 1.继承 在C中&#xff0c;我们通过函数来实现代码的复用&#xff0c;防止重复造轮子&#xff0c;但是使用函数也有一个缺点1&#xff0c;就是当函数被定义完成之后&#xff0c;它的功能也就确定了&#xff0c;无法被修改&#xff0c;这时候我们引入继承。 C中的继承…...