1/** 2 * \file 3 * \brief Simple Server to test Barrelfish's implementation of and conformance 4 * to the BSD/POSIX sockets API. 5 * 6 * The server waits until it receives BUFFER_SIZE characters from the client 7 * and then echos these back to the client, waits again, ... 8 * 9 * The code it written so that it compiles on Barrelfish as well as on a 10 * POSIX system. 11 */ 12 13/* 14 * Copyright (c) 2012, ETH Zurich. 15 * All rights reserved. 16 * 17 * This file is distributed under the terms in the attached LICENSE file. 18 * If you do not find this file, copies can be found by writing to: 19 * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich, 20 * Attn: Systems Group. 21 */ 22 23#include <assert.h> 24#include <inttypes.h> 25#include <stdbool.h> 26#include <stdio.h> 27#include <stdlib.h> 28#include <string.h> 29#include <unistd.h> 30 31#include <arpa/inet.h> 32#include <sys/select.h> 33#include <sys/socket.h> 34#include <sys/types.h> 35 36#ifdef BARRELFISH 37# include <barrelfish/barrelfish.h> 38# include <lwip/tcpip.h> 39#endif /* BARRELFISH */ 40 41#define DEFAULT_PORT 4242 42#define BACKLOG 10 43#define BUFFER_SIZE 32 44 45#ifndef MAX 46#define MAX(a,b) (((a)>(b))?(a):(b)) 47#endif 48#ifndef MIN 49#define MIN(a,b) (((a)<(b))?(a):(b)) 50#endif 51 52enum client_state { 53 READING, 54 WRITING, 55 FINISHED 56}; 57 58struct client { 59 int socket; 60 char buffer[BUFFER_SIZE + 1]; 61 int buffer_index; 62 enum client_state state; 63 struct client *next; 64}; 65 66struct client *clients = NULL; 67 68#ifdef BARRELFISH 69extern void network_polling_loop(void); 70 71static int poll_loop(void *args) 72{ 73 network_polling_loop(); 74 75 // should never be reached 76 return EXIT_FAILURE; 77} 78#endif /* BARRELFISH */ 79 80static void start_server(uint16_t port, int *serversocket) 81{ 82 int ret = 0; 83 int listenfd = 0; 84 struct sockaddr_in listen_addr; 85 86 listenfd = socket(AF_INET, SOCK_STREAM, 0); 87 if (listenfd < 0) { 88 perror("Failed to open socket"); 89 exit(EXIT_FAILURE); 90 } 91 92 memset(&listen_addr, 0, sizeof(struct sockaddr_in)); 93 listen_addr.sin_family = AF_INET; 94 listen_addr.sin_port = htons(port); 95 listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); 96 97 ret = bind(listenfd, (struct sockaddr *)&listen_addr, 98 sizeof(struct sockaddr_in)); 99 if (ret < 0) { 100 perror("Failed to bind address to socket"); 101 exit(EXIT_FAILURE); 102 } 103 104 ret = listen(listenfd, BACKLOG); 105 if (ret < 0) { 106 perror("Failed to put socket into listening mode"); 107 exit(EXIT_FAILURE); 108 } 109 110 *serversocket = listenfd; 111} 112 113static void handle_new_client(int serversocket) 114{ 115 struct client *client; 116 int clientfd = 0; 117 struct sockaddr_in client_addr; 118 socklen_t client_addrlen = sizeof(struct sockaddr_in); 119 120 clientfd = accept(serversocket, (struct sockaddr *) &client_addr, 121 &client_addrlen); 122 if (clientfd < 0) { 123 perror("Failed to accept new client."); 124 exit(EXIT_FAILURE); 125 } 126 127 /* allocate client struct */ 128 client = calloc(1, sizeof(struct client)); 129 assert(client != NULL); 130 131 client->socket = clientfd; 132 client->buffer_index = 0; 133 client->state = READING; 134 135 /* insert new client at head of the list */ 136 if (clients == NULL) { 137 client->next = NULL; 138 clients = client; 139 } else { 140 client->next = clients; 141 clients = client; 142 } 143} 144 145static void handle_client_read(struct client *client) 146{ 147 assert(client != NULL); 148 assert(client->state == READING); 149 150 ssize_t ret = 0; 151 152 ret = read(client->socket, &client->buffer[client->buffer_index], 153 BUFFER_SIZE - client->buffer_index); 154 assert(read > 0); 155 156 client->buffer_index += ret; 157 158 if (client->buffer_index == BUFFER_SIZE) { 159 client->buffer[BUFFER_SIZE] = '\0'; 160 client->state = WRITING; 161 client->buffer_index = 0; 162 } 163} 164 165static void handle_client_write(struct client *client) 166{ 167 assert(client != NULL); 168 assert(client->state == WRITING); 169 170 ssize_t ret = 0; 171 172 ret = write(client->socket, &client->buffer[client->buffer_index], 173 (BUFFER_SIZE + 1) - client->buffer_index); 174 assert(write > 0); 175 176 client->buffer_index +=ret; 177 178 if (client->buffer_index == BUFFER_SIZE + 1) { 179 client->state = READING; 180 client->buffer_index = 0; 181 memset(client->buffer, '\0', BUFFER_SIZE + 1); 182 } 183} 184 185static void server_accept_loop(int serversocket) 186{ 187 int ret = 0; 188 struct client *client; 189 fd_set readfds; 190 fd_set writefds; 191 int maxfd = 0; 192 193 while (true) { 194 195 /* build select sets */ 196 FD_ZERO(&readfds); 197 FD_ZERO(&writefds); 198 199 FD_SET(serversocket, &readfds); 200 maxfd = MAX(maxfd, serversocket); 201 202 for (client = clients; client != NULL; client = client->next) { 203 if (client->state == READING) { 204 FD_SET(client->socket, &readfds); 205 } else if (client->state == WRITING) { 206 FD_SET(client->socket, &writefds); 207 } 208 maxfd = MAX(maxfd, client->socket); 209 } 210 ret = select(maxfd + 1, &readfds, &writefds, NULL, NULL); 211 assert(ret > 0); 212 213 /* new client */ 214 if (FD_ISSET(serversocket, &readfds)) { 215 handle_new_client(serversocket); 216 } 217 218 /* process clients */ 219 for (client = clients; client != NULL; client = client->next) { 220 if (client->state == READING || client->state == WRITING) { 221 if (FD_ISSET(client->socket, &readfds)) { 222 handle_client_read(client); 223 } 224 if (FD_ISSET(client->socket, &writefds)) { 225 handle_client_write(client); 226 } 227 } else { 228 /* TODO remove finished clients from linked list. */ 229 } 230 } 231 } 232} 233 234int main(int argc, char *argv[]) 235{ 236 uint16_t port = 0; 237 int serversocket = 0; 238#ifdef BARRELFISH 239 struct thread *t; 240#endif /* BARRELFISH */ 241 242 // Parse command line arguments 243 for (int i = 0; i < argc; i++) { 244 if (strncmp(argv[i], "port=", strlen("port=")) == 0) { 245 port = atoi(argv[i] + strlen("port=")); 246 printf("port=%" PRIu16 "\n", port); 247 } 248 } 249 250 // Set default values 251 if (port == 0) { 252 port = DEFAULT_PORT; 253 } 254 255#ifdef BARRELFISH 256 /* 257 * Start the main lwIP thread, that has exclusive access to the lwip core 258 * functions. Other threads communicate with this thread using message 259 * boxes. 260 * 261 * Note that tcpip_init() calls lwip_init_auto(). 262 */ 263 tcpip_init(NULL, NULL); 264 lwip_socket_init(); 265 266 t = thread_create(poll_loop, NULL); 267#endif /* BARRELFISH */ 268 269 start_server(port, &serversocket); 270 printf("Using server socket: %d\n", serversocket); 271 server_accept_loop(serversocket); 272 273 return EXIT_SUCCESS; 274} 275