Improved calculation of flags in SOCKET-SEND and SOCKET-RECEIVE.
[iolib.git] / tests / echo-server.c
blobe45bbc2460b7e40009b3c84ec76256a89a017eb2
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*-
3 * echo-server.c --- Simple echo server for testing purposes.
5 * Copyright (C) 2007, Luis Oliveira <loliveira@common-lisp.net>
7 * Permission is hereby granted, free of charge, to any person
8 * obtaining a copy of this software and associated documentation
9 * files (the "Software"), to deal in the Software without
10 * restriction, including without limitation the rights to use, copy,
11 * modify, merge, publish, distribute, sublicense, and/or sell copies
12 * of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
28 /* TODO: IPv6 support, port to Winsock */
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <time.h>
33 #include <ctype.h>
34 #include <string.h>
36 #ifdef _WIN32
37 #include <windows.h>
38 #include <Winsock2.h>
39 #include <Ws2tcpip.h>
40 #else
41 #include <unistd.h>
42 #include <errno.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <sys/select.h>
48 #endif
50 #define DEFAULT_PORT 7
51 #define BACKLOG 10
52 #define MAXCONS FD_SETSIZE
54 static int
55 sendallto(int s, char *buf, int len, struct sockaddr *to, socklen_t tolen)
57 int sent = 0;
58 int left = len;
60 while (sent < len) {
61 int n = sendto(s, buf+sent, left, 0, to, tolen);
62 if (n == -1) { return -1; }
63 sent += n;
64 left -= n;
67 return 0;
70 static char time_str[9];
72 static const char* timestamp()
74 time_t now = time(NULL);
75 struct tm *tms = localtime(&now);
76 strftime(time_str, (sizeof time_str)-1, "%T", tms);
77 return time_str;
80 int main(int argc, char *argv[])
82 int fds[MAXCONS];
83 struct sockaddr_in addrs[MAXCONS];
84 int conns = 0;
85 int local_port, listenfd, dgramfd;
86 struct sockaddr_in local;
87 socklen_t sin_size = sizeof(struct sockaddr_in);
89 switch (argc) {
90 case 1: local_port = DEFAULT_PORT; break;
91 case 2: local_port = strtol(argv[1], NULL, 10); break;
92 default:
93 fprintf(stderr, "Usage: %s [port number]\n", argv[0]);
94 fprintf(stderr, "The default port is: %d.\n", DEFAULT_PORT);
95 exit(1);
98 if (local_port <= 0 || local_port > 65535) {
99 fprintf(stderr, "Error: invalid port number.\n");
100 exit(1);
103 printf("[%s] listening on port %d\n", timestamp(), local_port);
105 if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
106 perror("socket");
107 exit(1);
110 fds[conns++] = listenfd;
112 if ((dgramfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
113 perror("dgram socket");
114 exit(1);
117 fds[conns++] = dgramfd;
119 local.sin_family = AF_INET;
120 local.sin_port = htons(local_port);
121 local.sin_addr.s_addr = INADDR_ANY;
122 memset(local.sin_zero, 0, sizeof local.sin_zero);
124 if (bind(listenfd, (struct sockaddr *) &local, sin_size) == -1) {
125 perror("bind");
126 exit(1);
129 if (bind(dgramfd, (struct sockaddr *) &local, sin_size) == -1) {
130 perror("dgram bind");
131 exit(1);
134 if (listen(listenfd, BACKLOG) == -1) {
135 perror("listen");
136 exit(1);
139 for (;;) {
140 int fd, max_fd, i, j, n;
141 fd_set readfds;
142 char buf[4096];
144 FD_ZERO(&readfds);
145 for (i = 0, max_fd = -1; i < conns; i++) {
146 if (fds[i] > max_fd)
147 max_fd = fds[i];
148 FD_SET(fds[i], &readfds);
151 if (select(max_fd+1, &readfds, NULL, NULL, NULL) == -1) {
152 perror("select");
153 exit(1);
156 if (FD_ISSET(listenfd, &readfds)) {
157 fd = accept(listenfd, (struct sockaddr *) &addrs[conns], &sin_size);
159 if (fd == -1) {
160 perror("accept");
161 continue;
162 } else {
163 printf("[%s] accepted connection from %s (fd: %d)\n",
164 timestamp(), inet_ntoa(addrs[conns].sin_addr), fd);
165 fds[conns++] = fd;
169 for (i = 1; i < conns; i++) {
170 if (FD_ISSET(fds[i], &readfds)) {
171 sin_size = sizeof(struct sockaddr_in);
172 n = recvfrom(fds[i], buf, (sizeof buf)-1, 0,
173 (struct sockaddr *) &addrs[i], &sin_size);
174 if (n == -1) {
175 perror("recvfrom");
176 goto error;
179 // n == 0? huh hmm..
180 if (n == 0) goto error;
182 printf("[%s] got %d bytes from %s (%s) ", timestamp(),
183 n, inet_ntoa(addrs[i].sin_addr),
184 i == 1 ? "udp" : "tcp");
186 for (j = 0; j < n; j++) {
187 if (j % 75 == 0)
188 printf("\n ");
190 if (isprint(buf[j]))
191 putchar(buf[j]);
192 else
193 printf("[0x%x]", buf[j]);
195 //putchar(isprint(buf[j]) ? buf[j] : '?');
198 if (sendallto(fds[i], buf, n, (struct sockaddr *) &addrs[i],
199 sizeof(struct sockaddr_in)) == -1) {
200 perror("\nsendto");
201 goto error;
204 printf("\n (echoed)\n");
205 continue;
207 error:
208 if (i == 1) continue;
209 printf("[%s] removing fd %d from set\n", timestamp(), fds[i]);
210 close(fds[i]);
211 for (j = i+1; j < conns; j++) {
212 fds[j-1] = fds[j];
213 addrs[j-1] = addrs[j];
215 conns--;
220 return 0;