|
| 1 | +//! filter.py(TARGET_TRIPLE): wasm32-wasip2 |
| 2 | +//! add-flags.py(RUN): --wasi=inherit-network=y |
| 3 | +#include <errno.h> |
| 4 | +#include <fcntl.h> |
| 5 | +#include <stdio.h> |
| 6 | +#include <stdlib.h> |
| 7 | +#include <stdbool.h> |
| 8 | +#include <string.h> |
| 9 | +#include <unistd.h> |
| 10 | +#include <netdb.h> |
| 11 | +#include <sys/socket.h> |
| 12 | +#include <netinet/in.h> |
| 13 | +#include <arpa/inet.h> |
| 14 | +#include <poll.h> |
| 15 | +#include "test.h" |
| 16 | + |
| 17 | +#define TEST(c) do { \ |
| 18 | + errno = 0; \ |
| 19 | + if (!(c)) \ |
| 20 | + t_error("%s failed (errno = %d)\n", #c, errno); \ |
| 21 | +} while(0) |
| 22 | + |
| 23 | +// #define _DEBUG 1 |
| 24 | + |
| 25 | +#ifdef _DEBUG |
| 26 | +#define DEBUG_PRINT(...) fprintf( stderr, __VA_ARGS__ ); fflush(stdout) |
| 27 | +#else |
| 28 | +#define DEBUG_PRINT(...) do{ } while ( 0 ) |
| 29 | +#endif |
| 30 | + |
| 31 | +#define BUFSIZE 256 |
| 32 | +int MAX_CONNECTIONS = 10; |
| 33 | + |
| 34 | +struct response { |
| 35 | + bool responded; |
| 36 | + char message[BUFSIZE]; |
| 37 | + int32_t client_index; |
| 38 | +}; |
| 39 | + |
| 40 | +struct request { |
| 41 | + bool sent; |
| 42 | + char message[BUFSIZE]; |
| 43 | +}; |
| 44 | + |
| 45 | +// We are done if client_incoming == client_outgoing |
| 46 | +bool done(struct request out[MAX_CONNECTIONS], char in[MAX_CONNECTIONS][BUFSIZE]) { |
| 47 | + for (size_t i = 0; i < MAX_CONNECTIONS; i++) { |
| 48 | + if (out[i].message[0] == 0) { |
| 49 | + return false; |
| 50 | + } |
| 51 | + if (strcmp(out[i].message, in[i]) != 0) { |
| 52 | + DEBUG_PRINT("sent[%zu] = %s != received[%zu] = %s\n", i, out[i].message, i, in[i]); |
| 53 | + return false; |
| 54 | + } |
| 55 | + DEBUG_PRINT("i = %zu %s matches %s\n", i, out[i].message, in[i]); |
| 56 | + } |
| 57 | + return true; |
| 58 | +} |
| 59 | + |
| 60 | +void test_udp_client() { |
| 61 | + // Prepare server socket |
| 62 | + int server_port = 4001; |
| 63 | + // Use non-blocking sockets |
| 64 | + int server_socket_fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); |
| 65 | + |
| 66 | + // Bind server to socket |
| 67 | + struct sockaddr_in server_address; |
| 68 | + server_address.sin_family = AF_INET; |
| 69 | + server_address.sin_addr.s_addr = htonl(INADDR_ANY); |
| 70 | + server_address.sin_port = htons(server_port); |
| 71 | + TEST(bind(server_socket_fd, (struct sockaddr*)&server_address, sizeof(server_address)) != -1); |
| 72 | + |
| 73 | + // Prepare client sockets |
| 74 | + // Use non-blocking sockets |
| 75 | + int client_sockets[MAX_CONNECTIONS]; |
| 76 | + for (size_t i = 0; i < MAX_CONNECTIONS; i++) { |
| 77 | + client_sockets[i] = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); |
| 78 | + TEST(client_sockets[i] != -1); |
| 79 | + } |
| 80 | + |
| 81 | + // Prepare sockaddr_in for client |
| 82 | + struct sockaddr_in sockaddr_in; |
| 83 | + sockaddr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| 84 | + sockaddr_in.sin_family = AF_INET; |
| 85 | + sockaddr_in.sin_port = htons(server_port); |
| 86 | + |
| 87 | + // Prepare requests from clients |
| 88 | + struct request client_outgoing[MAX_CONNECTIONS]; |
| 89 | + for (size_t i = 0; i < MAX_CONNECTIONS; i++) { |
| 90 | + int32_t number = 0; |
| 91 | + getentropy(&number, sizeof(int32_t)); |
| 92 | + sprintf(client_outgoing[i].message, "%d", number); |
| 93 | + client_outgoing[i].sent = false; |
| 94 | + } |
| 95 | + char client_incoming[MAX_CONNECTIONS][BUFSIZE]; |
| 96 | + for (size_t i = 0; i < MAX_CONNECTIONS; i++) { |
| 97 | + client_incoming[i][0] = 0; |
| 98 | + } |
| 99 | + struct response server_incoming[MAX_CONNECTIONS]; |
| 100 | + for (size_t i = 0; i < MAX_CONNECTIONS; i++) { |
| 101 | + server_incoming[i].responded = false; |
| 102 | + server_incoming[i].message[0] = 0; |
| 103 | + } |
| 104 | + struct pollfd client_pollfds[MAX_CONNECTIONS]; |
| 105 | + for (size_t i = 0; i < MAX_CONNECTIONS; i++) { |
| 106 | + client_pollfds[i].fd = client_sockets[i]; |
| 107 | + client_pollfds[i].events = POLLRDNORM; |
| 108 | + client_pollfds[i].revents = 0; |
| 109 | + } |
| 110 | + |
| 111 | + struct sockaddr_in addresses_of_clients[MAX_CONNECTIONS]; |
| 112 | + socklen_t address_len = sizeof(sockaddr_in); |
| 113 | + |
| 114 | + struct pollfd server_pollfd = { .fd = server_socket_fd, .events = POLLRDNORM, .revents = 0 }; |
| 115 | + struct pollfd server_client_pollfds[MAX_CONNECTIONS]; |
| 116 | + size_t next_client = 0; |
| 117 | + size_t server_client_index = 0; |
| 118 | + int32_t tries = 0; |
| 119 | + while (!done(client_outgoing, client_incoming) && (tries < (MAX_CONNECTIONS * 2))) { |
| 120 | + DEBUG_PRINT("server polling for new messages\n"); |
| 121 | + |
| 122 | + // Server polls for new messages |
| 123 | + server_pollfd.events = POLLRDNORM; |
| 124 | + poll(&server_pollfd, 1, 100); |
| 125 | + if ((server_pollfd.revents & POLLRDNORM) != 0) { |
| 126 | + int bytes_received = recvfrom(server_socket_fd, server_incoming[server_client_index].message, |
| 127 | + BUFSIZE, 0, (struct sockaddr*)&(addresses_of_clients[server_client_index]), &address_len); |
| 128 | + if (bytes_received > 0) { |
| 129 | + server_incoming[server_client_index].client_index = server_client_index; |
| 130 | + server_client_index++; |
| 131 | + } else |
| 132 | + DEBUG_PRINT("Shouldn't happen: poll(server_pollfd) said there is data but recvfrom() failed to return it\n"); |
| 133 | + } |
| 134 | + |
| 135 | + DEBUG_PRINT("next client sending\n"); |
| 136 | + // Next client sends |
| 137 | + if (next_client < MAX_CONNECTIONS) { |
| 138 | + struct pollfd client_pollfd = { .fd = client_sockets[next_client], .events = POLLWRNORM, .revents = 0 }; |
| 139 | + poll(&client_pollfd, 1, 100); |
| 140 | + if ((client_pollfd.events & POLLWRNORM) != 0) { |
| 141 | + int32_t bytes_sent = sendto(client_sockets[next_client], client_outgoing[next_client].message, |
| 142 | + strlen(client_outgoing[next_client].message) + 1, 0, (struct sockaddr*)&sockaddr_in, address_len); |
| 143 | + if (bytes_sent == -1) { |
| 144 | + DEBUG_PRINT("client %zu failed to send, error: %s\n", next_client, strerror(errno)); |
| 145 | + TEST(errno==EWOULDBLOCK); |
| 146 | + // Will retry on next iteration of loop |
| 147 | + } else { |
| 148 | + DEBUG_PRINT("client %zu sent %d bytes\n", next_client, bytes_sent); |
| 149 | + client_outgoing[next_client].sent = true; |
| 150 | + if (next_client < MAX_CONNECTIONS) |
| 151 | + next_client++; |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + DEBUG_PRINT("server echoing\n"); |
| 157 | + // Echo messages back to clients |
| 158 | + server_pollfd.events = POLLWRNORM; |
| 159 | + poll(&server_pollfd, 1, 100); |
| 160 | + for (size_t i = 0; i < server_client_index; i++) { |
| 161 | + if (server_incoming[i].message[0] != 0 && !server_incoming[i].responded) { |
| 162 | + ssize_t bytes_sent = sendto(server_socket_fd, server_incoming[i].message, BUFSIZE, 0, (struct sockaddr*)&addresses_of_clients[i], address_len); |
| 163 | + if (bytes_sent == -1) { |
| 164 | + int the_error = errno; |
| 165 | + TEST(the_error==EWOULDBLOCK); |
| 166 | + // Will retry on next iteration of loop |
| 167 | + } else { |
| 168 | + DEBUG_PRINT("server echoed message %s back to client %zu\n", server_incoming[i].message, i); |
| 169 | + server_incoming[i].responded = true; |
| 170 | + } |
| 171 | + break; |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + DEBUG_PRINT("clients receiving\n"); |
| 176 | + // Clients receive messages |
| 177 | + poll(client_pollfds, MAX_CONNECTIONS, 100); |
| 178 | + DEBUG_PRINT("======= client_pollfds: =========\n"); |
| 179 | + for (size_t i = 0; i < MAX_CONNECTIONS; i++) { |
| 180 | + DEBUG_PRINT("{ .fd = %d .events = %d .revents = %d }\n", client_pollfds[i].fd, client_pollfds[i].events, client_pollfds[i].revents); |
| 181 | + } |
| 182 | + DEBUG_PRINT("=================================\n"); |
| 183 | + for (size_t i = 0; i < MAX_CONNECTIONS; i++) { |
| 184 | + // Note: poll() will return revents == events for an unbound UDP socket; |
| 185 | + // so that's why we check if we already sent on this socket, and do |
| 186 | + // nothing if we haven't yet |
| 187 | + if ((client_pollfds[i].revents & POLLRDNORM) != 0) { |
| 188 | + DEBUG_PRINT("i = %zu, already received (%s), already sent? %s\n", i, client_incoming[i], client_outgoing[i].sent ? "yes" : "no"); |
| 189 | + if (client_incoming[i][0] == 0 && client_outgoing[i].sent) { |
| 190 | + int32_t bytes_received = recvfrom(client_pollfds[i].fd, &client_incoming[i], BUFSIZE, 0, (struct sockaddr*)&server_address, &address_len); |
| 191 | + if (bytes_received != -1) { |
| 192 | + DEBUG_PRINT("client %zu received message %s (%d bytes) from server\n", i, client_incoming[i], bytes_received); |
| 193 | + } else { |
| 194 | + DEBUG_PRINT("Shouldn't happen: poll() said there was data, but client %zu did not receive message from server: %s\n", i, strerror(errno)); |
| 195 | + } |
| 196 | + } |
| 197 | + } |
| 198 | + } |
| 199 | + tries++; |
| 200 | + } |
| 201 | + |
| 202 | + DEBUG_PRINT("========= exited loop ==========\n"); |
| 203 | + |
| 204 | + TEST(done(client_outgoing, client_incoming)); |
| 205 | + |
| 206 | + // Shut down clients |
| 207 | + for (size_t i = 0; i < MAX_CONNECTIONS; i++) |
| 208 | + close(client_sockets[i]); |
| 209 | + |
| 210 | + // Shut down server |
| 211 | + close(server_socket_fd); |
| 212 | +} |
| 213 | + |
| 214 | +int main(void) |
| 215 | +{ |
| 216 | + test_udp_client(); |
| 217 | + |
| 218 | + return t_status; |
| 219 | +} |
0 commit comments