emper-echo-server.c 2.94 KB
Newer Older
Florian Fischer's avatar
Florian Fischer committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright © 2020 Florian Fischer
/* #include <assert.h> */
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#include "config.h"
#define EMPER_ASYNC_LIB
#define EMPER_ASYNC_NETWORK
#include "emper.h"

#define unlikely(x) __builtin_expect(!!(x), 1)

19
static int port = 12345;
Florian Fischer's avatar
Florian Fischer committed
20

21
_Noreturn static void die(const char* msg) {
Florian Fischer's avatar
Florian Fischer committed
22
23
24
25
26
27
28
29
30
31
32
33
	perror(msg);
	exit(EXIT_FAILURE);
}

#define BUF_MAX 4096
void client_func(void* arg) {
	int client_fd = *(int*)arg;
	free(arg);

	char buf[BUF_MAX];
	for (;;) {
#ifdef USE_ASYNC_IO
34
		ssize_t bytes_recv = sync_recv(client_fd, (void*)&buf, BUF_MAX, MSG_WAITALL);
Florian Fischer's avatar
Florian Fischer committed
35
#else
36
		ssize_t bytes_recv = recv(client_fd, (void*)&buf, BUF_MAX, MSG_WAITALL);
Florian Fischer's avatar
Florian Fischer committed
37
38
39
40
41
42
43
44
45
46
47
48
49
#endif
		// socket was shutdown
		if (unlikely(bytes_recv == 0)) {
			return;
		}

		if (unlikely(bytes_recv == -1)) {
			perror("recv failed");
			if (errno != EINTR) {
				return;
			}
		}

50
51
52
53
54
55
56
57
58
		ssize_t bytes_send = 0;
		while (bytes_send < bytes_recv) {
#ifdef USE_ASYNC_IO
			ssize_t new_bytes_send = sync_send(client_fd, buf, bytes_recv - bytes_send, MSG_NOSIGNAL);
#else
			ssize_t new_bytes_send = send(client_fd, buf, bytes_recv - bytes_send, MSG_NOSIGNAL);
#endif
			// socket was shutdown
			if (unlikely(new_bytes_send == 0)) {
Florian Fischer's avatar
Florian Fischer committed
59
60
				return;
			}
61
62
63
64
65
66
67
68
69

			if (unlikely(bytes_send == -1)) {
				if (errno != EINTR) {
					perror("recv failed");
					return;
				}
			}

			bytes_send += new_bytes_send;
Florian Fischer's avatar
Florian Fischer committed
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
		}
	}
}

#define BACKLOG 5
static void welcome_func() {
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1) {
		die("socket failed");
	}

	struct sockaddr_in servaddr;
	memset(&servaddr, 0, sizeof(servaddr));	 // NOLINT

	// assign IP, PORT
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = INADDR_ANY;
	servaddr.sin_port = htons(port);

	int reuseaddr = 1;
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1) {
		die("setsockopt failed");
	}

	if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
		die("bind failed");
	}

	if (listen(sockfd, BACKLOG) != 0) {
		die("listen failed");
	}

	for (;;) {
#ifdef USE_ASYNC_IO
104
		int client_fd = sync_accept(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
Florian Fischer's avatar
Florian Fischer committed
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#else
		socklen_t address_len = sizeof(servaddr);
		int client_fd = accept(sockfd, (struct sockaddr*)&servaddr, &address_len);
#endif
		if (unlikely(client_fd < 0)) {
			perror("accept failed");
		}

		int* passable_fd = malloc(sizeof(int));
		if (unlikely(!passable_fd)) {
			die("allocating passable fd failed");
		}

		*passable_fd = client_fd;
		fiber* client_fiber = fiber_from(client_func, (void*)passable_fd);
		schedule(client_fiber);
	}
}

int main(int argc, char* argv[]) {
125
126
127
	if (argc > 1) {
		const int decimal = 10;
		port = (int)strtol(argv[1], NULL, decimal);
Florian Fischer's avatar
Florian Fischer committed
128
129
130
131
132
133
134
135
136
	}

	init_runtime();

	fiber* welcome_fiber = fiber_from0(welcome_func);
	schedule(welcome_fiber);

	wait_until_runtime_finished();
}