Skip to content

Commit f2530f2

Browse files
committedNov 22, 2016
Synch 2 lecture code
1 parent 4ead7ee commit f2530f2

15 files changed

+1327
-0
lines changed
 

‎synch2/.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
serviceblaster
2+
serviceclient
3+
servicelookup
4+
serviceserver
5+
serviceserver-[0-9][0-9]

‎synch2/GNUmakefile

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
SERVER_PROGRAMS = $(sort $(patsubst %.c,%,$(wildcard serviceserver-??.c)))
2+
PROGRAMS = serviceblaster serviceclient servicelookup serviceserver $(SERVER_PROGRAMS)
3+
all: $(PROGRAMS)
4+
5+
O ?= 2
6+
CFLAGS := -pthread
7+
include ../common/rules.mk
8+
9+
%.o: %.c GNUmakefile $(BUILDSTAMP)
10+
$(CC) $(CPPFLAGS) $(CFLAGS) $(DEPCFLAGS) $(O) -o $@ -c $<
11+
12+
$(PROGRAMS): %: %.o
13+
$(CC) $(CFLAGS) $(O) -o $@ $^
14+
15+
16+
clean:
17+
rm -f *.o *.core $(PROGRAMS)
18+
rm -rf $(DEPSDIR) *.dSYM
19+
20+
.PHONY: all clean

‎synch2/helpers.h

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#ifndef HELPERS_H
2+
#define HELPERS_H
3+
#include <unistd.h>
4+
#include <stdio.h>
5+
#include <assert.h>
6+
#include <stdlib.h>
7+
#include <string.h>
8+
#include <ctype.h>
9+
#include <signal.h>
10+
#include <sys/wait.h>
11+
#include <sys/time.h>
12+
#include <sys/select.h>
13+
#include <sched.h>
14+
#include <errno.h>
15+
#include <sys/resource.h>
16+
#include <fcntl.h>
17+
18+
// timestamp()
19+
// Return the current time as a double.
20+
21+
static inline double timestamp(void) {
22+
struct timeval tv;
23+
gettimeofday(&tv, NULL);
24+
return tv.tv_sec + tv.tv_usec / 1000000.0;
25+
}
26+
27+
28+
// nfork()
29+
// Like `fork()`, but nondeterministically runs the child first.
30+
//
31+
// This is actually no different from `fork`, since the OS is allowed to
32+
// run either process first (or to run them in parallel on multiple
33+
// cores), but in practice it is rare that the child runs first. This
34+
// function is useful for shaking out race conditions.
35+
36+
static inline pid_t nfork(void) {
37+
pid_t p = fork();
38+
if (p > 0) {
39+
struct timeval tv;
40+
gettimeofday(&tv, NULL);
41+
if (tv.tv_usec % 7 >= 4)
42+
usleep(tv.tv_usec % 7);
43+
}
44+
return p;
45+
}
46+
47+
48+
// handle_signal(signo, handler)
49+
// Install `handler` as the signal handler for `signo`.
50+
// The `handler` is automatically re-installed after signal delivery.
51+
// Has the same interface as `signal()` (`man 2 signal`), but is portable.
52+
53+
static inline int handle_signal(int signo, void (*handler)(int)) {
54+
struct sigaction sa;
55+
sa.sa_handler = handler; // call `handler` on signal
56+
sigemptyset(&sa.sa_mask); // don't block other signals in handler
57+
sa.sa_flags = 0; // don't restart system calls
58+
return sigaction(signo, &sa, NULL);
59+
}
60+
61+
62+
// raise_file_limit()
63+
// Attempt to raise the number of open file descriptors this
64+
// process is allowed.
65+
66+
static inline void raise_file_limit(void) {
67+
struct rlimit rlim;
68+
int r = getrlimit(RLIMIT_NOFILE, &rlim);
69+
if (r >= 0 && rlim.rlim_cur != RLIM_INFINITY) {
70+
if (geteuid() == 0 && rlim.rlim_max < 10240
71+
&& rlim.rlim_max != RLIM_INFINITY)
72+
rlim.rlim_cur = rlim.rlim_max = 10240;
73+
else
74+
rlim.rlim_cur = rlim.rlim_max;
75+
setrlimit(RLIMIT_NOFILE, &rlim);
76+
}
77+
}
78+
79+
80+
// make_nonblocking(fd)
81+
// Make file descriptor `fd` nonblocking: attempts to read
82+
// from `fd` will fail with errno EWOULDBLOCK if no data is
83+
// available, and attempts to write to `fd` will fail with
84+
// errno EWOULDBLOCK if no space is available. Not all file
85+
// descriptors can be made nonblocking, but pipes and network
86+
// sockets can.
87+
88+
static inline int make_nonblocking(int fd) {
89+
return fcntl(fd, F_SETFL, O_NONBLOCK);
90+
}
91+
92+
#endif

‎synch2/serviceblaster.c

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include "helpers.h"
2+
#include <netdb.h>
3+
4+
int main(int argc, char** argv) {
5+
const char* host = "localhost";
6+
const char* port = "6168";
7+
int max_conns = 10000;
8+
raise_file_limit();
9+
10+
int opt;
11+
while ((opt = getopt(argc, argv, "h:p:n:")) >= 0) {
12+
if (opt == 'h')
13+
host = optarg;
14+
else if (opt == 'p')
15+
port = optarg;
16+
else if (opt == 'n')
17+
max_conns = strtol(optarg, NULL, 0);
18+
}
19+
20+
struct addrinfo hints, *ai;
21+
memset(&hints, 0, sizeof(hints));
22+
hints.ai_family = AF_UNSPEC;
23+
hints.ai_socktype = SOCK_STREAM;
24+
hints.ai_flags = AI_NUMERICSERV;
25+
int r = getaddrinfo(host, port, &hints, &ai);
26+
if (r != 0) {
27+
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r));
28+
exit(1);
29+
}
30+
31+
int* cfds = (int*) malloc(max_conns * sizeof(int));
32+
int nconns;
33+
for (nconns = 0; nconns < max_conns; ++nconns) {
34+
cfds[nconns] = socket(ai->ai_family, ai->ai_socktype, 0);
35+
if (cfds[nconns] < 0) {
36+
perror("socket");
37+
break;
38+
}
39+
40+
r = connect(cfds[nconns], ai->ai_addr, ai->ai_addrlen);
41+
if (r < 0) {
42+
perror("connect");
43+
break;
44+
}
45+
46+
fprintf(stderr, "\r%d/%d connections", nconns, max_conns);
47+
}
48+
49+
fprintf(stderr, "Holding open %d connections\n", nconns);
50+
while (1)
51+
/* do nothing */;
52+
}

‎synch2/serviceclient.c

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "helpers.h"
2+
#include <netdb.h>
3+
4+
int main(int argc, char** argv) {
5+
const char* host = "localhost";
6+
const char* port = "6168";
7+
8+
// parse arguments
9+
int opt;
10+
while ((opt = getopt(argc, argv, "h:p:")) >= 0) {
11+
if (opt == 'h')
12+
host = optarg;
13+
else if (opt == 'p')
14+
port = optarg;
15+
}
16+
17+
// look up host and port
18+
struct addrinfo hints, *ai;
19+
memset(&hints, 0, sizeof(hints));
20+
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6
21+
hints.ai_socktype = SOCK_STREAM; // use TCP
22+
hints.ai_flags = AI_NUMERICSERV;
23+
int r = getaddrinfo(host, port, &hints, &ai);
24+
if (r != 0) {
25+
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r));
26+
exit(1);
27+
}
28+
29+
// connect to server
30+
int fd = socket(ai->ai_family, ai->ai_socktype, 0);
31+
if (fd < 0) {
32+
perror("socket");
33+
exit(1);
34+
}
35+
36+
r = connect(fd, ai->ai_addr, ai->ai_addrlen);
37+
if (r < 0) {
38+
perror("connect");
39+
exit(1);
40+
}
41+
42+
if (optind == argc)
43+
fprintf(stderr, "Usage: serviceclient NAME [NAME...]\n");
44+
45+
// write arguments
46+
FILE* f = fdopen(fd, "a+");
47+
char buf[BUFSIZ];
48+
for (; optind < argc; ++optind)
49+
fprintf(f, "%s\n", argv[optind]);
50+
// shut down writing (no more arguments)
51+
fflush(f);
52+
shutdown(fd, SHUT_WR);
53+
// read results
54+
while (fgets(buf, BUFSIZ, f))
55+
fputs(buf, stdout);
56+
57+
// done
58+
fclose(f); // This also closes `fd`.
59+
}

‎synch2/servicelookup.c

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "helpers.h"
2+
#include <netdb.h>
3+
4+
int main(int argc, char** argv) {
5+
// Usage: ./servicelookup SERVICE...
6+
if (argc == 1)
7+
fprintf(stderr, "Usage: servicelookup NAME [NAME...]\n");
8+
9+
for (int i = 1; i < argc; ++i) {
10+
// Look up the corresponding TCP port number
11+
struct servent* service = getservbyname(argv[i], "tcp");
12+
13+
// Print it
14+
int port = service ? ntohs(service->s_port) : 0;
15+
printf("%s,%d\n", argv[i], port);
16+
}
17+
}

‎synch2/serviceserver-02.c

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#include "helpers.h"
2+
#include <netinet/in.h>
3+
#include <arpa/inet.h>
4+
#include <netdb.h>
5+
6+
int make_listen(int port);
7+
void handle_connection(FILE* fin, FILE* fout);
8+
int remove_trailing_whitespace(char* buf);
9+
10+
int main(int argc, char** argv) {
11+
// Usage: ./serviceserver [PORT]
12+
int port = 6168;
13+
if (argc >= 2) {
14+
port = strtol(argv[1], NULL, 0);
15+
assert(port > 0 && port <= 65535);
16+
}
17+
18+
// Prepare listening socket
19+
int fd = make_listen(port);
20+
21+
// Automatically reap children
22+
signal(SIGCHLD, SIG_IGN);
23+
24+
while (1) {
25+
// Accept connection on listening socket
26+
int cfd = accept(fd, NULL, NULL);
27+
if (cfd < 0) {
28+
perror("accept");
29+
exit(1);
30+
}
31+
32+
// Fork child to handle connection
33+
pid_t p = fork();
34+
if (p == 0) {
35+
FILE* f = fdopen(cfd, "a+");
36+
handle_connection(f, f);
37+
fclose(f);
38+
exit(0);
39+
} else if (p < 0) {
40+
perror("fork");
41+
exit(1);
42+
}
43+
}
44+
}
45+
46+
void handle_connection(FILE* fin, FILE* fout) {
47+
char buf[BUFSIZ];
48+
49+
while (fgets(buf, BUFSIZ, fin))
50+
if (remove_trailing_whitespace(buf)) {
51+
// Look up the corresponding TCP port number
52+
struct servent* service = getservbyname(buf, "tcp");
53+
54+
// Print it
55+
int port = service ? ntohs(service->s_port) : 0;
56+
fprintf(fout, "%s,%d\n", buf, port);
57+
}
58+
59+
if (ferror(fin))
60+
perror("read");
61+
}
62+
63+
int make_listen(int port) {
64+
// Create socket
65+
int fd = socket(AF_INET, SOCK_STREAM, 0);
66+
assert(fd >= 0);
67+
68+
// Useful options: close-on-exec, reuse-address
69+
int r = fcntl(fd, F_SETFD, FD_CLOEXEC);
70+
assert(r >= 0);
71+
72+
int yes = 1;
73+
r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
74+
assert(r >= 0);
75+
76+
// Bind to port
77+
struct sockaddr_in address;
78+
memset(&address, 0, sizeof(address));
79+
address.sin_family = AF_INET;
80+
address.sin_port = htons(port);
81+
address.sin_addr.s_addr = INADDR_ANY;
82+
r = bind(fd, (struct sockaddr*) &address, sizeof(address));
83+
assert(r >= 0);
84+
85+
// Actually start listening
86+
r = listen(fd, 100);
87+
assert(r >= 0);
88+
89+
return fd;
90+
}
91+
92+
int remove_trailing_whitespace(char* buf) {
93+
int len = strlen(buf);
94+
while (len > 0 && isspace((unsigned char) buf[len - 1])) {
95+
--len;
96+
buf[len] = 0;
97+
}
98+
return len;
99+
}

‎synch2/serviceserver-03.c

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#include "helpers.h"
2+
#include <netinet/in.h>
3+
#include <arpa/inet.h>
4+
#include <netdb.h>
5+
6+
int make_listen(int port);
7+
void handle_connection(FILE* fin, FILE* fout);
8+
int remove_trailing_whitespace(char* buf);
9+
10+
int main(int argc, char** argv) {
11+
// Usage: ./serviceserver [PORT]
12+
int port = 6168;
13+
if (argc >= 2) {
14+
port = strtol(argv[1], NULL, 0);
15+
assert(port > 0 && port <= 65535);
16+
}
17+
18+
// Prepare listening socket
19+
int fd = make_listen(port);
20+
21+
// Automatically reap children
22+
signal(SIGCHLD, SIG_IGN);
23+
24+
while (1) {
25+
// Accept connection on listening socket
26+
int cfd = accept(fd, NULL, NULL);
27+
if (cfd < 0) {
28+
perror("accept");
29+
exit(1);
30+
}
31+
32+
// Fork child to handle connection
33+
pid_t p = fork();
34+
if (p == 0) {
35+
close(fd);
36+
FILE* f = fdopen(cfd, "a+");
37+
handle_connection(f, f);
38+
fclose(f);
39+
exit(0);
40+
} else if (p < 0) {
41+
perror("fork");
42+
exit(1);
43+
} else
44+
close(cfd);
45+
}
46+
}
47+
48+
void handle_connection(FILE* fin, FILE* fout) {
49+
char buf[BUFSIZ];
50+
51+
while (fgets(buf, BUFSIZ, fin))
52+
if (remove_trailing_whitespace(buf)) {
53+
// Look up the corresponding TCP port number
54+
struct servent* service = getservbyname(buf, "tcp");
55+
56+
// Print it
57+
int port = service ? ntohs(service->s_port) : 0;
58+
fprintf(fout, "%s,%d\n", buf, port);
59+
}
60+
61+
if (ferror(fin))
62+
perror("read");
63+
}
64+
65+
int make_listen(int port) {
66+
// Create socket
67+
int fd = socket(AF_INET, SOCK_STREAM, 0);
68+
assert(fd >= 0);
69+
70+
// Useful options: close-on-exec, reuse-address
71+
int r = fcntl(fd, F_SETFD, FD_CLOEXEC);
72+
assert(r >= 0);
73+
74+
int yes = 1;
75+
r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
76+
assert(r >= 0);
77+
78+
// Bind to port
79+
struct sockaddr_in address;
80+
memset(&address, 0, sizeof(address));
81+
address.sin_family = AF_INET;
82+
address.sin_port = htons(port);
83+
address.sin_addr.s_addr = INADDR_ANY;
84+
r = bind(fd, (struct sockaddr*) &address, sizeof(address));
85+
assert(r >= 0);
86+
87+
// Actually start listening
88+
r = listen(fd, 100);
89+
assert(r >= 0);
90+
91+
return fd;
92+
}
93+
94+
int remove_trailing_whitespace(char* buf) {
95+
int len = strlen(buf);
96+
while (len > 0 && isspace((unsigned char) buf[len - 1])) {
97+
--len;
98+
buf[len] = 0;
99+
}
100+
return len;
101+
}

‎synch2/serviceserver-04.c

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#include "helpers.h"
2+
#include <netinet/in.h>
3+
#include <arpa/inet.h>
4+
#include <netdb.h>
5+
#include <pthread.h>
6+
7+
int make_listen(int port);
8+
void handle_connection(FILE* fin, FILE* fout);
9+
int remove_trailing_whitespace(char* buf);
10+
11+
void* connection_thread(void* arg) {
12+
FILE* f = (FILE*) arg;
13+
pthread_detach(pthread_self());
14+
handle_connection(f, f);
15+
fclose(f);
16+
pthread_exit(0);
17+
}
18+
19+
int main(int argc, char** argv) {
20+
// Usage: ./serviceserver [PORT]
21+
int port = 6168;
22+
if (argc >= 2) {
23+
port = strtol(argv[1], NULL, 0);
24+
assert(port > 0 && port <= 65535);
25+
}
26+
27+
// Prepare listening socket
28+
int fd = make_listen(port);
29+
30+
while (1) {
31+
// Accept connection on listening socket
32+
int cfd = accept(fd, NULL, NULL);
33+
if (cfd < 0) {
34+
perror("accept");
35+
exit(1);
36+
}
37+
38+
// Start thread to handle connection; no buffering please
39+
pthread_t t;
40+
FILE* f = fdopen(cfd, "a+");
41+
setvbuf(f, NULL, _IONBF, 0);
42+
int r = pthread_create(&t, NULL, connection_thread, (void*) f);
43+
if (r != 0) {
44+
perror("pthread_create");
45+
exit(1);
46+
}
47+
}
48+
}
49+
50+
void handle_connection(FILE* fin, FILE* fout) {
51+
char buf[BUFSIZ];
52+
53+
while (fgets(buf, BUFSIZ, fin))
54+
if (remove_trailing_whitespace(buf)) {
55+
// Look up the corresponding TCP port number
56+
struct servent* service = getservbyname(buf, "tcp");
57+
58+
// Print it
59+
int port = service ? ntohs(service->s_port) : 0;
60+
fprintf(fout, "%s,%d\n", buf, port);
61+
}
62+
63+
if (ferror(fin))
64+
perror("read");
65+
}
66+
67+
int make_listen(int port) {
68+
// Create socket
69+
int fd = socket(AF_INET, SOCK_STREAM, 0);
70+
assert(fd >= 0);
71+
72+
// Useful options: close-on-exec, reuse-address
73+
int r = fcntl(fd, F_SETFD, FD_CLOEXEC);
74+
assert(r >= 0);
75+
76+
int yes = 1;
77+
r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
78+
assert(r >= 0);
79+
80+
// Bind to port
81+
struct sockaddr_in address;
82+
memset(&address, 0, sizeof(address));
83+
address.sin_family = AF_INET;
84+
address.sin_port = htons(port);
85+
address.sin_addr.s_addr = INADDR_ANY;
86+
r = bind(fd, (struct sockaddr*) &address, sizeof(address));
87+
assert(r >= 0);
88+
89+
// Actually start listening
90+
r = listen(fd, 100);
91+
assert(r >= 0);
92+
93+
return fd;
94+
}
95+
96+
int remove_trailing_whitespace(char* buf) {
97+
int len = strlen(buf);
98+
while (len > 0 && isspace((unsigned char) buf[len - 1])) {
99+
--len;
100+
buf[len] = 0;
101+
}
102+
return len;
103+
}

‎synch2/serviceserver-05.c

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#include "helpers.h"
2+
#include <netinet/in.h>
3+
#include <arpa/inet.h>
4+
#include <netdb.h>
5+
#include <pthread.h>
6+
7+
int make_listen(int port);
8+
void handle_connection(FILE* fin, FILE* fout);
9+
int remove_trailing_whitespace(char* buf);
10+
11+
static int n_connection_threads;
12+
13+
void* connection_thread(void* arg) {
14+
FILE* f = (FILE*) arg;
15+
++n_connection_threads;
16+
pthread_detach(pthread_self());
17+
handle_connection(f, f);
18+
fclose(f);
19+
--n_connection_threads;
20+
pthread_exit(0);
21+
}
22+
23+
int main(int argc, char** argv) {
24+
// Usage: ./serviceserver [PORT]
25+
int port = 6168;
26+
if (argc >= 2) {
27+
port = strtol(argv[1], NULL, 0);
28+
assert(port > 0 && port <= 65535);
29+
}
30+
31+
// Prepare listening socket
32+
int fd = make_listen(port);
33+
34+
while (1) {
35+
// Accept connection on listening socket
36+
int cfd = accept(fd, NULL, NULL);
37+
if (cfd < 0) {
38+
perror("accept");
39+
exit(1);
40+
}
41+
42+
// If too many threads, wait until one exits
43+
while (n_connection_threads > 100)
44+
sched_yield();
45+
46+
// Start thread to handle connection; no buffering please
47+
pthread_t t;
48+
FILE* f = fdopen(cfd, "a+");
49+
setvbuf(f, NULL, _IONBF, 0);
50+
int r = pthread_create(&t, NULL, connection_thread, (void*) f);
51+
if (r != 0) {
52+
perror("pthread_create");
53+
exit(1);
54+
}
55+
}
56+
}
57+
58+
void handle_connection(FILE* fin, FILE* fout) {
59+
char buf[BUFSIZ];
60+
61+
while (fgets(buf, BUFSIZ, fin))
62+
if (remove_trailing_whitespace(buf)) {
63+
// Look up the corresponding TCP port number
64+
struct servent* service = getservbyname(buf, "tcp");
65+
66+
// Print it
67+
int port = service ? ntohs(service->s_port) : 0;
68+
fprintf(fout, "%s,%d\n", buf, port);
69+
}
70+
71+
if (ferror(fin))
72+
perror("read");
73+
}
74+
75+
int make_listen(int port) {
76+
// Create socket
77+
int fd = socket(AF_INET, SOCK_STREAM, 0);
78+
assert(fd >= 0);
79+
80+
// Useful options: close-on-exec, reuse-address
81+
int r = fcntl(fd, F_SETFD, FD_CLOEXEC);
82+
assert(r >= 0);
83+
84+
int yes = 1;
85+
r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
86+
assert(r >= 0);
87+
88+
// Bind to port
89+
struct sockaddr_in address;
90+
memset(&address, 0, sizeof(address));
91+
address.sin_family = AF_INET;
92+
address.sin_port = htons(port);
93+
address.sin_addr.s_addr = INADDR_ANY;
94+
r = bind(fd, (struct sockaddr*) &address, sizeof(address));
95+
assert(r >= 0);
96+
97+
// Actually start listening
98+
r = listen(fd, 100);
99+
assert(r >= 0);
100+
101+
return fd;
102+
}
103+
104+
int remove_trailing_whitespace(char* buf) {
105+
int len = strlen(buf);
106+
while (len > 0 && isspace((unsigned char) buf[len - 1])) {
107+
--len;
108+
buf[len] = 0;
109+
}
110+
return len;
111+
}

‎synch2/serviceserver-06.c

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#include "helpers.h"
2+
#include <netinet/in.h>
3+
#include <arpa/inet.h>
4+
#include <netdb.h>
5+
#include <pthread.h>
6+
7+
int make_listen(int port);
8+
void handle_connection(FILE* fin, FILE* fout);
9+
int remove_trailing_whitespace(char* buf);
10+
11+
static pthread_mutex_t mutex;
12+
static int n_connection_threads;
13+
14+
void* connection_thread(void* arg) {
15+
FILE* f = (FILE*) arg;
16+
17+
pthread_mutex_lock(&mutex);
18+
++n_connection_threads;
19+
pthread_mutex_unlock(&mutex);
20+
21+
pthread_detach(pthread_self());
22+
handle_connection(f, f);
23+
fclose(f);
24+
25+
pthread_mutex_lock(&mutex);
26+
--n_connection_threads;
27+
pthread_mutex_unlock(&mutex);
28+
pthread_exit(0);
29+
}
30+
31+
int main(int argc, char** argv) {
32+
// Usage: ./serviceserver [PORT]
33+
int port = 6168;
34+
if (argc >= 2) {
35+
port = strtol(argv[1], NULL, 0);
36+
assert(port > 0 && port <= 65535);
37+
}
38+
39+
// Prepare listening socket
40+
int fd = make_listen(port);
41+
42+
pthread_mutex_init(&mutex, NULL);
43+
44+
while (1) {
45+
// Accept connection on listening socket
46+
int cfd = accept(fd, NULL, NULL);
47+
if (cfd < 0) {
48+
perror("accept");
49+
exit(1);
50+
}
51+
52+
// If too many threads, wait until one exits
53+
while (n_connection_threads > 100)
54+
sched_yield();
55+
56+
// Start thread to handle connection; no buffering please
57+
pthread_t t;
58+
FILE* f = fdopen(cfd, "a+");
59+
setvbuf(f, NULL, _IONBF, 0);
60+
int r = pthread_create(&t, NULL, connection_thread, (void*) f);
61+
if (r != 0) {
62+
perror("pthread_create");
63+
exit(1);
64+
}
65+
}
66+
}
67+
68+
void handle_connection(FILE* fin, FILE* fout) {
69+
char buf[BUFSIZ];
70+
71+
while (fgets(buf, BUFSIZ, fin))
72+
if (remove_trailing_whitespace(buf)) {
73+
// Look up the corresponding TCP port number
74+
struct servent* service = getservbyname(buf, "tcp");
75+
76+
// Print it
77+
int port = service ? ntohs(service->s_port) : 0;
78+
fprintf(fout, "%s,%d\n", buf, port);
79+
}
80+
81+
if (ferror(fin))
82+
perror("read");
83+
}
84+
85+
int make_listen(int port) {
86+
// Create socket
87+
int fd = socket(AF_INET, SOCK_STREAM, 0);
88+
assert(fd >= 0);
89+
90+
// Useful options: close-on-exec, reuse-address
91+
int r = fcntl(fd, F_SETFD, FD_CLOEXEC);
92+
assert(r >= 0);
93+
94+
int yes = 1;
95+
r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
96+
assert(r >= 0);
97+
98+
// Bind to port
99+
struct sockaddr_in address;
100+
memset(&address, 0, sizeof(address));
101+
address.sin_family = AF_INET;
102+
address.sin_port = htons(port);
103+
address.sin_addr.s_addr = INADDR_ANY;
104+
r = bind(fd, (struct sockaddr*) &address, sizeof(address));
105+
assert(r >= 0);
106+
107+
// Actually start listening
108+
r = listen(fd, 100);
109+
assert(r >= 0);
110+
111+
return fd;
112+
}
113+
114+
int remove_trailing_whitespace(char* buf) {
115+
int len = strlen(buf);
116+
while (len > 0 && isspace((unsigned char) buf[len - 1])) {
117+
--len;
118+
buf[len] = 0;
119+
}
120+
return len;
121+
}

‎synch2/serviceserver-07.c

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#include "helpers.h"
2+
#include <netinet/in.h>
3+
#include <arpa/inet.h>
4+
#include <netdb.h>
5+
#include <pthread.h>
6+
7+
int make_listen(int port);
8+
void handle_connection(FILE* fin, FILE* fout);
9+
int remove_trailing_whitespace(char* buf);
10+
11+
static pthread_mutex_t mutex;
12+
static pthread_cond_t condvar;
13+
static int n_connection_threads;
14+
15+
void* connection_thread(void* arg) {
16+
FILE* f = (FILE*) arg;
17+
18+
pthread_mutex_lock(&mutex);
19+
++n_connection_threads;
20+
pthread_mutex_unlock(&mutex);
21+
22+
pthread_detach(pthread_self());
23+
handle_connection(f, f);
24+
fclose(f);
25+
26+
pthread_mutex_lock(&mutex);
27+
--n_connection_threads;
28+
pthread_cond_signal(&condvar);
29+
pthread_mutex_unlock(&mutex);
30+
pthread_exit(0);
31+
}
32+
33+
int main(int argc, char** argv) {
34+
// Usage: ./serviceserver [PORT]
35+
int port = 6168;
36+
if (argc >= 2) {
37+
port = strtol(argv[1], NULL, 0);
38+
assert(port > 0 && port <= 65535);
39+
}
40+
41+
// Prepare listening socket
42+
int fd = make_listen(port);
43+
44+
pthread_mutex_init(&mutex, NULL);
45+
pthread_cond_init(&condvar, NULL);
46+
47+
while (1) {
48+
// Accept connection on listening socket
49+
int cfd = accept(fd, NULL, NULL);
50+
if (cfd < 0) {
51+
perror("accept");
52+
exit(1);
53+
}
54+
55+
// If too many threads, wait until one exits
56+
while (n_connection_threads > 100) {
57+
pthread_mutex_lock(&mutex);
58+
if (n_connection_threads > 100)
59+
pthread_cond_wait(&condvar, &mutex);
60+
pthread_mutex_unlock(&mutex);
61+
}
62+
63+
// Start thread to handle connection; no buffering please
64+
pthread_t t;
65+
FILE* f = fdopen(cfd, "a+");
66+
setvbuf(f, NULL, _IONBF, 0);
67+
int r = pthread_create(&t, NULL, connection_thread, (void*) f);
68+
if (r != 0) {
69+
perror("pthread_create");
70+
exit(1);
71+
}
72+
}
73+
}
74+
75+
void handle_connection(FILE* fin, FILE* fout) {
76+
char buf[BUFSIZ];
77+
78+
while (fgets(buf, BUFSIZ, fin))
79+
if (remove_trailing_whitespace(buf)) {
80+
// Look up the corresponding TCP port number
81+
struct servent* service = getservbyname(buf, "tcp");
82+
83+
// Print it
84+
int port = service ? ntohs(service->s_port) : 0;
85+
fprintf(fout, "%s,%d\n", buf, port);
86+
}
87+
88+
if (ferror(fin))
89+
perror("read");
90+
}
91+
92+
int make_listen(int port) {
93+
// Create socket
94+
int fd = socket(AF_INET, SOCK_STREAM, 0);
95+
assert(fd >= 0);
96+
97+
// Useful options: close-on-exec, reuse-address
98+
int r = fcntl(fd, F_SETFD, FD_CLOEXEC);
99+
assert(r >= 0);
100+
101+
int yes = 1;
102+
r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
103+
assert(r >= 0);
104+
105+
// Bind to port
106+
struct sockaddr_in address;
107+
memset(&address, 0, sizeof(address));
108+
address.sin_family = AF_INET;
109+
address.sin_port = htons(port);
110+
address.sin_addr.s_addr = INADDR_ANY;
111+
r = bind(fd, (struct sockaddr*) &address, sizeof(address));
112+
assert(r >= 0);
113+
114+
// Actually start listening
115+
r = listen(fd, 100);
116+
assert(r >= 0);
117+
118+
return fd;
119+
}
120+
121+
int remove_trailing_whitespace(char* buf) {
122+
int len = strlen(buf);
123+
while (len > 0 && isspace((unsigned char) buf[len - 1])) {
124+
--len;
125+
buf[len] = 0;
126+
}
127+
return len;
128+
}

‎synch2/serviceserver-08.c

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#include "helpers.h"
2+
#include <netinet/in.h>
3+
#include <arpa/inet.h>
4+
#include <netdb.h>
5+
#include <pthread.h>
6+
7+
// Connection structure
8+
#define CONN_BUFSIZ 1024
9+
typedef struct conn {
10+
FILE* f;
11+
char buf[CONN_BUFSIZ];
12+
struct conn* next;
13+
struct conn* prev;
14+
} conn;
15+
#define MAX_CONNS 10240
16+
conn* conns = NULL;
17+
18+
int make_listen(int port);
19+
void handle_connection(conn* c);
20+
int remove_trailing_whitespace(char* buf);
21+
22+
int main(int argc, char** argv) {
23+
// Usage: ./serviceserver [PORT]
24+
int port = 6168;
25+
if (argc >= 2) {
26+
port = strtol(argv[1], NULL, 0);
27+
assert(port > 0 && port <= 65535);
28+
}
29+
30+
// Prepare listening socket
31+
int lfd = make_listen(port);
32+
make_nonblocking(lfd);
33+
34+
// Prepare connection array
35+
raise_file_limit();
36+
37+
while (1) {
38+
// Prepare set of relevant connections
39+
fd_set rfds;
40+
FD_ZERO(&rfds);
41+
FD_SET(lfd, &rfds); // New connection attempts
42+
int max_fd = lfd;
43+
for (conn* c = conns; c; c = c->next) {
44+
int fd = fileno(c->f);
45+
FD_SET(fd, &rfds);
46+
max_fd = (fd > max_fd ? fd : max_fd);
47+
}
48+
49+
// Select to block until we're ready
50+
(void) select(max_fd + 1, &rfds, NULL, NULL, NULL);
51+
52+
// Maybe accept an incoming connection
53+
if (FD_ISSET(lfd, &rfds)) {
54+
int cfd = accept(lfd, NULL, NULL);
55+
if (cfd < 0) {
56+
perror("accept");
57+
exit(1);
58+
}
59+
make_nonblocking(cfd);
60+
61+
conn* c = (conn*) malloc(sizeof(conn));
62+
c->f = fdopen(cfd, "a+");
63+
c->buf[0] = 0; // empty string
64+
c->next = conns;
65+
if (conns)
66+
conns->prev = c;
67+
c->prev = NULL;
68+
conns = c;
69+
}
70+
71+
// Handle data from existing connections
72+
for (conn* c = conns; c; ) {
73+
conn* next = c->next;
74+
if (FD_ISSET(fileno(c->f), &rfds))
75+
handle_connection(c); /* might close `c` */
76+
c = next;
77+
}
78+
}
79+
}
80+
81+
void handle_connection(conn* c) {
82+
size_t len = strlen(c->buf);
83+
84+
// Attempt to load more data into the buffer.
85+
// If you fail, maybe try again later
86+
int ch = EOF;
87+
while (len != CONN_BUFSIZ - 1
88+
&& (ch = fgetc(c->f)) != EOF
89+
&& ch != '\n') {
90+
c->buf[len] = ch;
91+
++len;
92+
}
93+
c->buf[len] = 0;
94+
95+
// Maybe the user ran out of data to send. Just try again later
96+
if (len != CONN_BUFSIZ - 1 && ch == EOF
97+
&& ferror(c->f) && errno == EAGAIN)
98+
return;
99+
100+
// At this point we have a line
101+
102+
// Remove trailing whitespace
103+
if (remove_trailing_whitespace(c->buf)) {
104+
struct servent* service = getservbyname(c->buf, "tcp");
105+
int port = service ? ntohs(service->s_port) : 0;
106+
fprintf(c->f, "%s,%d\n", c->buf, port);
107+
fflush(c->f);
108+
}
109+
110+
c->buf[0] = 0;
111+
112+
// If the file is done, remove the connection
113+
if (feof(c->f) || ferror(c->f)) {
114+
fclose(c->f);
115+
if (c->prev)
116+
c->prev->next = c->next;
117+
else
118+
conns = c->next;
119+
if (c->next)
120+
c->next->prev = c->prev;
121+
free(c);
122+
}
123+
}
124+
125+
int make_listen(int port) {
126+
// Create socket
127+
int fd = socket(AF_INET, SOCK_STREAM, 0);
128+
assert(fd >= 0);
129+
130+
// Useful options: close-on-exec, reuse-address
131+
int r = fcntl(fd, F_SETFD, FD_CLOEXEC);
132+
assert(r >= 0);
133+
134+
int yes = 1;
135+
r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
136+
assert(r >= 0);
137+
138+
// Bind to port
139+
struct sockaddr_in address;
140+
memset(&address, 0, sizeof(address));
141+
address.sin_family = AF_INET;
142+
address.sin_port = htons(port);
143+
address.sin_addr.s_addr = INADDR_ANY;
144+
r = bind(fd, (struct sockaddr*) &address, sizeof(address));
145+
assert(r >= 0);
146+
147+
// Actually start listening
148+
r = listen(fd, 100);
149+
assert(r >= 0);
150+
151+
return fd;
152+
}
153+
154+
int remove_trailing_whitespace(char* buf) {
155+
int len = strlen(buf);
156+
while (len > 0 && isspace((unsigned char) buf[len - 1])) {
157+
--len;
158+
buf[len] = 0;
159+
}
160+
return len;
161+
}

‎synch2/serviceserver-09.c

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#include "helpers.h"
2+
#include <netinet/in.h>
3+
#include <arpa/inet.h>
4+
#include <netdb.h>
5+
#include <pthread.h>
6+
7+
// Connection structure
8+
#define CONN_BUFSIZ 1024
9+
typedef struct conn {
10+
FILE* f;
11+
char buf[CONN_BUFSIZ];
12+
struct conn* next;
13+
struct conn* prev;
14+
} conn;
15+
#define MAX_CONNS 10240
16+
conn* conns = NULL;
17+
18+
int make_listen(int port);
19+
void handle_connection(conn* c);
20+
void close_connection(conn* c);
21+
int remove_trailing_whitespace(char* buf);
22+
23+
int main(int argc, char** argv) {
24+
// Usage: ./serviceserver [PORT]
25+
int port = 6168;
26+
if (argc >= 2) {
27+
port = strtol(argv[1], NULL, 0);
28+
assert(port > 0 && port <= 65535);
29+
}
30+
31+
// Prepare listening socket
32+
int lfd = make_listen(port);
33+
make_nonblocking(lfd);
34+
35+
// Prepare connection array
36+
raise_file_limit();
37+
38+
while (1) {
39+
// Prepare set of relevant connections
40+
fd_set rfds;
41+
FD_ZERO(&rfds);
42+
FD_SET(lfd, &rfds); // New connection attempts
43+
int max_fd = lfd;
44+
for (conn* c = conns; c; c = c->next) {
45+
int fd = fileno(c->f);
46+
FD_SET(fd, &rfds);
47+
max_fd = (fd > max_fd ? fd : max_fd);
48+
}
49+
50+
// Select to block until we're ready
51+
(void) select(max_fd + 1, &rfds, NULL, NULL, NULL);
52+
53+
// Maybe accept an incoming connection
54+
if (FD_ISSET(lfd, &rfds)) {
55+
int cfd = accept(lfd, NULL, NULL);
56+
if (cfd < 0 && errno == EMFILE) {
57+
for (int i = 0; i < 10 && conns; ++i)
58+
close_connection(conns);
59+
continue;
60+
} else if (cfd < 0) {
61+
perror("accept");
62+
exit(1);
63+
}
64+
make_nonblocking(cfd);
65+
66+
conn* c = (conn*) malloc(sizeof(conn));
67+
c->f = fdopen(cfd, "a+");
68+
c->buf[0] = 0; // empty string
69+
c->next = conns;
70+
if (conns)
71+
conns->prev = c;
72+
c->prev = NULL;
73+
conns = c;
74+
}
75+
76+
// Handle data from existing connections
77+
for (conn* c = conns; c; ) {
78+
conn* next = c->next;
79+
if (FD_ISSET(fileno(c->f), &rfds))
80+
handle_connection(c); /* might close `c` */
81+
c = next;
82+
}
83+
}
84+
}
85+
86+
void handle_connection(conn* c) {
87+
size_t len = strlen(c->buf);
88+
89+
// Attempt to load more data into the buffer.
90+
// If you fail, maybe try again later
91+
int ch = EOF;
92+
while (len != CONN_BUFSIZ - 1
93+
&& (ch = fgetc(c->f)) != EOF
94+
&& ch != '\n') {
95+
c->buf[len] = ch;
96+
++len;
97+
}
98+
c->buf[len] = 0;
99+
100+
// Maybe the user ran out of data to send. Just try again later
101+
if (len != CONN_BUFSIZ - 1 && ch == EOF
102+
&& ferror(c->f) && errno == EAGAIN)
103+
return;
104+
105+
// At this point we have a line
106+
107+
// Remove trailing whitespace
108+
if (remove_trailing_whitespace(c->buf)) {
109+
struct servent* service = getservbyname(c->buf, "tcp");
110+
int port = service ? ntohs(service->s_port) : 0;
111+
fprintf(c->f, "%s,%d\n", c->buf, port);
112+
fflush(c->f);
113+
}
114+
115+
c->buf[0] = 0;
116+
117+
// If the file is done, remove the connection
118+
if (feof(c->f) || ferror(c->f))
119+
close_connection(c);
120+
}
121+
122+
void close_connection(conn* c) {
123+
fclose(c->f);
124+
if (c->prev)
125+
c->prev->next = c->next;
126+
else
127+
conns = c->next;
128+
if (c->next)
129+
c->next->prev = c->prev;
130+
free(c);
131+
}
132+
133+
int make_listen(int port) {
134+
// Create socket
135+
int fd = socket(AF_INET, SOCK_STREAM, 0);
136+
assert(fd >= 0);
137+
138+
// Useful options: close-on-exec, reuse-address
139+
int r = fcntl(fd, F_SETFD, FD_CLOEXEC);
140+
assert(r >= 0);
141+
142+
int yes = 1;
143+
r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
144+
assert(r >= 0);
145+
146+
// Bind to port
147+
struct sockaddr_in address;
148+
memset(&address, 0, sizeof(address));
149+
address.sin_family = AF_INET;
150+
address.sin_port = htons(port);
151+
address.sin_addr.s_addr = INADDR_ANY;
152+
r = bind(fd, (struct sockaddr*) &address, sizeof(address));
153+
assert(r >= 0);
154+
155+
// Actually start listening
156+
r = listen(fd, 100);
157+
assert(r >= 0);
158+
159+
return fd;
160+
}
161+
162+
int remove_trailing_whitespace(char* buf) {
163+
int len = strlen(buf);
164+
while (len > 0 && isspace((unsigned char) buf[len - 1])) {
165+
--len;
166+
buf[len] = 0;
167+
}
168+
return len;
169+
}

‎synch2/serviceserver.c

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include "helpers.h"
2+
#include <netinet/in.h>
3+
#include <arpa/inet.h>
4+
#include <netdb.h>
5+
6+
int make_listen(int port);
7+
void handle_connection(FILE* fin, FILE* fout);
8+
int remove_trailing_whitespace(char* buf);
9+
10+
int main(int argc, char** argv) {
11+
// Usage: ./serviceserver [PORT]
12+
int port = 6168;
13+
if (argc >= 2) {
14+
port = strtol(argv[1], NULL, 0);
15+
assert(port > 0 && port <= 65535);
16+
}
17+
18+
// Prepare listening socket
19+
int fd = make_listen(port);
20+
21+
while (1) {
22+
// Accept connection on listening socket
23+
int cfd = accept(fd, NULL, NULL);
24+
if (cfd < 0) {
25+
perror("accept");
26+
exit(1);
27+
}
28+
29+
// Handle connection
30+
FILE* f = fdopen(cfd, "a+");
31+
handle_connection(f, f);
32+
fclose(f);
33+
}
34+
}
35+
36+
void handle_connection(FILE* fin, FILE* fout) {
37+
char buf[BUFSIZ];
38+
39+
while (fgets(buf, BUFSIZ, fin))
40+
if (remove_trailing_whitespace(buf)) {
41+
// Look up the corresponding TCP port number
42+
struct servent* service = getservbyname(buf, "tcp");
43+
44+
// Print it
45+
int port = service ? ntohs(service->s_port) : 0;
46+
fprintf(fout, "%s,%d\n", buf, port);
47+
}
48+
49+
if (ferror(fin))
50+
perror("read");
51+
}
52+
53+
int make_listen(int port) {
54+
// Create socket
55+
int fd = socket(AF_INET, SOCK_STREAM, 0);
56+
assert(fd >= 0);
57+
58+
// Useful options: close-on-exec, reuse-address
59+
int r = fcntl(fd, F_SETFD, FD_CLOEXEC);
60+
assert(r >= 0);
61+
62+
int yes = 1;
63+
r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
64+
assert(r >= 0);
65+
66+
// Bind to port
67+
struct sockaddr_in address;
68+
memset(&address, 0, sizeof(address));
69+
address.sin_family = AF_INET;
70+
address.sin_port = htons(port);
71+
address.sin_addr.s_addr = INADDR_ANY;
72+
r = bind(fd, (struct sockaddr*) &address, sizeof(address));
73+
assert(r >= 0);
74+
75+
// Actually start listening
76+
r = listen(fd, 100);
77+
assert(r >= 0);
78+
79+
return fd;
80+
}
81+
82+
int remove_trailing_whitespace(char* buf) {
83+
int len = strlen(buf);
84+
while (len > 0 && isspace((unsigned char) buf[len - 1])) {
85+
--len;
86+
buf[len] = 0;
87+
}
88+
return len;
89+
}

0 commit comments

Comments
 (0)
Please sign in to comment.