广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >c++怎样实现一个简易的网络缓冲区的实践
  • 290
分享到

c++怎样实现一个简易的网络缓冲区的实践

2023-06-22 05:06:04 290人浏览 八月长安
摘要

这期内容当中小编将会给大家带来有关c++怎样实现一个简易的网络缓冲区的实践,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1. 前言请思考以下几个问题:1).为什么需要设计网络缓冲区,内核中不是有读写缓冲区

这期内容当中小编将会给大家带来有关c++怎样实现一个简易的网络缓冲区的实践,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

1. 前言

请思考以下几个问题:

1).为什么需要设计网络缓冲区,内核中不是有读写缓冲区吗?

需要设计的网络缓冲区和内核中tcp缓冲区的关系如下图所示,通过Socket进行进行绑定。具体来说网络缓冲区包括读(接收)缓冲区和写(发送)缓冲区。设计读缓冲区的目的是:当从TCP中读数据时,不能确定读到的是一个完整的数据包,如果是不完整的数据包,需要先放入缓冲区中进行缓存,直到数据包完整才进行业务处理。设计写缓冲区的目的是:向TCP写数据不能保证所有数据写成功,如果TCP写缓冲区已满,则会丢弃数据包,所以需要一个写缓冲区暂时存储需要写的数据。

c++怎样实现一个简易的网络缓冲区的实践

2).缓冲区应该设置为堆内存还是栈内存?

假设有一个服务端程序,需要同时连接多个客户端,每一个socket就是一个连接对象,所以不同的socket都需要自己对应的读写缓冲区。如果将缓冲区设置为栈内存,很容易爆掉,故将将其设置为堆内存更加合理。此外,缓冲区容量上限一般是有限制的,一开始不需要分配过大,仅仅在缓冲区不足时进行扩展。

c++怎样实现一个简易的网络缓冲区的实践

3).读写缓冲区的基本要求是什么?

通过以上分析,不难得出读写缓冲区虽然是两个独立的缓冲区,但是其核心功能相同,可以复用其代码。
读写缓冲区至少提供两类接口:存储数据和读取数据
读写缓冲区要求:先进先出,保证存储的数据是有序的

4).如何界定数据包?

第一种使用特殊字符界定数据包:例如\n,\r\n,第二种通过长度界定数据包,数据包中首先存储的是整个数据包的长度,再根据长度进行读取。

5).几种常见的缓冲区设计

①ringbuffer+读写指针
ringbuffer是一段连续的内存,当末端已经写入数据后,会从头部继续写数据,所以感觉上像一个环,实际是一个循环数组。ringbuffer的缺点也很明显:不能够扩展、对于首尾部分的数据需要增加一次io调用。

c++怎样实现一个简易的网络缓冲区的实践

②可扩展的读写缓冲区+读写指针
下图设计了一种可扩展的读写缓冲区,在创建时会分配一块固定大小的内存,整个结构分为预留空间数据空间。预留空间用于存储必要的信息,真正存储数据的空间由连续内存组成。此种缓冲区设计相对于ringbuffer能够扩展,但是也有一定的缺点:由于需要最大化利用空间,会将数据移动至开头,移动操作会降低读写速度。

本文实现可扩展的读写缓冲区+读写指针

c++怎样实现一个简易的网络缓冲区的实践

2. 数据结构

①Buffer类的设计与初始化

Buffer类的数据结构如下所示,m_s是指向缓冲区的指针,m_max_size是缓冲区的长度,初始设置为10,并根据扩展因子m_expand_par进行倍增。扩展因子m_expand_par设置为2,表示每次扩增长度翻倍,也就是说缓冲区的长度随扩展依次为10、20、40、80。

class Buffer{public:Buffer();//构造~Buffer();int init();//分配缓冲区private:char* m_s;//缓冲区指针size_t m_read_index;//读指针位置size_t m_write_index;//写指针位置size_t m_max_size;//缓冲区长度size_t m_expand_par;//扩展因子};

构造函数的初始化列表中初始化成员变量。实际初始化缓冲区在init函数中分配内存,大小为m_max_size不在构造函数中初始化缓冲区的原因是:如果构造函数中分配失败,无法处理,也可使用RAII手段进行处理

Buffer::Buffer():m_read_index(0),m_write_index(0),m_max_size(10), m_expand_par(2),m_s(nullptr){}Buffer::~Buffer(){delete[] m_s;}int Buffer::init(){m_s = new char[m_max_size]();if (m_s == nullptr) {cout << "分配m_s失败\n";return -1;}return 0;}

②读写指针的位置变化

当缓冲区为空时,读写指针位置相同都为0。

c++怎样实现一个简易的网络缓冲区的实践

在写入长度为6的数据后,读写指针位置如图

c++怎样实现一个简易的网络缓冲区的实践

接着读取两个字节后,读写指针如图

c++怎样实现一个简易的网络缓冲区的实践

③扩展缓冲区实现

扩展缓冲区实际分为两步,将有效数据前移至缓冲区头(最大化利用数据),再进行扩展。根据成员变量扩展因子m_expand_par的值,将缓冲区按倍数扩大。

假设当前存储数据4个字节,读写指针如下图。需要新增9个字节

c++怎样实现一个简易的网络缓冲区的实践

将数据前移至缓冲区头

c++怎样实现一个简易的网络缓冲区的实践

扩展缓冲区为2倍

c++怎样实现一个简易的网络缓冲区的实践

写入9个字节

c++怎样实现一个简易的网络缓冲区的实践

实际需要实现的两个私有成员函数:调整数据位置至缓冲区头adjust_buffer()扩展expand_buffer(),设置为私有属性则是因为不希望用户调用,仅仅在写入缓冲区前判断不够就进行扩展,用户不应该知道与主动调用。

class Buffer {public:...private:void adjust_buffer();//调整数据位置至缓冲区头部头void expand_buffer(size_t need_size);//扩展缓冲区长度...}

adjust_buffer()实现如下,注释写的较为清楚,不再赘述

void Buffer::adjust_buffer(){if (m_read_index == 0)//数据已经在头部,直接返回return;int used_size = m_write_index - m_read_index;if (used_size == 0) {//缓冲区为空,重置读写指针m_write_index = 0;m_read_index = 0;}else {cout << "调整前read_index write_index" << m_read_index << " " << m_write_index << endl;memcpy(m_s, &m_s[m_read_index], used_size);//将数据拷贝至头部m_write_index -= m_read_index;//写指针也前移cout << "调整了" << used_size << "个字节" << endl;m_read_index = 0;//读指针置0}cout << "调整后read_index write_index" << m_read_index << " " << m_write_index << endl;}

扩展缓冲区实现如下:

  • 首先根据需要写入的字节数判断缓冲区长度多大才能够容下

  • 申请新的存储区,并将数据拷贝到新存储区

  • 释放旧缓冲区,将新存储区作为缓冲区

void Buffer::expand_buffer(size_t need_size)//need_size需要写入的字节数{size_t used_size = m_write_index - m_read_index;//used_size表示已经存储的字节数size_t remain_size = m_max_size - used_size;//remain_size表示剩余空间size_t expand_size = m_max_size;while (remain_size < need_size) {//剩余空间不够时扩展,用while表示直到扩展至够用expand_size *= m_expand_par;remain_size = expand_size - used_size;//cout << "扩展长度中... 总剩余 总长度 " << remain_size << "  " << expand_size << endl;}char* s1 = new char[expand_size]();//申请新的空间memcpy(s1, m_s, m_max_size);free(m_s);m_s = s1;//将新空间挂载到缓冲区m_max_size = expand_size;//更新缓冲区总长度//cout << "扩展结束,总长度为" << m_max_size << endl;}

3. 外部接口设计与实现

以读缓冲区为例需要提供的接口有:向缓冲区写入数据write_to_buffer(),向缓冲区读取数据read_from_buffer(),得到能够读取的最大字节数readable_bytes()

class Buffer {public:void write_to_buffer(char* src);//从src中写数据size_t readable_bytes();//存储数据的字节数size_t read_from_buffer(char *dst,int bytes);//读数据size_t pop_bytes(size_t bytes);//丢弃数据}

① 写入缓冲区write_to_buffer()

write_to_buffer()实现的思路如流程图所示:

判断剩余空间:

剩余空间不够:调整数据至头部、扩展缓冲区
剩余空间足够:向下继续

判断当前空间:

当前空间不够:调整数据至头部
剩余空间足够:向下继续

存储数据

c++怎样实现一个简易的网络缓冲区的实践

根据流程图实现起来逻辑非常清晰,src表示原始数据

void Buffer::write_to_buffer(char* src){size_t used_size = m_write_index - m_read_index;//used_size表示已经存储的字节数size_t remain_size = m_max_size - used_size;//remain_size表示剩余空间size_t cur_size = m_max_size - m_write_index;//cur_size表示当前能够存储的空间size_t size = init_random_write(&src);//printf("已经使用%d,剩余总长度%d,剩余当前长度%d\n", used_size, remain_size, cur_size);if (size > remain_size) {//剩余空间不够adjust_buffer();expand_buffer(size);}else if (size > cur_size) {//剩余空间够,当前存不下adjust_buffer();}memcpy(&m_s[m_write_index], src, size);//存储数据m_write_index += size;delete[] src;//更新并打印log//used_size = m_write_index - m_read_index;//remain_size = m_max_size - used_size;//cur_size = m_max_size - m_write_index;//printf("已经使用%d,剩余总长度%d,剩余当前长度%d\n", used_size, remain_size, cur_size);}

流程图中还出现随机一段数据,这是用来调试的。随机初始化一段长度为0~ 40,字符a~ z的数据,并写缓存区

static int get_random_len() {return rand() % 40;}static int get_random_ala() {return rand() % 26;}size_t Buffer::init_random_write(char** src){int size = get_random_len();char ala = get_random_ala();*src = new char[size];cout << "准备写入的长度为" << size << " 值全是 " << (unsigned char)('a' + ala) << endl;for (int i = 0; i < size; i++) {(*src)[i] = 'a' + ala;}return size;}

② 读取缓冲区read_from_buffer()

read_from_buffer(char*dst,int read_size)传入需要拷贝到目的地址和需要读取的字节数,需要注意的是需要读取的字节数为-1表示全部读取,函数返回实际读取的字节数。实现如流程图所示:

c++怎样实现一个简易的网络缓冲区的实践

代码如下

size_t Buffer::read_from_buffer(char*dst,int read_size){size_t read_max = m_write_index - m_read_index;//read_max存储的字节数if (read_size == 0 || read_max == 0)//读取0字节和空缓存区时直接返回return 0;if (read_size == -1) {//全读走memcpy(dst, &m_s[m_read_index], read_max);m_read_index += read_max;cout << "读取了" << read_max << "个字节" << endl;}else if (read_size > 0) {//读取指定字节if ((size_t)read_size > read_max)read_size = read_max;memcpy(dst, &m_s[m_read_index], read_size);m_read_index += read_size;cout << "读取了" << read_size << "个字节" << endl;}return read_size;//返回读取的字节数}

③ 丢弃数据pop_bytes

size_t pop_bytes(size_t size)传入需要丢弃的字节数,需要注意的是需要丢弃的字节数为-1表示全部丢弃;-2表示随机丢弃0~ 40字节,函数返回实际丢弃的字节数。实现如流程图所示:

c++怎样实现一个简易的网络缓冲区的实践

size_t Buffer::pop_bytes(size_t size){size_t read_max = m_write_index - m_read_index;//存储数据长度//test randomif (size == -2)size = get_random_len();if (size == 0 || read_max == 0)//缓冲区为空或丢弃0字节返回return 0;if (size == -1) {//全丢m_read_index += read_max;cout << "丢弃了" << read_max << "个字节" << endl;return read_max;}if (size > 0) {//丢弃指定字节if (size > read_max)size = read_max;m_read_index += size;cout << "丢弃了" << size << "个字节" << endl;}return size;}

④ 其他接口

peek_read()peek_write()返回读写指针的位置

size_t peek_read();//指向准备读的位置(调试用)size_t peek_write();//指向准备写的位置(调试用)size_t Buffer::peek_write(){return m_write_index;}size_t Buffer::peek_read(){return m_read_index;}

4. 完整代码与测试

① 完整代码

Buffer.h

#pragma onceclass Buffer {public:Buffer();//构造~Buffer();//析构int init();//分配缓冲区void write_to_buffer(char* src);//写数据size_t pop_bytes(size_t bytes);//丢弃数据size_t read_from_buffer(char *dst,int bytes);//读数据size_t readable_bytes();//得到存储数据的字节数size_t peek_read();//指向准备读的位置(调试用)size_t peek_write();//指向准备写的位置(调试用)private:void adjust_buffer();//调整数据位置至缓冲区头void expand_buffer(size_t need_size);//扩展缓冲区长度size_t init_random_write(char** src);//随机初始化一段数据(调试用)private:char* m_s;//缓冲区指针size_t m_read_index;//读指针位置size_t m_write_index;//写指针位置size_t m_max_size;//缓冲区长度size_t m_expand_par;//扩展因子};

Buffer.cpp:

#include "Buffer.h"#include<iOStream>#include<time.h>using namespace std;int total_write = 0;//记录总写入int total_read = 0;//记录总读取static int get_random_len() {return rand() % 40;}static int get_random_ala() {return rand() % 26;}Buffer::Buffer():m_read_index(0),m_write_index(0),m_max_size(10), m_expand_par(2),m_s(nullptr){}Buffer::~Buffer(){delete[] m_s;}int Buffer::init(){m_s = new char[m_max_size]();if (m_s == nullptr) {cout << "分配m_s失败\n";return -1;}return 0;}size_t Buffer::read_from_buffer(char*dst,int read_size){size_t read_max = m_write_index - m_read_index;//read_max存储的字节数if (read_size == 0 || read_max == 0)//读取0字节和空缓存区时直接返回return 0;if (read_size == -1) {//全读走memcpy(dst, &m_s[m_read_index], read_max);m_read_index += read_max;printf("读取完成:\t读取%d个字节\n", read_max); total_read += read_max;}else if (read_size > 0) {//读取指定字节if ((size_t)read_size > read_max)read_size = read_max;memcpy(dst, &m_s[m_read_index], read_size);m_read_index += read_size;printf("读取完成:\t读取%d个字节\n", read_size);total_read += read_size;}return read_size;//返回读取的字节数}size_t Buffer::readable_bytes(){return m_write_index - m_read_index;}size_t Buffer::peek_write(){return m_write_index;}size_t Buffer::peek_read(){return m_read_index;}void Buffer::write_to_buffer(char* src){size_t used_size = m_write_index - m_read_index;//used_size表示已经存储的字节数size_t remain_size = m_max_size - used_size;//remain_size表示剩余空间size_t cur_size = m_max_size - m_write_index;//cur_size表示当前能够存储的空间size_t size = init_random_write(&src);//printf("已经使用%d,剩余总长度%d,剩余当前长度%d\n", used_size, remain_size, cur_size);if (size > remain_size) {//剩余空间不够adjust_buffer();expand_buffer(size);}else if (size > cur_size) {//剩余空间够,当前存不下adjust_buffer();}memcpy(&m_s[m_write_index], src, size);//存储数据m_write_index += size;delete[] src;//更新并打印logused_size = m_write_index - m_read_index;remain_size = m_max_size - used_size;cur_size = m_max_size - m_write_index;printf("写入完成:\t总存储%d,剩余空间%d,剩余当前空间%d\n", used_size, remain_size, cur_size);}size_t Buffer::pop_bytes(size_t size){size_t read_max = m_write_index - m_read_index;//存储数据长度//test randomif (size == -2)size = get_random_len();if (size == 0 || read_max == 0)//缓冲区为空或丢弃0字节返回return 0;if (size == -1) {//全丢m_read_index += read_max;cout << "丢弃了" << read_max << "个字节" << endl;total_read += read_max;return read_max;}if (size > 0) {//丢弃指定字节if (size > read_max)size = read_max;m_read_index += size;cout << "丢弃了" << size << "个字节" << endl;total_read += size;}return size;}size_t Buffer::init_random_write(char** src){int size = get_random_len();total_write += size;*src = new char[size];char ala = get_random_ala();cout << "随机写入:\t长度为" << size << " 值全是 " << (unsigned char)('a' + ala) << endl;for (int i = 0; i < size; i++) {(*src)[i] = 'a' + ala;}return size;}void Buffer::adjust_buffer(){if (m_read_index == 0)//数据已经在头部,直接返回return;int used_size = m_write_index - m_read_index;if (used_size == 0) {//缓冲区为空,重置读写指针m_write_index = 0;m_read_index = 0;}else {cout << "调整前read_index write_index" << m_read_index << " " << m_write_index << endl;memcpy(m_s, &m_s[m_read_index], used_size);//将数据拷贝至头部m_write_index -= m_read_index;//写指针也前移cout << "调整了" << used_size << "个字节" << endl;m_read_index = 0;//读指针置0}cout << "调整后read_index write_index" << m_read_index << " " << m_write_index << endl;}void Buffer::expand_buffer(size_t need_size)//need_size需要写入的字节数{size_t used_size = m_write_index - m_read_index;//used_size表示已经存储的字节数size_t remain_size = m_max_size - used_size;//remain_size表示剩余空间size_t expand_size = m_max_size;while (remain_size < need_size) {//剩余空间不够时扩展,用while表示直到扩展至够用expand_size *= m_expand_par;remain_size = expand_size - used_size;cout << "扩展长度中... 总剩余 总长度 " << remain_size << "  " << expand_size << endl;}char* s1 = new char[expand_size]();//申请新的空间memcpy(s1, m_s, m_max_size);free(m_s);m_s = s1;//将新空间挂载到缓冲区m_max_size = expand_size;//更新缓冲区总长度cout << "扩展结束,总长度为" << m_max_size << endl;}

② 测试

int main() {srand((unsigned)time(NULL));//调试需要初始化随机种子Buffer* pbuffer = new Buffer();//创建Buffer对象if (pbuffer->init() != 0)//init函数分配缓冲区return 0;{char* s = nullptr;//s是指向随机数据的指针char* read = new char[1000];//读取时将数据存储到的指针readsize_t read_size = 0;//本次读取到的字节数pbuffer->write_to_buffer(s);read_size = pbuffer-> read_from_buffer(read, -1);pbuffer->write_to_buffer(s);pbuffer->pop_bytes(-2);read_size = read_size = pbuffer-> read_from_buffer(read, 0);pbuffer->write_to_buffer(s);cout << "总写入\t" << total_write << endl;;cout << "总读取\t" << total_read << endl;cout << "目前写入" << total_write - total_read << endl;cout << "可读取\t" << pbuffer->readable_bytes()<< endl;printf(" write %d read %d \n", pbuffer->peek_write(),pbuffer->peek_read());if (total_write - total_read != pbuffer->readable_bytes()) {//根据总写入-总读取和一共存储的字节数判断是否存储正确cout << "error!!!" << endl;}elsecout << "test is ok\n\n\n";}delete s;delete[] read;delete pbuffer;return 0;}

随机1000000次测试

int main() {srand((unsigned)time(NULL));//调试需要初始化随机种子Buffer* pbuffer = new Buffer();//创建Buffer对象if (pbuffer->init() != 0)//init函数分配缓冲区return 0;char* s = nullptr;//s是指向随机数据的指针char* read = new char[1000];//读取时将数据存储到的指针readsize_t read_size = 0;//本次读取到的字节数unsigned long long time = 0;//调试的循环次数while (1) {pbuffer->write_to_buffer(s);read_size = pbuffer-> read_from_buffer(read, -1);pbuffer->write_to_buffer(s);pbuffer->write_to_buffer(s);pbuffer->pop_bytes(-2);read_size = read_size = pbuffer-> read_from_buffer(read, 0);pbuffer->write_to_buffer(s);pbuffer->pop_bytes(-2);pbuffer->write_to_buffer(s);read_size = pbuffer-> read_from_buffer(read, -1);pbuffer->write_to_buffer(s);pbuffer->write_to_buffer(s);read_size = pbuffer-> read_from_buffer(read, 22);pbuffer->write_to_buffer(s);pbuffer->write_to_buffer(s);read_size = pbuffer-> read_from_buffer(read, -1);pbuffer->pop_bytes(-2);pbuffer->pop_bytes(-2);pbuffer->write_to_buffer(s);pbuffer->write_to_buffer(s);read_size = pbuffer-> read_from_buffer(read, 2);pbuffer->write_to_buffer(s);read_size = pbuffer-> read_from_buffer(read, 17);pbuffer->write_to_buffer(s);pbuffer->pop_bytes(-2);pbuffer->write_to_buffer(s);pbuffer->write_to_buffer(s);pbuffer->read_from_buffer(read, 18);cout << "总写入\t" << total_write << endl;;cout << "总读取\t" << total_read << endl;cout << "目前写入" << total_write - total_read << endl;cout << "可读取\t" << pbuffer->readable_bytes()<< endl;printf(" write %d read %d \n", pbuffer->peek_write(),pbuffer->peek_read());if (total_write - total_read != pbuffer->readable_bytes()) {//根据总写入-总读取和一共存储的字节数判断是否存储正确cout << "error!!!" << endl;break;}if (time == 1000000)//循环1000000次{cout << "1000000 ok!!!" << endl;break;}cout << time++ << " is ok\n\n\n";}delete s;delete[] read;delete pbuffer;return 0;}

上述就是小编为大家分享的c++怎样实现一个简易的网络缓冲区的实践了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网其他教程频道。

--结束END--

本文标题: c++怎样实现一个简易的网络缓冲区的实践

本文链接: https://www.lsjlt.com/news/302826.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • c++怎样实现一个简易的网络缓冲区的实践
    这期内容当中小编将会给大家带来有关c++怎样实现一个简易的网络缓冲区的实践,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1. 前言请思考以下几个问题:1).为什么需要设计网络缓冲区,内核中不是有读写缓冲区...
    99+
    2023-06-22
  • c++实现一个简易的网络缓冲区的实践
    目录1. 前言2. 数据结构3. 外部接口设计与实现4. 完整代码与测试1. 前言 请思考以下几个问题: 1).为什么需要设计网络缓冲区,内核中不是有读写缓冲区吗? 需要设计的网络缓...
    99+
    2022-11-12
  • VUE实现一个简易老虎机的项目实践
    目录简单分析下先看看效果htmljs部分总结今天突然要做一个竖直滚动老虎机,可以设置中奖位置,以及中奖回调,然后再带点常规的滚动动画,还是有点意思,和之前的转盘抽奖有点类似,有兴趣可...
    99+
    2022-11-13
  • C++怎么实现print缓冲区的刷新
    这篇文章主要讲解了“C++怎么实现print缓冲区的刷新”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++怎么实现print缓冲区的刷新”吧!printf缓冲区问题以下内容在Linux测试...
    99+
    2023-06-30
  • 基于Python怎样实现一个简易的数据管理系统
    这期内容当中小编将会给大家带来有关基于Python怎样实现一个简易的数据管理系统,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。为了方便的实现记录数据、修改数据没有精力去做一个完整的系统去管理数据。因此,在...
    99+
    2023-06-22
  • 【看表情包学Linux】缓冲区的概念 | Git 三板斧 | 实现简易进度条
      爆笑教程,只送有缘人 👉 《看表情包学Linux》 💭 写在前面:本章我们先对缓冲区的概念进行一个详细的探究,之后会带着大家一步步去编写一个简陋的 "进度条" 小程序,过程还是挺有意思的,虽然实现的过...
    99+
    2023-09-07
    linux 运维 服务器 git
  • 怎么用Python实现一个简易的截图工具
    这篇文章主要讲解了“怎么用Python实现一个简易的截图工具”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用Python实现一个简易的截图工具”吧!代码:# # -*...
    99+
    2023-07-02
  • C++怎么实现一个简单的线程池
    本文小编为大家详细介绍“C++怎么实现一个简单的线程池”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++怎么实现一个简单的线程池”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、设计线程池应该包括保存线程的容...
    99+
    2023-06-30
  • java实现一个简单的网络爬虫代码示例
    目前市面上流行的爬虫以python居多,简单了解之后,觉得简单的一些页面的爬虫,主要就是去解析目标页面(html)。那么就在想,java有没有用户方便解析html页面呢?找到了一个jsoup包,一个非常方便解析html的工具呢。使用方式也非...
    99+
    2023-05-30
    网络爬虫 java jsoup
  • 如何利用C++实现一个简单的网页爬虫程序?
    如何利用C++实现一个简单的网页爬虫程序?简介:互联网是一个信息的宝库,而通过网页爬虫程序可以轻松地从互联网上获取大量有用的数据。本文将介绍如何使用C++编写一个简单的网页爬虫程序,以及一些常用的技巧和注意事项。一、准备工作安装C++编译器...
    99+
    2023-11-04
    C++ 网页爬虫 程序实现
  • 基于Java怎样实现一个简单的单词本Android App
    这篇文章跟大家分析一下“基于Java怎样实现一个简单的单词本Android App”。内容详细易懂,对“基于Java怎样实现一个简单的单词本Android App”感兴趣的朋友可以跟着小编的思路慢慢深入来阅读一下,希望阅...
    99+
    2023-06-29
  • 如何利用C++实现一个简单的网站访问统计程序?
    随着互联网的迅速发展,越来越多的网站开始关注网站访问数据的统计,并将这些数据用于网站的优化和改进。因此,开发一个简单的网站访问统计程序对于网站管理者来说非常有用。而其中一个实现这一目标的可能性是通过使用C++,该语言可以帮助您以更高效的方式...
    99+
    2023-11-04
    网站 统计 访问
  • 在C#程序编译另一个程序的实现方法是怎样的
    本篇文章为大家展示了在C#程序编译另一个程序的实现方法是怎样的,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。C#程序编译编译前,要用到VS2005提供的一个编译工具 devenv.exe,这个在VS...
    99+
    2023-06-18
  • C#中怎么实现一个日历样式的下拉式计算器
    C#中怎么实现一个日历样式的下拉式计算器,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。本文介绍了如何在Visual Studio中创建用户控件来显示下拉式计算器...
    99+
    2023-06-17
  • 怎么使用nodejs实现一个简单的网页爬虫功能
    这篇文章主要介绍了怎么使用nodejs实现一个简单的网页爬虫功能,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。网页源码  使用http.get()方法获取网页源码,以hao1...
    99+
    2023-06-06
  • Python实现一个简单三层神经网络的搭建及测试 代码解析
    目录1.初始化2.预测3.训练4.测试 废话不多说了,直接步入正题,一个完整的神经网络一般由三层构成:输入层,隐藏层(可以有多层)和输出层。本文所构建的神经网络隐藏层只有一层。一个神...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作