基础问题:求数字 aann 次方。
最基本的想法,就是一次一次相乘,时间复杂度为 O(n)O(n)
代码如下:

1
2
3
4
5
6
7
8
9
using ll = long long;
ll pow(ll a, ll n)
{
ll ret = 1;
for (ll i = 0; i < n; i++) {
ret *= a;
}
return ret;
}

稍微思考一下便可得知,计算一个数的nn次方并不需要相乘 nn 次。我们如果知道 aan/2n/2 次方,便可以用两个 an/2a^{n/2} 相乘,获得 ana^n。顺着这个思想往下想,可以考虑把 nn 拆成二进制的形式,这样只要把对应的二进制位数得到的幂相乘起来就可以得出答案了。举例:当 nn 为 7 时,我们要 676^777 按照二进制可以拆成 0000011100000111。那我们只要知道 616^1626^2646^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
ll qpow(ll a, ll n)
{
if (n == 0) {
return 1;
} else if (n % 2 == 1) {
return qpow(a, n - 1) * a;
} else {
ll ret = qpow(a, n / 2);
return ret * ret;
}
}

ll qpow2(ll a, ll n)
{
ll ret = 1;
while (n) {
if (n & 1) {
ret *= a;
}
a *= a;
n >>= 1;
}
return ret;
}

一为递归的方式,二为迭代的方式。考虑到调用栈切换,方式二会快一些。这种把幂拆成二进制的方法,就被称为快速幂。
完整代码如下:

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
#include <iostream>
#include <time.h>
using namespace std;
using ll = long long;
ll pow(ll a, ll n)
{
ll ret = 1;
for (ll i = 0; i < n; i++) {
ret *= a;
}
return ret;
}

ll qpow(ll a, ll n)
{
if (n == 0) {
return 1;
} else if (n % 2 == 1) {
return qpow(a, n - 1) * a;
} else {
ll ret = qpow(a, n / 2);
return ret * ret;
}
}

ll qpow2(ll a, ll n)
{
ll ret = 1;
while (n) {
if (n & 1) {
ret *= a;
}
a *= a;
n >>= 1;
}
return ret;
}
int main()
{
ll a = 7, n = 16;
clock_t startTime, endTime;
startTime = clock(); // cpu clock time
cout << pow(a, n) << endl;
endTime = clock();
cout << (endTime - startTime) << endl;

startTime = clock(); // cpu clock time
cout << qpow(a, n) << endl;
endTime = clock();
cout << (endTime - startTime) << endl;

startTime = clock(); // cpu clock time
cout << qpow2(a, n) << endl;
endTime = clock();
cout << (endTime - startTime) << endl;
}

可以看到后面两种cpu周期普遍低于前一种。

priority_queue是C++的一种STL容器,实现为堆。在leetcode刷题中非常常用。有些时候我们需要塞入自定义的数据结构。这样就需要对其的排序方式做一个重新定义。
假设有以下数据结构

1
2
3
4
struct node {
int x;
int y;
};

对比方式为只看x的大小。把x的值大的放在堆顶。
则对比函数cmp写法如下:

1. 仿函数(函数对象)

用这种方式,需要显示的定义优先队列的容器类型(vector)以及比较函数(cmp)。

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
#include<iostream>
#include<queue>
using namespace std;
//函数对象类
template <typename T>
class cmp {
public:
//重载 () 运算符
bool operator()(T a, T b) {
return a.x < b.x;
}
};
struct node {
int x;
int y;
};
int main() {
node a = {1,2};
node b = {0,2};
node c = {1,3};
node d = {2,5};
node e = {3,6};
priority_queue<node, vector<node>, cmp<node>> pq; // 显式指定容器类型和比较函数
pq.push(a);
pq.push(b);
pq.push(c);
pq.push(d);
pq.push(e);
while (!pq.empty()) {
cout << pq.top().x << "," << pq.top().y << endl;
pq.pop();
}
return 0;
}

结果:

1
2
3
4
5
6
@└────> # ./pq
3,6
2,5
1,3
1,2
0,2

2. 使用自定义类型比较关系

重载需要比较类型的<<符号,之后在类型直接写类型名称即可。

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
#include<iostream>
#include<queue>
using namespace std;
struct node {
int x;
int y;
bool operator < (node b) const { // 这里后面的const必须加
return this->x < b.x;
}
};
int main() {
node a = {1,2};
node b = {0,2};
node c = {1,3};
node d = {2,5};
node e = {3,6};
priority_queue<node> pq; // 只写类型名称即可
pq.push(a);
pq.push(b);
pq.push(c);
pq.push(d);
pq.push(e);
while (!pq.empty()) {
cout << pq.top().x << "," << pq.top().y << endl;
pq.pop();
}
return 0;
}

结果:

1
2
3
4
5
6
@└────> # ./pq
3,6
2,5
1,3
1,2
0,2

3. 使用lambda表达式

使用lambda表达式需要在pq对象构造的时候,将lambda表达式作为参数传入其中。即pq(cmp)

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
#include<iostream>
#include<queue>
#include<functional>
using namespace std;

struct node {
int x;
int y;
};
int main() {
auto cmp = [](node x, node y) {
return x.x < y.x;
};
node a = {1,2};
node b = {0,2};
node c = {1,3};
node d = {2,5};
node e = {3,6};
priority_queue<node, vector<node>, decltype(cmp)> pq(cmp); // 需要在pq对象创建的时候将lambda表达式作为参数传入
// priority_queue<node, vector<node>, function<bool(node, node)> > pq(cmp); // 和上面的效果相同,但是用function需要包含头文件functional
pq.push(a);
pq.push(b);
pq.push(c);
pq.push(d);
pq.push(e);
while (!pq.empty()) {
cout << pq.top().x << "," << pq.top().y << endl;
pq.pop();
}
return 0;
}

结果:

1
2
3
4
5
6
@└────> # ./pq
3,6
2,5
1,3
1,2
0,2

4. 函数指针(和上面的相比有些多余,刷题不是很实用)

这种方式和lambda表达式的方式很类似。

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
#include<iostream>
#include<queue>
#include<functional>
using namespace std;

struct node {
int x;
int y;
};
bool cmp(node x, node y) {
return x.x < y.x;
}
int main() {
node a = {1,2};
node b = {0,2};
node c = {1,3};
node d = {2,5};
node e = {3,6};
bool (*funcp)(node x, node y) = cmp;
priority_queue<node, vector<node>, decltype(*funcp)> pq(*funcp);
pq.push(a);
pq.push(b);
pq.push(c);
pq.push(d);
pq.push(e);
while (!pq.empty()) {
cout << pq.top().x << "," << pq.top().y << endl;
pq.pop();
}
return 0;
}

结果:

1
2
3
4
5
6
@└────> # ./pq
3,6
2,5
1,3
1,2
0,2

reference:

https://blog.csdn.net/Strengthennn/article/details/119078911

客户端:

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
#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h>
#include <fcntl.h>

#define BUFFER_SIZE 64

int main(int argc, char* argv[]) {
if (argc <= 2) {
printf("usage: %s ip_address port_number\n", basename(argv[0]));
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);

struct sockaddr_in server_address;
bzero(&server_address, sizeof(server_address));
server_address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &server_address.sin_addr);
server_address.sin_port = htons(port);

int sockfd = socket(PF_INET, SOCK_STREAM, 0);
assert(sockfd >= 0);
if (connect(sockfd, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) {
printf("connection failed!\n");
close(sockfd);
return 1;
}

pollfd fds[2];
// 0文件描述符为标准输入 注册可读事件POLLIN
fds[0].fd = 0;
fds[0].events = POLLIN;
fds[0].revents = 0;

// sockfd文件描述符为监听的端口 注册可读事件POLLIN和对方关闭连接事件POLLRDHUP
fds[1].fd = sockfd;
fds[1].events = POLLIN | POLLRDHUP;
fds[1].revents = 0;

char read_buf[BUFFER_SIZE];
int pipefd[2];
int ret = pipe(pipefd);
assert(ret != -1);

while (1) {
ret = poll(fds, 2, -1);
if (ret < 0) {
printf("poll failure\n");
break;
}

if (fds[1].revents & POLLRDHUP) {
printf("server close the connection");
break;
} else if (fds[1].revents & POLLIN) {
memset(read_buf, '\0', BUFFER_SIZE);
recv(fds[1].fd, read_buf, BUFFER_SIZE - 1, 0);
printf("%s\n", read_buf);
}

if (fds[0].revents & POLLIN) {
// 零拷贝 0 -> sockfd
ret = splice(0, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
ret = splice(pipefd[0], NULL, sockfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
}
}
close(sockfd);
return 0;
}

服务器:

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
#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <poll.h>

#define USER_LIMIT 5
#define BUFFER_SIZE 64
#define FD_LIMIT 65535

struct client_data
{
sockaddr_in address;
char* write_buf;
char buf[BUFFER_SIZE];
};

int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}

int main(int argc, char* argv[])
{
if (argc <= 2) {
printf("usage: %s ip_address portnumber\n", basename(argv[0]));
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);

int ret = 0;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);

int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);

ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != 1);

ret = listen(listenfd, 5);
assert(ret != -1);

client_data* users = new client_data[FD_LIMIT];

pollfd fds[USER_LIMIT + 1];
int user_counter = 0;
for (int i = 1; i <= USER_LIMIT; i++) {
fds[i].fd = -1;
fds[i].events = 0;
}
// 注册事件:监听端口是否可读或者出现错误
fds[0].fd = listenfd;
fds[0].events = POLLIN | POLLERR;
fds[0].revents = 0;

while (1) {
ret = poll(fds, user_counter + 1, -1);
if (ret < 0) {
printf("poll failure\n");
break;
}

for (int i = 0; i < user_counter + 1; i++) {
if ((fds[i].fd == listenfd) && (fds[i].revents & POLLIN)) {
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);

if (connfd < 0) {
printf("errno is: %d\n", errno);
continue;
}

if (user_counter >= USER_LIMIT) {
const char* info = "too many users\n";
printf("%s", info);
send(connfd, info, strlen(info), 0);
close(connfd);
continue;
}

user_counter++;
users[connfd].address = client_address;
setnonblocking(connfd);
// 用于和新连接进来的客户的交互的文件描述符connfd
fds[user_counter].fd = connfd;
fds[user_counter].events = POLLIN | POLLRDHUP | POLLERR;
fds[user_counter].revents = 0;
printf("comes a new user, now have %d users\n", user_counter);
} else if (fds[i].revents & POLLERR) { // 某个和用户连接出错了
printf("get an error from %d\n", fds[i].fd);
char errors[100];
memset(errors, '\0', 100);
socklen_t length = sizeof(errors);
if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &errors, &length) < 0) {
printf("get socket options failed]n");
}
continue;
} else if (fds[i].revents & POLLRDHUP) { // 用户断开了连接
users[fds[i].fd] = users[fds[user_counter].fd];
close(fds[i].fd);
fds[i] = fds[user_counter];
i--;
user_counter--;
printf("a client left\n");
} else if (fds[i].revents & POLLIN) { // 用户连接可读,表示用户发送了数据过来
int connfd = fds[i].fd;
memset(users[connfd].buf, '\0', BUFFER_SIZE);
ret = recv(connfd, users[connfd].buf, BUFFER_SIZE - 1, 0);
printf("get %d bytes of client data %s from %d\n", ret, users[connfd].buf, connfd);
if (ret < 0) {
if (errno != EAGAIN) {
close(connfd);
users[fds[i].fd] = users[fds[user_counter].fd];
fds[i] = fds[user_counter];
i--;
user_counter--;
}
} else if (ret == 0) { // 对方关闭了连接 此时有POLLHUP处理

} else {
for (int j = 1; j <= user_counter; j++) {
if (fds[j].fd == connfd) {
continue;
}

fds[j].events |= ~POLLIN; // 原书这里是这样写的 目的为注销可读。但是本人认为应该是 &= ,下同。
fds[j].events |= POLLOUT;
users[fds[j].fd].write_buf = users[connfd].buf; // 其他用户的buf写上当前收到的某fd所发送的信息
}
}
} else if (fds[i].revents & POLLOUT) { // 用户连接可写,把buffer信息写进去
int connfd = fds[i].fd;
if (!users[connfd].write_buf) {
continue;
}
ret = send(connfd, users[connfd].write_buf, strlen(users[connfd].write_buf), 0);
users[connfd].write_buf = NULL;
fds[i].events |= ~POLLOUT;
fds[i].events |= POLLIN;
}
}
}
delete[] users;
close(listenfd);
return 0;

}

演示结果:
服务器:
在这里插入图片描述


客户端1:
客户端发送:client 1 : 1111111


客户端2:
客户端发送:client 2 : 2222222


reference:
linux高性能服务器编程——游双

定义

假设有如下场景:函数 A 调用了函数 B。寄存器 %rbx 需要在调用 B 函数前后保持一致。

1
2
3
4
5
6
7
func A:
...
call func B
...

func B:
...

调用者保存寄存器:\color{red}{调用者保存寄存器:}

1
2
3
4
5
6
7
8
9
func A:
...
save register %rbx
call func B
restore register %rbx
...

func B:
...

如上图所示,寄存器 %rbx 是由函数 B 的调用者,即函数 func A 来保存并且恢复的。函数 B 感知不到这个情况,可以尽情使用寄存器 %rbx。

被调用者保存寄存器:\color{red}{被调用者保存寄存器:}

1
2
3
4
5
6
7
8
9
func A:
...
call func B
...

func B:
save register %rbx
...
restore register %rbx

如图所示,寄存器 %rbx 是由被调用函数,即函数 B 来保存并回复的。函数 A 感知不到这个情况。

寄存器分类

不同的寄存器有不同的保存策略。
callee saved(被调用者保存):\color{red}{callee \ saved(被调用者保存):}

%rbx ,%rbp,%r12,%r13,%r14,%r15

caller saved(调用者保存)\color{red}{caller \ saved(调用者保存):}

%rax,%rdi,%rsi,%rdx,%rcx,%8,%r9,%r10,%r11

定理内容:
aa为自然数,pp为一个质数。则有

apa (mod p)a^p \equiv a\ (mod\ p)

其中 \equiv 是同模符号,表示左右的数字对于p来说取模,是相等的。
证明:
数学归纳法

  1. a=1a = 1时, 显然成立。
  2. a=aa = a时,设 p(apa)p|(a^p - a),即ppapaa^p - a的约数。
  3. 则当a=a+1a = a + 1时, 尝试证明 p((a+1)p(a+1))p | ((a + 1)^p - (a + 1))
    证明如下:
    根据二项式定理,展开该项:

(a+1)p(a+1)=k=0pCpkak1pka1=Cp0a01p+k=1p1Cpkak1pk+Cppap10a1=1+k=1p1Cpkak1pk+apa1=k=1p1Cpkak1pk+apa\begin{aligned} (a + 1)^p - (a + 1) &= \sum_{k=0}^{p}C_p^ka^k1^{p-k} -a - 1 \\ &= C_p^0a^01^p + \sum_{k=1}^{p-1}C_p^ka^k1^{p-k} + C_p^pa^p1^0 - a - 1 \\ &= 1 + \sum_{k=1}^{p-1}C_p^ka^k1^{p-k} + a^p - a - 1 \\ &= \sum_{k=1}^{p-1}C_p^ka^k1^{p-k} + a^p - a \end{aligned}

因为Cpk=p!k!(pk)!C_p^k = \frac{p!}{k!(p - k)!}并且pp为质数,所以ppk=1p1Cpkak1pk\sum_{k=1}^{p-1}C_p^ka^k1^{p-k}的因数,即 p k=1p1Cpkak1pkp\ | \sum_{k=1}^{p-1}C_p^ka^k1^{p-k}
又因为由假设2可知ppapaa^p - a的因数。所以ppk=1p1Cpkak1pk+apa\sum_{k=1}^{p-1}C_p^ka^k1^{p-k} + a^p - a的因数,即为(a+1)p(a+1)(a + 1)^p - (a + 1)的因数。证毕。

应用:
如果aa不是pp的倍数,那么还有

ap11 (mod p)a^{p - 1} \equiv 1\ (mod\ p)

所以可以用上式解决一些问题,如计算21002^{100}模13。
因为2不是13的倍数,则21311(mod 13)2^{13 - 1} \equiv 1 (mod \ 13)

2100=2128+4 (mod 13)=(212)824 (mod 13)=1824 (mod 13)=16 (mod 13)=3\begin{aligned} 2^{100} &= 2^{12 * 8 + 4} \ (mod \ 13)\\ &= (2^{12})^8 * 2^4\ (mod \ 13)\\ &= 1^8 * 2^4\ (mod \ 13)\\ &= 16\ (mod\ 13)\\ &= 3 \end{aligned}

reference:

  1. https://zhuanlan.zhihu.com/p/87611586
  2. https://www.cnblogs.com/-citywall123/p/10673191.html

linux 服务器高级编程ET LT代码

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
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<fcntl.h>
#include<stdlib.h>
#include<sys/epoll.h>
#include<pthread.h>

#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10

int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}

void addfd(int epollfd, int fd, bool enable_et) //在内核事件表中为fd注册事件
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN;
if (enable_et)
{
event.events |= EPOLLET;
}
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event); // 操作内核事件表的函数
setnonblocking(fd);
}

void lt(epoll_event* events, int number, int epollfd, int listenfd)
{
char buf[BUFFER_SIZE];
for (int i = 0; i < number; i++) {
int sockfd = events[i].data.fd;
if (sockfd == listenfd) {
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
addfd(epollfd, connfd, false);
printf("connect fd:%d listenfd=%d\n", connfd, listenfd);
} else if (events[i].events & EPOLLIN) {
printf("event trigger once\n");
memset(buf, '\0', BUFFER_SIZE);
int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);
printf("socket fd:%d listenfd=%d\n", sockfd, listenfd);
if (ret < 0) {
close(sockfd);
continue;
}
printf("get %d bytes of content: %s\n", ret, buf);
} else {
printf("sth else happend\n");
}
}
}

void et(epoll_event* events, int number, int epollfd, int listenfd)
{
char buf[BUFFER_SIZE];
for (int i = 0 ; i < number; i++) {
int sockfd = events[i].data.fd;
if (sockfd == listenfd) {
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
addfd(epollfd, connfd, true);
} else if (events[i].events & EPOLLIN) {
printf("event trigger once\n");
while (1) {
memset(buf, '\0', BUFFER_SIZE);
int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);
if (ret < 0) {
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
printf("read later\n");
break;
}
close(sockfd);
break;
} else if (ret == 0) {
close(sockfd);
} else {
printf("get %d bytes of content:%s\n", ret, buf);
}
}
} else {
printf("nothing happend\n");
}
}
}

int main(int argc, char* argv[])
{
if (argc <= 2) {
printf("usage: %s ip_address port_number\n", basename(argv[0]));
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);

int ret = 0;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);

int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);

ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);

ret = listen(listenfd, 5);
assert(ret != -1);

epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(5);
addfd(epollfd, listenfd, true);

while(1) {
int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
if (ret < 0) {
printf("epoll fail\n");
break;
}
// lt(events, ret, epollfd, listenfd);
et(events, ret, epollfd, listenfd);
}

close(listenfd);
return 0;
}

ET:
1
ET模式下需要一次性处理完所有数据,下次就不会加入到epoll_wait的事件中了。


LT:
2
LT模式下未处理完的事件还是会被加入到epoll_wait监听的事件中。


reference:
linux高性能服务器编程——游双P153-P157