东风草堂博客

公众号:开发者来风

问题原因

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

MySQL 体系结构

  • 连接层
    最上层是一些客户端和链接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
  • 服务层
    第二层架构主要完成大多数的核心服务功能,如 SQL 接口,并完成缓存的查询, SQL 的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如过程、函数等。
  • 引擎层
    存储引擎真正的负责了 MySQL 中数据的存储和提取,服务器通过 API 和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。
  • 存储层
    主要是将数据存储在文件系统之上,并完成与存储引擎的交互。

show create table account,查看建表语句,engine指定存储引擎。show engins查询支持的引擎。

InnoDB

阅读全文 »


传统的缓存策略一般是请求到达 Tomcat 后,先查询 Redis ,如果未命中则查询数据库,存在下面的问题:

  • 请求要经过 Tomcat 处理, Tomcat 的性能成为整个系统的瓶颈
  • Redis 缓存失效时,会对数据库产生冲击

多级缓存方案

用作缓存的 Nginx 是业务 Nginx ,需要部署为集群,再有专门的 Nginx 用来做反向代理(proxy_pass+upstream配置):

缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。我们把缓存分为两类:

  • 分布式缓存,例如 Redis :
    • 优点:存储容量更大、可靠性更好、可以在集群间共享.缺点:访问缓存有网络开销
    • 场景:缓存数据量较大、可靠性要求较高、需要在集群间共享
  • 进程本地缓存,例如 HashMap 、 GuavaCache :
    • 优点:读取本地内存,没有网络开销,速度更快·缺点:存储容量有限、可靠性较低、无法共享
    • 场景:性能要求较高,缓存数据量较小
阅读全文 »
0%