| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <netinet/in.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/select.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| int server(const char* service) { |
| int port = atoi(service); |
| printf("listen on port %d\n", port); |
| |
| int s = socket(AF_INET6, SOCK_STREAM, 0); |
| if (s < 0) { |
| printf("socket failed (errno = %d)\n", errno); |
| return -1; |
| } |
| |
| struct sockaddr_in6 addr; |
| memset(&addr, 0, sizeof(addr)); |
| addr.sin6_family = AF_INET6; |
| addr.sin6_addr = in6addr_any; // works with IPv4 |
| addr.sin6_port = htons((uint16_t)port); |
| |
| if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) { |
| printf("bind failed (errno = %d)\n", errno); |
| close(s); |
| return -1; |
| } |
| |
| if (listen(s, 1) < 0) { |
| printf("listen failed\n"); |
| close(s); |
| return -1; |
| } |
| |
| fd_set active_readfds, active_writefds, active_exceptfds; |
| FD_ZERO(&active_readfds); |
| FD_ZERO(&active_writefds); |
| FD_ZERO(&active_exceptfds); |
| |
| FD_SET(s, &active_readfds); |
| int nwatch = s + 1; |
| |
| int ret = 0; |
| for (;;) { |
| fd_set readfds = active_readfds; |
| fd_set writefds = active_writefds; |
| fd_set exceptfds = active_exceptfds; |
| |
| int nready = select(nwatch, &readfds, &writefds, &exceptfds, NULL); |
| if (nready < 0) { |
| printf("select failed (errno = %d)\n", errno); |
| ret = -1; |
| goto end; |
| } |
| |
| if (FD_ISSET(s, &readfds)) { |
| --nready; |
| // a connection is waiting |
| socklen_t addrlen = sizeof(addr); |
| int conn = accept(s, (struct sockaddr*)&addr, &addrlen); |
| if (conn < 0) { |
| close(s); |
| printf("accept failed (errno = %d)\n", errno); |
| ret = -1; |
| goto end; |
| } |
| char str[INET6_ADDRSTRLEN]; |
| struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&addr; |
| printf("connected from %s\n", inet_ntop(AF_INET6, &sin6->sin6_addr, str, sizeof(str))); |
| |
| FD_SET(conn, &active_readfds); |
| if (nwatch < (conn + 1)) |
| nwatch = conn + 1; |
| } |
| |
| for (int fd = 0; fd < nwatch; fd++) { |
| if (nready == 0) |
| break; |
| |
| if (fd == s) |
| continue; |
| |
| if (FD_ISSET(fd, &readfds)) { |
| --nready; |
| // data is ready to read |
| char buf[128]; |
| int done = 0; |
| ssize_t nread = read(fd, buf, sizeof(buf)); |
| if (nread == 0) { |
| done = 1; |
| } else if (nread < 0) { |
| printf("read failed on fd %d (%zd)\n", fd, nread); |
| done = 1; |
| } else { |
| int nwrite = 0; |
| while (nwrite < nread) { |
| ssize_t n = write(fd, buf + nwrite, nread - nwrite); |
| if (n < 0) { |
| printf("write failed on fd %d (%zd)\n", fd, n); |
| done = 1; |
| break; |
| } |
| nwrite += n; |
| } |
| } |
| if (done) { |
| close(fd); |
| FD_CLR(fd, &active_readfds); |
| } |
| } |
| } |
| } |
| end: |
| for (int i = 0; i < nwatch; i++) { |
| if (FD_ISSET(i, &active_readfds)) { |
| close(i); |
| } |
| } |
| |
| return ret; |
| } |
| |
| void usage(void) { printf("usage: selecttest port\n"); } |
| |
| int main(int argc, char** argv) { |
| if (argc < 2) { |
| usage(); |
| return -1; |
| } |
| return server(argv[1]); |
| } |