-
Notifications
You must be signed in to change notification settings - Fork 0
/
server_nonblock.cpp
127 lines (123 loc) · 3.7 KB
/
server_nonblock.cpp
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <vector>
#include <thread>
#include <mutex>
std::mutex mu;
std::vector<int> connections;
const auto _10ms = std::chrono::milliseconds(10);
void callback() {
std::vector<char> buf(256);
while (true) {
bool recv_any = false;
int i = 0, connect;
while (true) {
{ // during resize size() may return surprisingly big value :-)
std::lock_guard<std::mutex> lg(mu);
if (i >= connections.size())
break;
connect = connections[i];
}
bool ok = false;
while (true) {
int ret;
if ((ret = recv(connect, buf.data(), buf.capacity(), 0)) <= 0) {
if (ret == 0) // connection closed
break;
if (errno == EINTR) { // interrupted, try again
continue;
} else if (errno == EAGAIN) { // no data
ok = true;
break;
}
perror("recv()"); // close and update connections list
break;
}
recv_any = ok = true;
int size = ret;
char *ptr = buf.data();
while (size > 0 && (ret = send(connect, ptr, size, 0)) != 0) {
if (ret == -1) {
if (errno == EINTR)
continue;
perror("send()");
break;
}
size -= ret;
ptr += ret;
}
break;
}
if (ok) {
i++;
} else { // close and update connections list
close(connect);
std::lock_guard<std::mutex> lg(mu);
connections[i] = connections[connections.size() - 1];
connections.pop_back();
}
}
if (!recv_any)
std::this_thread::sleep_for(_10ms);
}
}
int main() {
int server = socket(AF_INET, SOCK_STREAM, 0);
if (server == -1) {
perror("socket()");
exit(EXIT_FAILURE);
}
struct sockaddr_in srv_addr, clnt_addr;
uint32_t addr_size = sizeof(srv_addr);
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
srv_addr.sin_port = htons(1234);
if (bind(server, (struct sockaddr*) &srv_addr, addr_size) != 0) {
perror("bind()");
exit(EXIT_FAILURE);
}
int flags = fcntl(server, F_GETFL, 0);
if (flags < 0) {
perror("fcntl(get)");
exit(EXIT_FAILURE);
}
if (fcntl(server, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("fcntl(set)");
exit(EXIT_FAILURE);
}
if (listen(server, 1000) != 0) {
perror("listen()");
exit(EXIT_FAILURE);
}
bool run = false;
while (true) {
int connect = accept(server, (struct sockaddr*) &clnt_addr, &addr_size);
if (connect < 0) {
if (errno == EWOULDBLOCK) {
std::this_thread::sleep_for(_10ms);
continue;
} else {
perror("accept()");
exit(EXIT_FAILURE);
}
}
{
std::lock_guard<std::mutex> lg(mu);
connections.push_back(connect);
}
if (!run) {
run = true;
std::thread t(callback);
t.detach();
}
}
return 0;
}