并发web服务器

indexl.html

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
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/sendfile.h>

#define PORT 8080
#define MAX_CLIENTS FD_SETSIZE

int main() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
perror("socket");
return 1;
}

int opt = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;

if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind");
return 1;
}

if (listen(listen_fd, SOMAXCONN) < 0) {
perror("listen");
return 1;
}

printf("Server running on http://0.0.0.0:%d\n", PORT);

fd_set master_set, read_set;
FD_ZERO(&master_set);
FD_SET(listen_fd, &master_set);

int max_fd = listen_fd;

while (1) {
read_set = master_set;

int nready = select(max_fd + 1, &read_set, NULL, NULL, NULL);
if (nready < 0) {
perror("select");
continue;
}

// 处理监听 socket(新连接)
if (FD_ISSET(listen_fd, &read_set)) {
int client_fd = accept(listen_fd, NULL, NULL);
if (client_fd >= 0) {
FD_SET(client_fd, &master_set);
if (client_fd > max_fd) max_fd = client_fd;
}
nready--;
if (nready <= 0) continue;
}

// 处理所有客户端 socket
for (int fd = 0; fd <= max_fd; fd++) {
if (!FD_ISSET(fd, &read_set)) continue;
if (fd == listen_fd) continue;

char buffer[2048] = {0};
ssize_t n = recv(fd, buffer, sizeof(buffer), 0);

if (n <= 0) {
close(fd);
FD_CLR(fd, &master_set);
continue;
}

// 打开 index.html
int file_fd = open("index.html", O_RDONLY);
if (file_fd < 0) {
const char *err = "HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 13\r\n"
"Connection: close\r\n\r\n"
"404 Not Found";
write(fd, err, strlen(err));
close(fd);
FD_CLR(fd, &master_set);
continue;
}

struct stat st;
fstat(file_fd, &st);
size_t filesize = st.st_size;

char header[256];
int len = snprintf(header, sizeof(header),
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %zu\r\n"
"Connection: close\r\n\r\n",
filesize
);
write(fd, header, len);

off_t offset = 0;
while (offset < filesize) {
ssize_t sent = sendfile(fd, file_fd, &offset, filesize - offset);
if (sent <= 0) break;
}

close(file_fd);
close(fd);
FD_CLR(fd, &master_set);
}
}

close(listen_fd);
return 0;
}

poll

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
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <poll.h>

#define PORT 8080
#define MAX_CLIENTS 1024

int main() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
perror("socket");
return 1;
}

int opt = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;

if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind");
return 1;
}

if (listen(listen_fd, SOMAXCONN) < 0) {
perror("listen");
return 1;
}

printf("Server running on http://0.0.0.0:%d\n", PORT);

struct pollfd fds[MAX_CLIENTS];
for (int i = 0; i < MAX_CLIENTS; i++) {
fds[i].fd = -1;
fds[i].events = POLLIN;
}

fds[0].fd = listen_fd;
fds[0].events = POLLIN;

while (1) {
int nready = poll(fds, MAX_CLIENTS, -1);
if (nready < 0) {
perror("poll");
continue;
}

// 监听 socket 可读 → accept
if (fds[0].revents & POLLIN) {
int client_fd = accept(listen_fd, NULL, NULL);
if (client_fd >= 0) {
// 放入 poll 数组
for (int i = 1; i < MAX_CLIENTS; i++) {
if (fds[i].fd < 0) {
fds[i].fd = client_fd;
fds[i].events = POLLIN;
break;
}
}
}
nready--;
if (nready <= 0) continue;
}

// 处理客户端
for (int i = 1; i < MAX_CLIENTS; i++) {
if (fds[i].fd < 0) continue;
if (!(fds[i].revents & POLLIN)) continue;

int fd = fds[i].fd;

char buffer[2048] = {0};
ssize_t n = recv(fd, buffer, sizeof(buffer), 0);

if (n <= 0) {
close(fd);
fds[i].fd = -1;
continue;
}

// 打开 index.html
int file_fd = open("index.html", O_RDONLY);
if (file_fd < 0) {
const char *err = "HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 13\r\n"
"Connection: close\r\n\r\n"
"404 Not Found";
write(fd, err, strlen(err));
close(fd);
fds[i].fd = -1;
continue;
}

struct stat st;
fstat(file_fd, &st);
size_t filesize = st.st_size;

char header[256];
int len = snprintf(header, sizeof(header),
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %zu\r\n"
"Connection: close\r\n\r\n",
filesize
);
write(fd, header, len);

off_t offset = 0;
while (offset < filesize) {
ssize_t sent = sendfile(fd, file_fd, &offset, filesize - offset);
if (sent <= 0) break;
}

close(file_fd);
close(fd);
fds[i].fd = -1;
}
}

close(listen_fd);
return 0;
}

epoll

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
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <sys/epoll.h>

#define PORT 8080
#define MAX_EVENTS 1024

// 设置非阻塞
static void set_nonblock(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int main() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
perror("socket");
return 1;
}

int opt = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;

if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind");
return 1;
}

if (listen(listen_fd, SOMAXCONN) < 0) {
perror("listen");
return 1;
}

printf("Server running on http://0.0.0.0:%d\n", PORT);

set_nonblock(listen_fd);

int epfd = epoll_create1(0);
struct epoll_event ev, events[MAX_EVENTS];

ev.events = EPOLLIN;
ev.data.fd = listen_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev);

while (1) {
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; i++) {
int fd = events[i].data.fd;

// 新连接
if (fd == listen_fd) {
int client_fd = accept(listen_fd, NULL, NULL);
if (client_fd >= 0) {
set_nonblock(client_fd);
ev.events = EPOLLIN;
ev.data.fd = client_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &ev);
}
continue;
}

// 客户端可读
if (events[i].events & EPOLLIN) {
char buffer[2048] = {0};
ssize_t nread = recv(fd, buffer, sizeof(buffer), 0);

if (nread <= 0) {
close(fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
continue;
}

// 打开 index.html
int file_fd = open("index.html", O_RDONLY);
if (file_fd < 0) {
const char *err = "HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 13\r\n"
"Connection: close\r\n\r\n"
"404 Not Found";
send(fd, err, strlen(err), 0);
close(fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
continue;
}

struct stat st;
fstat(file_fd, &st);
size_t filesize = st.st_size;

char header[256];
int len = snprintf(header, sizeof(header),
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %zu\r\n"
"Connection: close\r\n\r\n",
filesize
);
send(fd, header, len, 0);

off_t offset = 0;
while (offset < filesize) {
ssize_t sent = sendfile(fd, file_fd, &offset, filesize - offset);
if (sent <= 0) break;
}

close(file_fd);
close(fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
}
}
}

close(listen_fd);
close(epfd);
return 0;
}