东风草堂博客

公众号:开发者来风

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
template <typename T>
class ThreadSafePointer {
public:
ThreadSafePointer() : data(nullptr), ref_count(new size_t(0)), mutex(new std::mutex()) {}

ThreadSafePointer(T* ptr) : data(ptr), ref_count(new size_t(1)), mutex(new std::mutex()) {}

ThreadSafePointer(const ThreadSafePointer<T>& other) {
std::lock_guard<std::mutex> lock(*other.mutex);
data = other.data;
ref_count = other.ref_count;
(*ref_count)++;
mutex = other.mutex;
}

ThreadSafePointer& operator=(const ThreadSafePointer<T>& other) {
if (this == &other) return *this;

release();
std::lock_guard<std::mutex> lock(*other.mutex);
data = other.data;
ref_count = other.ref_count;
(*ref_count)++;
mutex = other.mutex;
return *this;
}

~ThreadSafePointer() {
release();
}

T* get() const {
std::lock_guard<std::mutex> lock(*mutex);
return data;
}

size_t use_count() const {
std::lock_guard<std::mutex> lock(*mutex);
return *ref_count;
}

private:
T* data;
size_t* ref_count;
std::mutex* mutex;

void release() {
std::lock_guard<std::mutex> lock(*mutex);
if (ref_count && --(*ref_count) == 0) {
delete data;
delete ref_count;
delete mutex;
}
}
};

ThreadSafePointer<int> ptr1(new int(42));
{
ThreadSafePointer<int> ptr2 = ptr1;
std::cout << "ptr2: " << *ptr2.get() << " (use count: " << ptr2.use_count() << ")" << std::endl;
}

std::cout << "ptr1: " << *ptr1.get() << " (use count: " << ptr1.use_count() << ")" << std::endl;

上面的代码实现了一个简单的线程安全智能指针 ThreadSafePointer,它用于管理一个动态分配的对象(例如 int)。以下是代码的原理解释:

  1. 类模板定义:

    • ThreadSafePointer 类是一个模板类,可以管理不同类型的动态分配对象。
  2. 成员变量:

    • data:指向实际数据的指针。
    • ref_count:一个指向引用计数的指针。引用计数表示有多少个智能指针共享相同的资源。
    • mutex:一个互斥锁,用于保护 dataref_count 的访问。
  3. 构造函数:

    • 默认构造函数:初始化 datanullptrref_count 为 0,mutex 为一个新的互斥锁。
    • 构造函数接受指向资源的原始指针,将 data 指向该资源,初始化 ref_count 为 1,创建一个新的互斥锁。
  4. 复制构造函数:

    • 复制构造函数被用于创建一个新的 ThreadSafePointer 对象,该对象共享相同的资源。
    • 它通过获取另一个智能指针的互斥锁来保证线程安全,并增加引用计数。
    • 共享的 ref_countmutex 在对象之间共享,以确保线程安全的引用计数。
  5. 赋值运算符重载:

    • 赋值运算符用于将一个 ThreadSafePointer 对象赋值给另一个对象。
    • 在这个实现中,首先释放调用对象的资源(如果有),然后像复制构造函数一样,共享另一个对象的资源。
  6. 析构函数:

    • 析构函数用于释放资源。
    • 它首先获取互斥锁以确保线程安全,然后检查引用计数。如果引用计数减为零,表示没有智能指针引用该资源,就释放资源并删除引用计数和互斥锁。
  7. get 方法:

    • get 方法返回存储在 ThreadSafePointer 中的指向资源的原始指针。在获取指针时,它会获取互斥锁以确保线程安全。
  8. use_count 方法:

    • use_count 方法返回当前资源的引用计数,以确定有多少个智能指针引用了该资源。同样,它也获取互斥锁以确保线程安全。
  9. main 函数:

    • main 函数中,我们演示了如何创建和使用 ThreadSafePointer 对象来管理整数资源。创建一个 ThreadSafePointer 对象时,资源的引用计数初始化为 1。然后,通过复制构造函数创建第二个智能指针,两个智能指针现在共享同一资源,引用计数增加到 2。

这个实现确保在多线程环境中正确管理资源的生命周期,通过使用互斥锁来保护引用计数和资源的访问,避免了竞态条件和资源泄漏。

倒置二叉树
二叉树遍历
实现一个单例类
二叉排序树实现插入算法
手写一个智能指针实现,线程安全的智能指针
找出一个字符串s包含字符串t的最小重复字符串 - 最小覆盖子串
一个n的数组,有一些随机数据,怎么抽出m长度的数据,保证数组的数据唯一,方法可能有很多,面试官会探讨最优方法
给一个任意整数组,剔除一个元素后,得出最大乘积例如:[4]int{2,3,4,-4} 最大乘积 24 情况A:奇数个负数;情况B:偶数(包括0)个负数,子情况:没有非负数;
怎么从一对数里面最快找到最大值和最小值 - 数组中最大数对和的最小值
给出一个字符串N “ABBCDB”(大小英文字母组成) ,按照一下任意一个规则,一:从头部删除一个字母,并追加到新字符串尾;二:从尾删除一个字母,并追加到新字符串尾;期望最后得到一个字典排序最小的字符串 - 选择每一步操作中字典序较小的方式?
有一个任意的整形数组,[]int,从数组取出任意一个元素是的它是符合下面条件的,一:它的左边都比它小,二它的右边都比它大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 计算每个位置左边的最大值
leftMax[0] = arr[0]
for i := 1; i < n; i++ {
leftMax[i] = max(leftMax[i-1], arr[i-1])
}

// 计算每个位置右边的最小值
rightMin[n-1] = arr[n-1]
for i := n - 2; i >= 0; i-- {
rightMin[i] = min(rightMin[i+1], arr[i+1])
}

// 检查每个位置是否满足条件
for i := 0; i < n; i++ {
if arr[i] > leftMax[i] && arr[i] < rightMin[i] {
return arr[i]
}
}

10000个整数的升序数组,无重复数,找出最接近目标数的下标

1
2
3
4
5
6
7
8
// 如果查找失败,判断最接近目标数的元素是array[left]还是array[right]
if (abs(array[left] - target) < abs(array[right] - target)) {
// 如果array[left]更接近,返回left
return left;
} else {
// 如果array[right]更接近,返回right
return right;
}

2根燃烧速度不同的香,每根1小时可燃完,如何测算出15分钟的时间

阅读全文 »

问题原因

EmailMultiAlternatives模块发送StringIO的数据作为csv的附件,在windows打开csv文件后乱码,修改如下部分后,附件为utf-8编码格式,所以在windows上打开csv文件是乱码的,因为wps打开默认是按ansi格式解码,但将csv文件在notepad中是可以正常打开的,因为notepad可以按照utf-8解码。

1
2
b = make_header([('数据归档.csv', 'utf-8')]).encode('utf-8')
email.attach(b, csvfile.getvalue().encode('utf-8'))

解决方案

可以在windows上将csv文件转码为asci格式的gbk编码,但是表情会变为问号,还不清楚是什么原因,或者将csv文件在手机wps打开,再另存为xls文件,再发给电脑就好了,但是很麻烦。

阅读全文 »

本程序旨在通过检查当前网络是否支持 IPv6 服务,以及判断指定的网络接口是否拥有全局 IPv6 地址。以下是程序的主要思路和函数概述。

1. getInterfaceNameFromSockaddr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
std::string getInterfaceNameFromSockaddr(const struct sockaddr_in &sockAddr)
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
return "";
}

struct ifconf ifc;
char buf[1024]; // 大小根据实际情况调整
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;

if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) == -1)
{
close(sockfd);
return "";
}

std::string interfaceName = "";
int num = ifc.ifc_len / sizeof(struct ifreq);
struct ifreq *ifr = ifc.ifc_req;

for (int i = 0; i < num; ++i)
{
struct sockaddr_in *addr = reinterpret_cast<struct sockaddr_in *>(&ifr[i].ifr_addr);

if (addr->sin_family == AF_INET)
{
char ipStr[INET_ADDRSTRLEN];
const char *ip = inet_ntop(AF_INET, &(addr->sin_addr), ipStr, INET_ADDRSTRLEN);
std::cout << "guess IP address: " << ip << std::endl;

if (ip != nullptr && std::memcmp(&(sockAddr.sin_addr), &(addr->sin_addr), sizeof(struct in_addr)) == 0)
{
interfaceName = ifr[i].ifr_name;
std::cout << "guessed!" << std::endl;
break;
}
}
}

close(sockfd);
return interfaceName;
}

思路:

  • 通过创建一个 IPv4 套接字,建立到一个虚拟地址的连接。
  • 获取本地套接字的信息,即获取与之连接的本地 IP 地址。
  • 遍历系统网络接口信息,根据 IP 地址的匹配找到对应的网络接口名称。
阅读全文 »

第一段四句:

Nowadays, an increasing number of people are concerned about 题目关键词 all over the world.
However,whether 辩论两边观点(单边观点也行) is a controversial issue.
From my perspective, it exerts an adverse/ a profound impact on (government, environment,关键词涉及的人事物都可以)
Therefore, sb should pay more attention to … rather than …

第二段三句:

To begin with, it is undeniable that… plays a significant role in…, such as…
That is,… (改写第一句话)
As a result,… (表结果,可以跟给的例子有关,没关也行), which will lead to…

第三段四句:

阅读全文 »

1
2
gpt prompt:
我现在正在练习英语写作,你作为资深英语专家,你能帮我看看我写的句子有没有什么问题吗?并给出优化后的句子

day 1

Today, I am preparing to write an article to improve my English level, I hope it will have a positive effect on my English level. The main reason is that I will have a PTE exam in a few weeks. The PTE exam is divided into listening, reading, speeking, and writing parts, I feel week in all parts. I want to achieve a good score because I intend to work in Australia. My goal is to score at least 65 points in all parts, which would classify my English as proficient. It’s a big challege for me because I haven’t been lerning English since I graduated from university. I look forward to having a life of freedom, but I find myself working from home every day. Moreover I’m concerned that if I don’t take this seriously, I might lose my job by the time I turn 35. Therefore, I’m exploring solutions to potentially move to Australia.

I’ve writen down some words and handled them over to ChatGPT for sentence optimization, this way, I aim to enhance my ability to express myself in English. Effective communication requires a long grasp of expression. For instance, the ability to quickly organize sentences on the spot, without prolonged pauses for thought.

I’ve acquired 200 new words, I plan to learn an aditional 200 every day. This is primarily to improve my ability to pronounce them quickly, as I might encounter them in the reading section of the PTE exam, allowing me to read more fluently.

阅读全文 »

1
https://cloud.tencent.com/developer/article/2215771

p2p server

需要通过stun穿透协议定时与穿透服务通信,即在定时器线程里面发送SStunLogin,再在epoll里面udp接收SStunLoginResponse表示登录成功,同时epoll里面udp需要解析SStunTransfer穿透命令,收到穿透命令后需要回复对方一个字节的包。

一个字节的穿透包不一定能发出去,比如当p2p client发起打洞标记时请求的p2p server port和p2p server外部端口对不上,那打的标记就没用,或者p2p client不是严格意义上的nat 3网络(外部端口会隔段时间变化),或者是nat 4对称型网络,基本打洞不成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// 定时器线程:每隔30秒发送STUN登录请求
std::thread timer_thread([&]()
{
while (true) {
static unsigned int s_seq = 0;
int size = sizeof(SStunLogin) + g_strDeviceId.size() - 1;
char* buf = new char[size];
SStunLogin* p = (SStunLogin*)buf;
p->head.len = htons(size);
p->head.cmd = htons(STUN_CMD_LOGIN);
p->head.seq = htonl(++s_seq);
p->nat = g_finalNatType;
memcpy_s(p->uid, size - sizeof(SStunLogin) + 1, g_strDeviceId.c_str(), g_strDeviceId.size());
sendto(sockfd, buf, size, 0, (const sockaddr*)&server_addr, sizeof(server_addr));
delete[] buf;
printf("udp stun alive seq = %u\n", s_seq);
sleep(30);
} });

// 创建epoll
int epoll_fd = epoll_create(1);
if (epoll_fd < 0)
{
std::cerr << "Failed to create epoll" << std::endl;
return 1;
}

// 添加UDP套接字到epoll
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = sockfd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &event) < 0)
{
std::cerr << "Failed to add socket to epoll" << std::endl;
return 1;
}

// epoll等待事件
const int MAX_EVENTS = 10;
struct epoll_event events[MAX_EVENTS];
while (true)
{
int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (num_events < 0)
{
std::cerr << "Error in epoll_wait" << std::endl;
return 1;
}

for (int i = 0; i < num_events; ++i)
{
if (events[i].data.fd == sockfd && events[i].events & EPOLLIN)
{
char buf[1500];
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
ssize_t num_bytes = recvfrom(sockfd, &buf, sizeof(buf), 0,
(struct sockaddr *)&client_addr, &addr_len);
if (num_bytes < 0)
{
std::cerr << "Error in recvfrom" << std::endl;
continue;
}

printf("recvfrom %s_%d size = %ld\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), num_bytes);

if (sizeof(SStunLoginResponse) == num_bytes)
{
// 尝试解析成穿透响应
SStunLoginResponse *slr = (SStunLoginResponse *)buf;
if (ntohs(slr->head.cmd) == STUN_CMD_LOGIN && ntohs(slr->head.len) == num_bytes)
{
printf("recv stun alive result = %d,seq = %u,ip = %s,port = %d\n", (int)slr->result, ntohl(slr->head.seq),
IpInt2Str(slr->ip).c_str(), ntohs(slr->port));
}
}
if (sizeof(SStunTransfer) == num_bytes)
{
// 尝试解析成穿透命令
SStunTransfer *st = (SStunTransfer *)buf;
if (ntohs(st->head.cmd) == STUN_CMD_TRANSFER && ntohs(st->head.len) == num_bytes)
{
SStunResponse response;
response.head.cmd = st->head.cmd;
response.head.seq = st->head.seq;
response.head.len = htons(sizeof(response));
response.result = 0;
int tmpRet = sendto(sockfd, (const char *)&response, sizeof(response), 0, (sockaddr *)&client_addr, addr_len);
if (tmpRet != sizeof(response))
{
printf("sendto %s_%d ret = %d,error = %d\n", IpInt2Str(client_addr.sin_addr.s_addr).c_str(), ntohs(client_addr.sin_port), tmpRet,
errno);
}
client_addr.sin_addr.s_addr = st->ip;
client_addr.sin_port = st->port;
tmpRet = sendto(sockfd, "0", 1, 0, (sockaddr *)&client_addr, addr_len);
if (tmpRet != 1)
{
printf("sendto %s_%d ret = %d,error = %d\n", IpInt2Str(client_addr.sin_addr.s_addr).c_str(), ntohs(client_addr.sin_port), tmpRet,
errno);
}
printf("stun to %s_%d\n", IpInt2Str(client_addr.sin_addr.s_addr).c_str(), ntohs(client_addr.sin_port));
}
}
else
{
std::cout << "receive data " << string(buf, num_bytes) << std::endl;
}
}
}
}
阅读全文 »

1
https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio/reference.html

知识点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# https://sourceforge.net/projects/boost/
# https://www.boost.org/users/history/
#boost 1.60
./bootstrap.sh --prefix=$(pwd)/app --with-libraries=atomic,system,filesystem,chrono,thread,date_time,exception,iostreams
vim project-config.jam
# using gcc : arm64 : /root/wxz_test/sdk_package/toolchain/bin/aarch64-openwrt-linux-musl-g++ ;
./b2 cxxflags="-std=c++0x -fPIC" cflags="-fPIC" -j7
./b2 install
# or
./b2 -a --with-program_options --with-atomic --with-date_time --with-system --with-thread --with-chrono --with-filesystem --with-serialization cxxflags="-std=c++0x -fPIC" cflags="-fPIC" -j7 install

# nm libboost_system.a | c++filt | grep "boost"

# 有的平台使用时,需要加这几个头,否则报错undefined reference to boost::system::system_category()
# 如果编译没有报错,则不要轻易加,会导致boost asio async_connect一直报错Operation already in progress
-DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_SYSTEM_NO_DEPRECATED -DBOOST_CHRONO_HEADER_ONLY

# /root/wxz_test/sdk_package/toolchain/bin/aarch64-openwrt-linux-g++ boosttest.cpp -o boosttest -std=c++11 -I/root/wxz_test/boost_1_60_0/app/include -L /root/wxz_test/boost_1_60_0/app/lib/ -l:libboost_system.a -l:libboost_thread.a -lpthread

ibevent是基于Reactor(反应器模式)。boost.asio是基于Proactor(主动器模式)。
io_context类似于reactor或iocp,有两个重要的命名空间boost::asio和boost::asio::ip
boost::asio下的io函数有connect、accept、read_some、write_some,异步io加个async即可。
boost::asio::ip主要有ip地址ip::address,端点ip::tcp::endpoint\ip::udp::address,sockect:ip::tcp::socket\ip::udp::socket.
套接字控制:set_option\get_option\io_control

1
2
3
// TCP套接字可以重用地址
ip::tcp::socket::reuse_address ra(true);
sock.set_option(ra);
阅读全文 »

命令记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
yum -y install dnsmasq

vim /etc/dnsmasq.conf
resolv-file=/etc/resolv.dnsmasq.conf
strict-order
listen-address=127.0.0.1,10.0.16.15
addn-hosts=/etc/dnsmasq.hosts
conf-dir=/etc/dnsmasq.d,.rpmnew,.rpmsave,.rpmorig

vim /etc/resolv.dnsmasq.conf
nameserver 127.0.0.1
nameserver 114.114.114.114
nameserver 8.8.8.8

vim /etc/dnsmasq.hosts
# 输入你需要解析的ip和域名

service dnsmasq restart
netstat -tlunp|grep 53
0%