| |
| |
| |
| |
| |
| |
|
|
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| #include <assert.h> |
| #ifdef __EMSCRIPTEN__ |
| #include <emscripten.h> |
| #endif |
|
|
| #include "test_sockets_msg.h" |
|
|
| |
| #ifndef MESSAGE |
| #define MESSAGE "pingtothepong" |
| #endif |
|
|
| typedef enum { |
| MSG_CONNECT, |
| MSG_READ, |
| MSG_WRITE |
| } msg_state_t; |
|
|
| typedef struct { |
| int fd; |
| msg_t msg; |
| msg_state_t state; |
| } server_t; |
|
|
| server_t server; |
| msg_t echo_msg; |
| int echo_read; |
| int echo_wrote; |
|
|
| void finish(int result) { |
| printf("finish: %d\n", result); |
| if (server.fd) { |
| close(server.fd); |
| server.fd = 0; |
| } |
| #ifdef __EMSCRIPTEN__ |
| #if TEST_ASYNC |
| emscripten_force_exit(result); |
| #else |
| emscripten_cancel_main_loop(); |
| #endif |
| #endif |
| exit(result); |
| } |
|
|
| void main_loop() { |
| static char out[1024*2]; |
| static int pos = 0; |
| fd_set fdr; |
| fd_set fdw; |
| int res; |
| |
| struct timeval tv = {0}; |
|
|
| |
| FD_ZERO(&fdr); |
| FD_ZERO(&fdw); |
| FD_SET(server.fd, &fdr); |
| FD_SET(server.fd, &fdw); |
| res = select(64, &fdr, &fdw, NULL, &tv); |
| if (res == -1) { |
| perror("select failed"); |
| finish(EXIT_FAILURE); |
| } |
|
|
| if (server.state == MSG_READ) { |
| if (!FD_ISSET(server.fd, &fdr)) { |
| return; |
| } |
|
|
| #if !TEST_DGRAM |
| |
| |
| int available; |
| res = ioctl(server.fd, FIONREAD, &available); |
| assert(res != -1); |
| assert(available); |
| #endif |
|
|
| res = do_msg_read(server.fd, &server.msg, echo_read, 0, NULL, NULL); |
| if (res == -1) { |
| return; |
| } else if (res == 0) { |
| perror("server closed"); |
| finish(EXIT_FAILURE); |
| } |
|
|
| echo_read += res; |
|
|
| |
| if (echo_read >= server.msg.length) { |
| assert(!strcmp(server.msg.buffer, MESSAGE)); |
| finish(EXIT_SUCCESS); |
| } |
| } |
|
|
| if (server.state == MSG_CONNECT) { |
| if (!FD_ISSET(server.fd, &fdw)) { |
| return; |
| } |
| int error = 0; |
| socklen_t len = sizeof(error); |
| int ret = getsockopt(server.fd, SOL_SOCKET, SO_ERROR, &error, &len); |
| if (error != 0) { |
| printf("connect failed: %s\n", strerror(error)); |
| finish(EXIT_FAILURE); |
| } |
| printf("connected\n"); |
| server.state = MSG_WRITE; |
| } |
|
|
| if (server.state == MSG_WRITE) { |
| if (!FD_ISSET(server.fd, &fdw)) { |
| return; |
| } |
|
|
| res = do_msg_write(server.fd, &echo_msg, echo_wrote, 0, NULL, 0); |
| if (res == -1) { |
| return; |
| } else if (res == 0) { |
| perror("server closed"); |
| finish(EXIT_FAILURE); |
| } |
|
|
| echo_wrote += res; |
|
|
| |
| if (echo_wrote >= echo_msg.length) { |
| server.state = MSG_READ; |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| void async_main_loop(int fd, void* userData) { |
| printf("%s callback\n", (char*)userData); |
| main_loop(); |
| } |
|
|
| void error_callback(int fd, int err, const char* msg, void* userData) { |
| int error; |
| socklen_t len = sizeof(error); |
|
|
| int ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); |
| printf("%s callback\n", (char*)userData); |
| printf("error message: %s\n", msg); |
|
|
| if (err == error) { |
| finish(EXIT_SUCCESS); |
| } else { |
| finish(EXIT_FAILURE); |
| } |
| } |
|
|
| int main() { |
| struct sockaddr_in addr; |
| int res; |
|
|
| memset(&server, 0, sizeof(server_t)); |
| server.state = MSG_CONNECT; |
|
|
| |
| memset(&echo_msg, 0, sizeof(msg_t)); |
| echo_msg.length = strlen(MESSAGE) + 1; |
| echo_msg.buffer = malloc(echo_msg.length); |
| strncpy(echo_msg.buffer, MESSAGE, echo_msg.length); |
|
|
| echo_read = 0; |
| echo_wrote = 0; |
|
|
| |
| #if !TEST_DGRAM |
| server.fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); |
| #else |
| server.fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); |
| #endif |
| if (server.fd == -1) { |
| perror("cannot create socket"); |
| finish(EXIT_FAILURE); |
| } |
| fcntl(server.fd, F_SETFL, O_NONBLOCK); |
|
|
| |
| memset(&addr, 0, sizeof(addr)); |
| addr.sin_family = AF_INET; |
| addr.sin_port = htons(SOCKK); |
| if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) { |
| perror("inet_pton failed"); |
| finish(EXIT_FAILURE); |
| } |
|
|
| res = connect(server.fd, (struct sockaddr *)&addr, sizeof(addr)); |
| if (res == -1 && errno != EINPROGRESS) { |
| perror("connect failed"); |
| finish(EXIT_FAILURE); |
| } |
|
|
| { |
| int z; |
| struct sockaddr_in adr_inet; |
| socklen_t len_inet = sizeof adr_inet; |
| z = getsockname(server.fd, (struct sockaddr *)&adr_inet, &len_inet); |
| if (z != 0) { |
| perror("getsockname"); |
| finish(EXIT_FAILURE); |
| } |
| char buffer[1000]; |
| sprintf(buffer, "%s:%u", inet_ntoa(adr_inet.sin_addr), (unsigned)ntohs(adr_inet.sin_port)); |
| |
| char *correct = "0.0.0.0:0"; |
| printf("got (expected) socket: %s (%s), size %lu (%lu)\n", buffer, correct, strlen(buffer), strlen(correct)); |
| assert(strlen(buffer) == strlen(correct)); |
| assert(strcmp(buffer, correct) == 0); |
| } |
|
|
| { |
| int z; |
| struct sockaddr_in adr_inet; |
| socklen_t len_inet = sizeof adr_inet; |
| z = getpeername(server.fd, (struct sockaddr *)&adr_inet, &len_inet); |
| if (z != 0) { |
| perror("getpeername"); |
| finish(EXIT_FAILURE); |
| } |
| char buffer[1000]; |
| sprintf(buffer, "%s:%u", inet_ntoa(adr_inet.sin_addr), (unsigned)ntohs(adr_inet.sin_port)); |
| char correct[1000]; |
| sprintf(correct, "127.0.0.1:%u", SOCKK); |
| printf("got (expected) socket: %s (%s), size %lu (%lu)\n", buffer, correct, strlen(buffer), strlen(correct)); |
| assert(strlen(buffer) == strlen(correct)); |
| assert(strcmp(buffer, correct) == 0); |
| } |
|
|
| #ifdef __EMSCRIPTEN__ |
| #if TEST_ASYNC |
| |
| |
| emscripten_set_socket_error_callback("error", error_callback); |
| emscripten_set_socket_open_callback("open", async_main_loop); |
| emscripten_set_socket_message_callback("message", async_main_loop); |
| #else |
| emscripten_set_main_loop(main_loop, 60, 0); |
| #endif |
| #else |
| while (1) main_loop(); |
| #endif |
|
|
| return EXIT_SUCCESS; |
| } |
|
|