0%

参考链接

monitoring-linux-networking-state-using-netlink

python重写

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
import struct
import socket
import os

nlmsghdr_format = "=IHHII"
ifinfomsg_format = "=BBHiII"
ifaddrmsg_format = "=BBBBI"
rtattr_format = "=HH"
nlmsghdr_size = 16
ifinfomsg_size = 16
ifaddrmsg_size = 8
rtattr_size = 4

RTM_NEWROUTE = 24
RTM_DELROUTE = 25
IFA_LOCAL = 2
IFLA_IFNAME = 3
IFLA_MAX = 51
RTMGRP_LINK = 1
RTMGRP_IPV4_IFADDR = 0x10
RTMGRP_IPV4_ROUTE = 0x40
IFF_UP = 0x1
IFF_RUNNING = 0x40
RTM_DELADDR = 21
RTM_DELLINK = 17
RTM_NEWLINK = 16
RTM_NEWADDR = 20


sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
sock.bind((os.getpid(), RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE))

def parseRtattr(rta, msg_len, ifla_rta_ix):
RTA_ALIGNTO = 4
RTA_ALIGN = lambda Length : (Length + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)
tb = [None for _ in range(IFLA_MAX + 1)]
point, size = 0, len(rta)
rta_len, rta_type = struct.unpack(rtattr_format, rta[point : rtattr_size])
while msg_len >= rtattr_size and rtattr_size <= rta_len <= msg_len and point + rtattr_size <= size:
rta_len, rta_type = struct.unpack(rtattr_format, rta[point : point + rtattr_size])
print("point is {}".format(point))
if rta_type <= IFLA_MAX:
print("rta_type is {}, rta_len is {}, rta is {}".format(rta_type, rta_len, ifla_rta_ix + point))
tb[rta_type] = ifla_rta_ix + point + rtattr_size
point += RTA_ALIGN(rta_len)
msg_len -= RTA_ALIGN(rta_len)

return tb


while True:
data = sock.recv(8192)
nlmsghdr = data[0:nlmsghdr_size]

msg_len, msg_type, flags, seq, pid = struct.unpack(nlmsghdr_format, nlmsghdr)
if msg_type == RTM_NEWROUTE or msg_type == RTM_DELROUTE:
print("Routing table was changed")
continue
# print("msg_len={}\t msg_type={}\t flags={}\t seq={}\t pid={}".format(msg_len, msg_type, flags, seq, pid))
# print(len(data))
# parser struct ifinfomsg
# unsigned really is a shorthand for unsigned int, and so defined in standard C
# https://stackoverflow.com/questions/1171839/what-is-the-unsigned-datatype
ifinfomsg = data[nlmsghdr_size : nlmsghdr_size + ifinfomsg_size]
ifi_family, __ifi_pad, ifi_type, ifi_index, ifi_flags, ifi_change = struct.unpack(ifinfomsg_format, ifinfomsg)
# print("ifi_family={}\t__ifi_pad={}\tifi_type={}\tifi_index={}\tifi_flags={}\tifi_change={}".format(ifi_family, __ifi_pad, ifi_type, ifi_index, ifi_flags, ifi_change))

ifla_rta = data[nlmsghdr_size + ifinfomsg_size :]
tb = parseRtattr(ifla_rta, msg_len, nlmsghdr_size + ifinfomsg_size)

if tb[IFLA_IFNAME]:
# may
for ix in range(tb[IFLA_IFNAME], len(data)):
if data[ix] == '\x00': break
ifName_format = "=" + "c" * (ix - tb[IFLA_IFNAME])
ifName = ''.join(struct.unpack(ifName_format, data[tb[IFLA_IFNAME] : ix]))
print("ifName is {}".format(ifName))
if ifi_flags & IFF_UP:
ifUpp = "UP"
else:
ifUpp = "DOWN"
print(ifUpp)
if ifi_flags & IFF_RUNNING:
ifRunn = "RUNNING"
else:
ifRunn = "NOT RUNNING"
print(ifRunn)
ifaddrmsg = data[nlmsghdr_size : nlmsghdr_size + ifaddrmsg_size]
ifa_family, ifa_prefixlen, ifa_flags, ifa_scope, ifa_index = struct.unpack(ifaddrmsg_format, ifaddrmsg)
ifa = data[nlmsghdr_size + ifaddrmsg_size :]
tba = parseRtattr(ifa, msg_len, nlmsghdr_size + ifaddrmsg_size)
if tba[IFA_LOCAL]:
for i in data[tba[IFA_LOCAL] : tba[IFA_LOCAL] + 4]:
print(ord(i))
ifAddress = '.'.join(str(i) for i in struct.unpack('=BBBB', data[tba[IFA_LOCAL] : tba[IFA_LOCAL] + 4 ]))
if msg_type == RTM_DELADDR:
print("Interface {}: address was removed".format(ifName))
pass
elif msg_type == RTM_DELLINK:
print("Network interface {} was removed".format(ifName))
pass
elif msg_type == RTM_NEWLINK:
print("New network interface {}, state: {} {}".format(ifName, ifUpp, ifRunn))
pass
elif msg_type == RTM_NEWADDR:
print("Interface {}: new address was assigned: {}".format(ifName, ifAddress))
pass

close(sock)

epoll的两种工作模式

LT(水平触发),ET(边缘触发)

LT模式: 支持阻塞和非阻塞套接字
ET模式: 只支持非阻塞套接字

胡思乱想

2022年对我来说真的是一个非常不平常的一年。悲伤应该是我今年的关键词。几乎大部分闲暇时间都是悲伤度过的。我有无数次想通过写博客来排毒。 对于这些种种的消极情绪,我知道不好,我也知道自己很naive。可是仿佛是控制不住似的,我也无力改变。so,我非常感谢我周围的朋友。没有这些朋友我可能真的会crash掉。
感情是一个非常珍贵的东西。两个人彼此相信,彼此宠溺,共同面对共同的未来。都是非常难得的事情。毕竟每个人成长的环境不同,接触到的教育信息不同,彼此相互兼容真的是很难的。或许是出于职业的天真,程序员天生是要修bug的,所以当我遇到问题的时候,都是在想这个问题,我能解,我可以单步调试,定位问题,解决问题。所以我自责,我焦虑,我后悔。

今年的有段时间非常喜欢这样的一句话:当你老了回顾一生就会发觉:何时出国读书、何时决定做第一份职业、何时选定了对象而恋爱、何时结婚,其实都是命运的巨变。只是当时站在三岔路口,眼见风云千樯,你作出抉择的那一日,在日记上,相当的沉闷和平凡,当时还以为是生命中普通的一天。 作为小镇错题家的我,是没有什么深谋远虑的规划,甚至还有好多不懂事的地方。所以我对于自己的好多决定都是基于贪心算法的。基于当时的定义域求一个局部的最优解。所以我从厦门到了深圳,又从深圳来了南京。而且当时做这些决定的时候,真的是无所畏惧。义无反顾。

在知乎上看到这样的观点:现代人的精神内耗非常严重。其中核心的原因是——焦虑源于无能,抑郁产自孤独。无能:照顾不好一个人(自己),照顾不好两个人(识人、脱单、经营亲密关系)… 孤独:和自己想法相似的人越来越少,能融入的圈子越来越少。
怎么才能解决这些精神内耗呢?我想肯定不是所谓的二舅。我认同的想法是——爱能治愈一切,做一个温柔且有力量的人。

奇怪的知识

sicp是一本好书,对我的编码能力提升了不少。让我更熟练的使用lambda表达式,理解dfs,体会抽象的作用。关于抽象,我想说两句。抽象是优雅的,举个例子,你无需详细了解http报文的细节。但你可以优雅的使用requests去做爬虫。你也可以优雅的使用flask去做web开发。这里也解释了什么是框架。
抽象也有evil的一面(个人观点)。对自学的小白是非常的不友好。因为小白不知道自己处在哪个抽象层,所以可能会产生盲目的浪费时间。当然抽象的东西太多的时候,对于初次接触框架代码的人,很容易产生畏难,劝退的情绪。csapp是一本好书,我也曾通宵去完成拆炸弹这个实验。jyy老师,是我目前接触到的国内最高质量计算机课程。

记录一下力扣今年的刷题情况,好像比我前几年的总刷题量都要多。

可是最近让我担心的是,我现在所处的环境需要我这么卷的去学这些东西吗?或者说我还有机会吗?毕竟一个人的命运,当然要靠自我奋斗,但也要考虑历史的进程。

天马行空的想法

时间很宝贵,我希望自己做些有用的事。提高姿(知)势(识)水平。一切都在进步,2023继续加油。

近期接触到一些事件处理,异步IO,HTTP请求相关的代码,例如samba的tevent和ceph的radosgw。自己对这块知识也是非常感兴趣。最终选择libevent作为切入点,学习一波相关知识。

a simple HTTP request

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
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For gethostbyname */
#include <netdb.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>

int main(int c, char **v)
{
const char query[] =
"GET / HTTP/1.0\r\n"
"Host: www.baidu.com\r\n"
"\r\n";
const char hostname[] = "www.baidu.com";
struct sockaddr_in sin;
struct hostent *h;
const char *cp;
int fd;
ssize_t n_written, remaining;
char buf[1024];

/* Look up the IP address for the hostname. Watch out; this isn't
threadsafe on most platforms. */
h = gethostbyname(hostname);
if (!h) {
fprintf(stderr, "Couldn't lookup %s: %s", hostname, hstrerror(h_errno));
return 1;
}
if (h->h_addrtype != AF_INET) {
fprintf(stderr, "No ipv6 support, sorry.");
return 1;
}

/* Allocate a new socket */
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return 1;
}

/* Connect to the remote host. */
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
sin.sin_addr = *(struct in_addr*)h->h_addr;
if (connect(fd, (struct sockaddr*) &sin, sizeof(sin))) {
perror("connect");
close(fd);
return 1;
}

/* Write the query. */
/* XXX Can send succeed partially? */
cp = query;
remaining = strlen(query);
while (remaining) {
n_written = send(fd, cp, remaining, 0);
if (n_written <= 0) {
perror("send");
return 1;
}
remaining -= n_written;
cp += n_written;
}

/* Get an answer back. */
while (1) {
ssize_t result = recv(fd, buf, sizeof(buf), 0);
if (result == 0) {
break;
} else if (result < 0) {
perror("recv");
close(fd);
return 1;
}
fwrite(buf, 1, result, stdout);
}

close(fd);
return 0;
}

上面是一个简单的例子,我们用到的每个函数都是阻塞的。例如当我们调用connect(fd, (struct sockaddr*) &sin, sizeof(sin))时,我们必须等待该函数返回结果,这样才能执行下一行语句。如果我们的需求就是简单的访问一下百度的主页,那么这样的代码是没有问题的。可是现实生活中,我们可以同时访问多个网页,我们肯定是不允许要等待前面的请求完成后,才能访问后边的网页。我想要的效果是并发的。
题外话:其实这个例子蛮有意思的。想象一下平时通过浏览器浏览网页的底层实现就是通过简单的socket连接,可以亲自动手读写HTTP请求。课本上枯燥的知识,可以得到实操。渐渐的理解计算机世界的一层层抽象。体会抽象的强大。当然也不难联想到真世界的产品,像windows,mac,ios,android,它们构建出了一层强大的抽象,让我们的生活更加便利,精彩。不然的话,如果想要浏览网页,必须掌握http报文格式,一个个字符输入终端。我想大多数人都是不允许这样糟糕的事情发生。

Block ROT13 server

解释一下ROT13就是对读取到字符做一个简单的处理。

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
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX_LINE 16384

char rot13_char(char c)
{
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}

void child(int fd)
{
char outbuf[MAX_LINE+1];
size_t outbuf_used = 0;
ssize_t result;

while (1) {
char ch;
result = recv(fd, &ch, 1, 0);
if (result == 0) {
break;
} else if (result == -1) {
perror("read");
break;
}

/* We do this test to keep the user from overflowing the buffer. */
if (outbuf_used < sizeof(outbuf)) {
outbuf[outbuf_used++] = rot13_char(ch);
}

if (ch == '\n') {
send(fd, outbuf, outbuf_used, 0);
outbuf_used = 0;
continue;
}
}
}

void run(void)
{
int listener;
struct sockaddr_in sin;

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(40713);

listener = socket(AF_INET, SOCK_STREAM, 0);

#ifndef WIN32
{
int one = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif

if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
return;
}

if (listen(listener, 16)<0) {
perror("listen");
return;
}

while (1) {
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
if (fd < 0) {
perror("accept");
} else {
child(fd);
}
}
}

int main(int c, char **v)
{
run();
return 0;
}

体验一下阻塞的效果

我们可以看到只有第一个连接的client是可以正常得到服务的,另外的两个连接都是处于阻塞状态。这显然是不合理的。

Forking ROT13 server

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
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX_LINE 16384

char
rot13_char(char c)
{
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}

void
child(int fd)
{
char outbuf[MAX_LINE+1];
size_t outbuf_used = 0;
ssize_t result;

while (1) {
char ch;
result = recv(fd, &ch, 1, 0);
if (result == 0) {
break;
} else if (result == -1) {
perror("read");
break;
}

/* We do this test to keep the user from overflowing the buffer. */
if (outbuf_used < sizeof(outbuf)) {
outbuf[outbuf_used++] = rot13_char(ch);
}

if (ch == '\n') {
send(fd, outbuf, outbuf_used, 0);
outbuf_used = 0;
continue;
}
}
}

void
run(void)
{
int listener;
struct sockaddr_in sin;

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(40713);

listener = socket(AF_INET, SOCK_STREAM, 0);

#ifndef WIN32
{
int one = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif

if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
return;
}

if (listen(listener, 16)<0) {
perror("listen");
return;
}

while (1) {
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
if (fd < 0) {
perror("accept");
} else {
if (fork() == 0) {
child(fd);
exit(0);
}
}
}
}

int main(int c, char **v)
{
run();
return 0;
}

哇,我们可以同时处理多个请求啦。

同时我们还可以在server端还可以简单的分析一下fork_ROT13的情况。

似乎多进程或多线程可以完美的解决问题。但是考虑到实际情况,显然是不合理的。细细想一下当面对成千上万的连接时,使用多进程或多线程的代价是非常昂贵的。这里涉及到操作系统层面的进程切换和线程切换。

Example: Using select

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>
/* for select */
#include <sys/select.h>

#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#define MAX_LINE 16384

char
rot13_char(char c)
{
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}

struct fd_state {
char buffer[MAX_LINE];
size_t buffer_used;

int writing;
size_t n_written;
size_t write_upto;
};

struct fd_state * alloc_fd_state(void)
{
struct fd_state *state = malloc(sizeof(struct fd_state));
if (!state)
return NULL;
state->buffer_used = state->n_written = state->writing =
state->write_upto = 0;
return state;
}

void free_fd_state(struct fd_state *state)
{
free(state);
}

void make_nonblocking(int fd)
{
fcntl(fd, F_SETFL, O_NONBLOCK);
}

int do_read(int fd, struct fd_state *state)
{
char buf[1024];
int i;
ssize_t result;
while (1) {
result = recv(fd, buf, sizeof(buf), 0);
if (result <= 0)
break;

for (i=0; i < result; ++i) {
if (state->buffer_used < sizeof(state->buffer))
state->buffer[state->buffer_used++] = rot13_char(buf[i]);
if (buf[i] == '\n') {
state->writing = 1;
state->write_upto = state->buffer_used;
}
}
}

if (result == 0) {
return 1;
} else if (result < 0) {
if (errno == EAGAIN)
return 0;
return -1;
}

return 0;
}

int do_write(int fd, struct fd_state *state)
{
while (state->n_written < state->write_upto) {
ssize_t result = send(fd, state->buffer + state->n_written,
state->write_upto - state->n_written, 0);
if (result < 0) {
if (errno == EAGAIN)
return 0;
return -1;
}
assert(result != 0);

state->n_written += result;
}

if (state->n_written == state->buffer_used)
state->n_written = state->write_upto = state->buffer_used = 0;

state->writing = 0;

return 0;
}

void run(void)
{
int listener;
struct fd_state *state[FD_SETSIZE];
struct sockaddr_in sin;
int i, maxfd;
fd_set readset, writeset, exset;

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(40713);

for (i = 0; i < FD_SETSIZE; ++i)
state[i] = NULL;

listener = socket(AF_INET, SOCK_STREAM, 0);
make_nonblocking(listener);

#ifndef WIN32
{
int one = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif

if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
return;
}

if (listen(listener, 16)<0) {
perror("listen");
return;
}

FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_ZERO(&exset);

while (1) {
maxfd = listener;

FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_ZERO(&exset);

FD_SET(listener, &readset);

for (i=0; i < FD_SETSIZE; ++i) {
if (state[i]) {
if (i > maxfd)
maxfd = i;
FD_SET(i, &readset);
if (state[i]->writing) {
FD_SET(i, &writeset);
}
}
}

if (select(maxfd+1, &readset, &writeset, &exset, NULL) < 0) {
perror("select");
return;
}

if (FD_ISSET(listener, &readset)) {
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
if (fd < 0) {
perror("accept");
} else if (fd > FD_SETSIZE) {
close(fd);
} else {
make_nonblocking(fd);
state[fd] = alloc_fd_state();
assert(state[fd]);/*XXX*/
}
}

for (i=0; i < maxfd+1; ++i) {
int r = 0;
if (i == listener)
continue;

if (FD_ISSET(i, &readset)) {
r = do_read(i, state[i]);
}
if (r == 0 && FD_ISSET(i, &writeset)) {
r = do_write(i, state[i]);
}
if (r) {
free_fd_state(state[i]);
state[i] = NULL;
close(i);
}
}
}
}

int main(int c, char **v)
{
setvbuf(stdout, NULL, _IONBF, 0);
run();
return 0;
}

ok,似乎select就够了,但是select的性能是和fd的个数成正比的。所以当fd数量越来越多的时候,select()的性能损耗也就越大。
不同的操作系统提供了不同的替换函数,像poll(),epoll(), kqueue()等。不幸的是,没有一个有效的,通用的,普遍存在的接口。因此libevent封装了这些接口,提供了一个更好的抽象接口。

安装和使用libevent

  1. 安装
    1
    $ sudo apt-get install libevent-dev
  2. 使用
    1
    gcc demo_libevent.c -o demo_libevent -levent
    其实就是编译的时候记得加-levent.

A low-level ROT13 server with Libevent

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>

#include <event2/event.h>

#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#define MAX_LINE 16384

void do_read(evutil_socket_t fd, short events, void *arg);
void do_write(evutil_socket_t fd, short events, void *arg);

char
rot13_char(char c)
{
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}

struct fd_state {
char buffer[MAX_LINE];
size_t buffer_used;

size_t n_written;
size_t write_upto;

struct event *read_event;
struct event *write_event;
};

struct fd_state *
alloc_fd_state(struct event_base *base, evutil_socket_t fd)
{
struct fd_state *state = malloc(sizeof(struct fd_state));
if (!state)
return NULL;
state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state);
if (!state->read_event) {
free(state);
return NULL;
}
state->write_event =
event_new(base, fd, EV_WRITE|EV_PERSIST, do_write, state);

if (!state->write_event) {
event_free(state->read_event);
free(state);
return NULL;
}

state->buffer_used = state->n_written = state->write_upto = 0;

assert(state->write_event);
return state;
}

void
free_fd_state(struct fd_state *state)
{
event_free(state->read_event);
event_free(state->write_event);
free(state);
}

void
do_read(evutil_socket_t fd, short events, void *arg)
{
struct fd_state *state = arg;
char buf[1024];
int i;
ssize_t result;
while (1) {
assert(state->write_event);
result = recv(fd, buf, sizeof(buf), 0);
if (result <= 0)
break;

for (i=0; i < result; ++i) {
if (state->buffer_used < sizeof(state->buffer))
state->buffer[state->buffer_used++] = rot13_char(buf[i]);
if (buf[i] == '\n') {
assert(state->write_event);
event_add(state->write_event, NULL);
state->write_upto = state->buffer_used;
}
}
}

if (result == 0) {
free_fd_state(state);
} else if (result < 0) {
if (errno == EAGAIN) // XXXX use evutil macro
return;
perror("recv");
free_fd_state(state);
}
}

void
do_write(evutil_socket_t fd, short events, void *arg)
{
struct fd_state *state = arg;

while (state->n_written < state->write_upto) {
ssize_t result = send(fd, state->buffer + state->n_written,
state->write_upto - state->n_written, 0);
if (result < 0) {
if (errno == EAGAIN) // XXX use evutil macro
return;
free_fd_state(state);
return;
}
assert(result != 0);

state->n_written += result;
}

if (state->n_written == state->buffer_used)
state->n_written = state->write_upto = state->buffer_used = 1;

event_del(state->write_event);
}

void
do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base *base = arg;
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
if (fd < 0) { // XXXX eagain??
perror("accept");
} else if (fd > FD_SETSIZE) {
close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */
} else {
struct fd_state *state;
evutil_make_socket_nonblocking(fd);
state = alloc_fd_state(base, fd);
assert(state); /*XXX err*/
assert(state->write_event);
event_add(state->read_event, NULL);
}
}

void
run(void)
{
evutil_socket_t listener;
struct sockaddr_in sin;
struct event_base *base;
struct event *listener_event;

base = event_base_new();
if (!base)
return; /*XXXerr*/

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(40713);

listener = socket(AF_INET, SOCK_STREAM, 0);
evutil_make_socket_nonblocking(listener);

#ifndef WIN32
{
int one = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif

if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
return;
}

if (listen(listener, 16)<0) {
perror("listen");
return;
}

listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
/*XXX check it */
event_add(listener_event, NULL);

event_base_dispatch(base);
}

int
main(int c, char **v)
{
setvbuf(stdout, NULL, _IONBF, 0);

run();
return 0;
}

A simpler ROT13 server with Libevent

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>

#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>

#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#define MAX_LINE 16384

void do_read(evutil_socket_t fd, short events, void *arg);
void do_write(evutil_socket_t fd, short events, void *arg);

char
rot13_char(char c)
{
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}

void
readcb(struct bufferevent *bev, void *ctx)
{
struct evbuffer *input, *output;
char *line;
size_t n;
int i;
input = bufferevent_get_input(bev);
output = bufferevent_get_output(bev);

while ((line = evbuffer_readln(input, &n, EVBUFFER_EOL_LF))) {
for (i = 0; i < n; ++i)
line[i] = rot13_char(line[i]);
evbuffer_add(output, line, n);
evbuffer_add(output, "\n", 1);
free(line);
}

if (evbuffer_get_length(input) >= MAX_LINE) {
/* Too long; just process what there is and go on so that the buffer
* doesn't grow infinitely long. */
char buf[1024];
while (evbuffer_get_length(input)) {
int n = evbuffer_remove(input, buf, sizeof(buf));
for (i = 0; i < n; ++i)
buf[i] = rot13_char(buf[i]);
evbuffer_add(output, buf, n);
}
evbuffer_add(output, "\n", 1);
}
}

void
errorcb(struct bufferevent *bev, short error, void *ctx)
{
if (error & BEV_EVENT_EOF) {
/* connection has been closed, do any clean up here */
/* ... */
} else if (error & BEV_EVENT_ERROR) {
/* check errno to see what error occurred */
/* ... */
} else if (error & BEV_EVENT_TIMEOUT) {
/* must be a timeout event handle, handle it */
/* ... */
}
bufferevent_free(bev);
}

void
do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base *base = arg;
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
if (fd < 0) {
perror("accept");
} else if (fd > FD_SETSIZE) {
close(fd);
} else {
struct bufferevent *bev;
evutil_make_socket_nonblocking(fd);
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, readcb, NULL, errorcb, NULL);
bufferevent_setwatermark(bev, EV_READ, 0, MAX_LINE);
bufferevent_enable(bev, EV_READ|EV_WRITE);
}
}

void
run(void)
{
evutil_socket_t listener;
struct sockaddr_in sin;
struct event_base *base;
struct event *listener_event;

base = event_base_new();
if (!base)
return; /*XXXerr*/

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(40713);

listener = socket(AF_INET, SOCK_STREAM, 0);
evutil_make_socket_nonblocking(listener);

#ifndef WIN32
{
int one = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif

if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
return;
}

if (listen(listener, 16)<0) {
perror("listen");
return;
}

listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
/*XXX check it */
event_add(listener_event, NULL);

event_base_dispatch(base);
}

int
main(int c, char **v)
{
setvbuf(stdout, NULL, _IONBF, 0);

run();
return 0;
}

工作

经过几番调试终于对radosgw部分的代码框架有了大致的了解,当然工作任务是顺利完成了。细细想来有下面的总结。

  1. radosgw通过下面的代码,针对http不同请求(GET, POST, PUT, HEAD…)创建不同的对象,处理不同的业务需求。值得注意的是RSET(Representational state transfer)是一个表现层的协议。

    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
    RGWOp* RGWHandler_REST::get_op(void)
    {
    RGWOp *op;
    switch (s->op) {
    case OP_GET:
    op = op_get();
    break;
    case OP_PUT:
    op = op_put();
    break;
    case OP_DELETE:
    op = op_delete();
    break;
    case OP_HEAD:
    op = op_head();
    break;
    case OP_POST:
    op = op_post();
    break;
    case OP_COPY:
    op = op_copy();
    break;
    case OP_OPTIONS:
    op = op_options();
    break;
    default:
    return NULL;
    }

    if (op) {
    op->init(store, s, this);
    }
    return op;
    } /* get_op */
  2. 封装libcurl, 实现httpclient
    代码src\rgw\rgw_http_client.cc, 需要细细品。

  3. 读日志vs动态调试
    不得不说当你对代码熟悉的时候,读日志比动态调试代码高效很多。我可以清晰地看到来自civetweb的报文信息,以及radosgw的各种处理。

  4. valgrind
    valgrind只是简单的用了下。

生活

  1. leetcode
    今天下午刷了好几道滑动窗口的题。参考了灵神给的模板。
  • 187.重复的DNA序列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Solution:
    def findRepeatedDnaSequences(self, s: str) -> List[str]:
    # 滑动窗口
    # 先将k-1的数据放入窗口(这里k=10)
    k = 10
    record = [c for c in s[:k-1]]
    R = dict()
    for in_ in s[k-1:]:
    record.append(in_)
    dk = ''.join(record)
    R[dk] = R.get(dk, 0) + 1
    record.pop(0)
    ans = []
    for k, v in R.items():
    if v != 1:
    ans.append(k)
    return ans
  • 219.存在重复元素 II

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
    # 滑动窗口
    # 先将k-1个元素,加入窗口
    k += 1
    record = dict()
    for n in nums[:k-1]:
    record[n] = record.get(n, 0) + 1
    if record[n] != 1:
    return True
    for in_, out in zip(nums[k-1:], nums):
    record[in_] = record.get(in_, 0) + 1
    if record[in_] != 1:
    return True
    record[out] -= 1
    if record[out] == 0:
    del record[out]
    return False
  • 220.存在重复元素 III

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    from typing import *
    import bisect
    class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
    # 滑动窗口
    # 初始化先将k-1个元素放入窗口内
    # 二分插入
    k += 1
    record = sorted([n for n in nums[:k-1]])
    for i in range(len(record) - 1):
    if record[i+1] - record[i] <= t:
    return True
    for in_, out in zip(nums[k-1:], nums):
    ix = bisect.bisect(record, in_)
    record.insert(ix, in_)
    if (ix - 1 >= 0 and record[ix] - record[ix-1] <= t) or (ix + 1 < len(record) and record[ix + 1] - record[ix] <= t):
    return True
    del record[bisect.bisect(record, out)-1]
    return False
  • 239.滑动窗口最大值

    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
    import bisect

    class dep:
    def __init__(self):
    self.sk = []
    def insert(self, x):
    while self.sk and self.sk[-1] < x:
    self.sk.pop()
    self.sk.append(x)
    def first(self):
    return self.sk[0]
    def popfirst(self):
    self.sk.pop(0)
    class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
    # 单调队列
    # 先将k-1个元素放入窗口内
    record = dep()
    for n in nums[:k-1]:
    record.insert(n)
    ans = []
    for in_, out in zip(nums[k-1:], nums):
    record.insert(in_)
    f = record.first()
    ans.append(f)
    if f == out:
    record.popfirst()
    return ans

    ps 评论区有意思的东西

  • 单调队列真是一种让人感到五味杂陈的数据结构,它的维护过程更是如此…..就拿此题来说,队头最大,往队尾方向单调……有机会站在队头的老大永远心狠手辣,当它从队尾杀进去的时候,如果它发现这里面没一个够自己打的,它会毫无人性地屠城,把原先队里的人头全部丢出去,转身建立起自己的政权,野心勃勃地准备开创一个新的王朝…..这时候,它的人格竟发生了一百八十度大反转,它变成了一位胸怀宽广的慈父!它热情地请那些新来的“小个子”们入住自己的王国……然而,这些小个子似乎天性都是一样的——嫉妒心强,倘若见到比自己还小的居然更早入住王国,它们会心狠手辣地找一个夜晚把它们通通干掉,好让自己享受更大的“蛋糕”;当然,遇到比自己强大的,它们也没辙,乖乖夹起尾巴做人。像这样的暗杀事件每天都在上演,虽然王国里日益笼罩上白色恐怖,但是好在没有后来者强大到足以干翻国王,江山还算能稳住。直到有一天,闯进来了一位真正厉害的角色,就像当年打江山的国王一样,手段狠辣,野心膨胀,于是又是大屠城……历史总是轮回的。

  • 觉得现代官场更加能阐明这个基本道理。比如你这个倒霉蛋25岁二本毕业在机关当科员, 熬了十年后老处长退休, 你们科长升处长, 你升科长。你梦想着再熬10年处长退休·你接班。结果是赵家人不讲武德。直接空降一个25岁的付处。他比你先接班,你等他退休也没戏, 所以这一辈子和处长没关系,自然可以从queue里pop out, 不用排队。

  1. 读书:《政治的人生》
    细细想来我已经好久没有读过这样的书了。我不敢相信这是1994年写的书,因为我感觉作者的思考的问题,表达的事情在2022年并不过时。







复盘

10月过的好快啊。国庆七天,隔离七天。半个月就过去了。

工作

  1. 调试samba.
    其实一个很小的权限问题,我当时是很快定位到不是radosgw的问题,因为radosgw已经把错误信息抛给了samba.
  2. 调试radosgw.
    自己对radosgw的代码也是越来越熟悉了。但是写抽象程度比较高的代码,我还是很不舒服的。有的时候写了好长时间都不知道自己写了啥。
  3. 学习rust.
    课程. 人家大二的课程。怎么说呢? 自我感觉没有python方便的。仿佛是cpp的加强版,rust站在了cpp的肩膀上,用一些更好的方法去处理优化cpp的缺点。
  4. vue和react.
    三分钟热度,希望能继续坚持…

问题

  1. 怎么用curl做http请求。
    curl不只是一个命令行程序,它也可以编译链接到其他程序中。例如radosgw中就封装了curl的一些方法,用来做http的请求(GET, POST, PUT…)
  2. 怎么处理请求
    我看好多都是用一个结构体封装每次的请求参数信息。好吧,这方面的知识一无所知,有待加强。

其实就是想强迫自己写一下博客。

恭喜啊

写博客一直是我心心念的事情。虽然大学的时候有在CSDN写过一点博客,但是作为程序员的高傲,肯定是想自己搞一个网站搭建博客。
几经波折我最终选择了hexo+GitHub。

总结

  1. 基本的前端常识,了解JavaScript, html, css等。
  2. 基本MarkDown文档的编写。
  3. 阅读文档的耐心。

需要理解的概念

  1. 非常建议大家阅读最新的英文文档,因为英文文档的部署方法用的是GitHub Actions.中文版本的部署方法是travis-ci(自我感觉使用travis-ci部署会麻烦点)。

其实GitHub Actionstravis-ci都是属于持续集成(Continuous integration,简称CI)服务。网上了查了一下有很多的专业术语去描述这件事情。具体到我们搭建博客的过程就是:hexo只是我们用来写博客的(注意这里的博客是Markdown的文件格式),但是要想作为浏览器浏览的网页,那就必须通过一些系列操作,然后生成我们想要的html文件(运行命令hexo generate)。那么这一系列操作便可以理解是持续集成服务的一部分。

遇到的困难

  1. 在阅读文档的时候,对于gh-pages当时没有理解。其实就是通过github actions会把生成的html文件copy到gh-pages分支,我们可以把该分支的内容作为github pages.
  2. 我的主题是通过github submodule的方式添加的。这就导致拉代码的时候,是要拉取子模块的代码。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    --- a/.github/workflows/pages.yml
    +++ b/.github/workflows/pages.yml
    @@ -11,7 +11,7 @@ jobs:
    steps:
    - uses: actions/checkout@v2
    with:
    - submodules: true
    + submodules: 'true'
    - name: Use Node.js 16.x
    uses: actions/setup-node@v2
    with:

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start hh

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment