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 */
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>
50 #define DEFAULT_PORT 7
52 #define MAXCONS FD_SETSIZE
55 sendallto(int s
, char *buf
, int len
, struct sockaddr
*to
, socklen_t tolen
)
61 int n
= sendto(s
, buf
+sent
, left
, 0, to
, tolen
);
62 if (n
== -1) { return -1; }
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
);
80 int main(int argc
, char *argv
[])
83 struct sockaddr_in addrs
[MAXCONS
];
85 int local_port
, listenfd
, dgramfd
;
86 struct sockaddr_in local
;
87 socklen_t sin_size
= sizeof(struct sockaddr_in
);
90 case 1: local_port
= DEFAULT_PORT
; break;
91 case 2: local_port
= strtol(argv
[1], NULL
, 10); break;
93 fprintf(stderr
, "Usage: %s [port number]\n", argv
[0]);
94 fprintf(stderr
, "The default port is: %d.\n", DEFAULT_PORT
);
98 if (local_port
<= 0 || local_port
> 65535) {
99 fprintf(stderr
, "Error: invalid port number.\n");
103 printf("[%s] listening on port %d\n", timestamp(), local_port
);
105 if ((listenfd
= socket(AF_INET
, SOCK_STREAM
, 0)) == -1) {
110 fds
[conns
++] = listenfd
;
112 if ((dgramfd
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
113 perror("dgram socket");
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) {
129 if (bind(dgramfd
, (struct sockaddr
*) &local
, sin_size
) == -1) {
130 perror("dgram bind");
134 if (listen(listenfd
, BACKLOG
) == -1) {
140 int fd
, max_fd
, i
, j
, n
;
145 for (i
= 0, max_fd
= -1; i
< conns
; i
++) {
148 FD_SET(fds
[i
], &readfds
);
151 if (select(max_fd
+1, &readfds
, NULL
, NULL
, NULL
) == -1) {
156 if (FD_ISSET(listenfd
, &readfds
)) {
157 fd
= accept(listenfd
, (struct sockaddr
*) &addrs
[conns
], &sin_size
);
163 printf("[%s] accepted connection from %s (fd: %d)\n",
164 timestamp(), inet_ntoa(addrs
[conns
].sin_addr
), 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
);
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
++) {
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) {
204 printf("\n (echoed)\n");
208 if (i
== 1) continue;
209 printf("[%s] removing fd %d from set\n", timestamp(), fds
[i
]);
211 for (j
= i
+1; j
< conns
; j
++) {
213 addrs
[j
-1] = addrs
[j
];