tcp-server

a TCP client-server application written in C using sockets


tcp-server / server.c

server.c (4.4K) raw | download
1#define _POSIX_C_SOURCE 200809L
2#include <sys/socket.h>
3#include <sys/wait.h>
4#include <netinet/in.h>
5#include <signal.h>
6#include <unistd.h>
7#include <errno.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <time.h>
12
13static int
14write_all(int fd, const char *buf, size_t len)
15{
16 while (len > 0) {
17 ssize_t n = write(fd, buf, len);
18 if (n < 0 && errno == EINTR) continue;
19 if (n <= 0) return -1;
20 buf += n;
21 len -= (size_t)n;
22 }
23 return 0;
24}
25
26/* Read only through the newline, leaving subsequent messages for the next call. */
27static int
28read_line(int fd, char *line, size_t size)
29{
30 size_t len = 0;
31 for (;;) {
32 char c;
33 ssize_t n = read(fd, &c, 1);
34 if (n < 0 && errno == EINTR) continue;
35 if (n < 0) return -1;
36 if (n == 0) return len == 0 ? 0 : -3;
37 if (c == '\0') return -3;
38 if (c == '\n') {
39 line[len] = '\0';
40 return 1;
41 }
42 if (len + 1 >= size) return -2;
43 line[len++] = c;
44 }
45}
46
47static void
48handle_client(int fd)
49{
50 char line[4097], reply[80];
51 for (;;) {
52 const char *text;
53 int result = read_line(fd, line, sizeof(line));
54 int quit = 0;
55 if (result <= 0) {
56 if (result == -1) perror("read");
57 if (result == -2)
58 (void)write_all(fd, "ERR line too long\n", 18);
59 if (result == -3)
60 (void)write_all(fd, "ERR malformed line\n", 19);
61 return;
62 }
63 if (strncmp(line, "ECHO ", 5) == 0) {
64 text = line + 5;
65 } else if (strcmp(line, "TIME") == 0) {
66 time_t now = time(NULL);
67 struct tm tm;
68 if (now == (time_t)-1 || localtime_r(&now, &tm) == NULL ||
69 strftime(reply, sizeof(reply), "%Y-%m-%d %H:%M:%S %z", &tm) == 0)
70 text = "ERR time unavailable";
71 else
72 text = reply;
73 } else if (strcmp(line, "QUIT") == 0) {
74 text = "Bye";
75 quit = 1;
76 } else {
77 text = "ERR unknown command";
78 }
79 if (write_all(fd, text, strlen(text)) < 0 ||
80 write_all(fd, "\n", 1) < 0)
81 return;
82 if (quit) return;
83 }
84}
85
86static void
87reap_children(int sig)
88{
89 int saved = errno;
90 pid_t pid;
91 (void)sig;
92 do {
93 pid = waitpid(-1, NULL, WNOHANG);
94 } while (pid > 0 || (pid < 0 && errno == EINTR));
95 errno = saved;
96}
97
98int
99main(int argc, char **argv)
100{
101 long port = 9000;
102 char *end;
103 int listener, one = 1;
104 struct sockaddr_in address = {0};
105 struct sigaction action = {0};
106
107 if (argc > 2) {
108 fprintf(stderr, "usage: %s [port]\n", argv[0]);
109 return 1;
110 }
111 if (argc == 2) {
112 errno = 0;
113 port = strtol(argv[1], &end, 10);
114 if (errno || !*argv[1] || *end || port < 1 || port > 65535) {
115 fprintf(stderr, "invalid port: %s\n", argv[1]);
116 return 1;
117 }
118 }
119 sigemptyset(&action.sa_mask);
120 action.sa_handler = SIG_IGN;
121 if (sigaction(SIGPIPE, &action, NULL) < 0) {
122 perror("sigaction");
123 return 1;
124 }
125 action.sa_handler = reap_children;
126 action.sa_flags = SA_RESTART | SA_NOCLDSTOP;
127 if (sigaction(SIGCHLD, &action, NULL) < 0) {
128 perror("sigaction");
129 return 1;
130 }
131 listener = socket(AF_INET, SOCK_STREAM, 0);
132 if (listener < 0) {
133 perror("socket");
134 return 1;
135 }
136 address.sin_family = AF_INET;
137 address.sin_addr.s_addr = htonl(INADDR_ANY);
138 address.sin_port = htons((unsigned short)port);
139 if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0 ||
140 bind(listener, (struct sockaddr *)&address, sizeof(address)) < 0 ||
141 listen(listener, 32) < 0) {
142 perror("server");
143 close(listener);
144 return 1;
145 }
146 printf("listening on port %ld\n", port);
147 fflush(stdout);
148 for (;;) {
149 int client = accept(listener, NULL, NULL);
150 pid_t pid;
151 if (client < 0) {
152 if (errno == EINTR || errno == ECONNABORTED) continue;
153 perror("accept");
154 break;
155 }
156 pid = fork();
157 if (pid == 0) {
158 close(listener);
159 handle_client(client);
160 close(client);
161 _exit(0);
162 }
163 if (pid < 0) perror("fork");
164 close(client);
165 }
166 close(listener);
167 return 1;
168}