diff --git a/.gitignore b/.gitignore index cb2d2aff..182ecedc 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ product-mini/platforms/linux-sgx/enclave-sample/iwasm build_out tests/wamr-test-suites/workspace -!/test-tools/wamr-ide/VSCode-Extension/.vscode \ No newline at end of file +!/test-tools/wamr-ide/VSCode-Extension/.vscode + +samples/socket-api/wasm-src/inc/pthread.h diff --git a/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h index 6f4d5d42..15ec0f51 100644 --- a/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h +++ b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h @@ -80,6 +80,12 @@ connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int listen(int sockfd, int backlog); +ssize_t +recvmsg(int sockfd, struct msghdr *msg, int flags); + +ssize_t +sendmsg(int sockfd, const struct msghdr *msg, int flags); + int socket(int domain, int type, int protocol); #endif diff --git a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c index 9f6bae93..f5236fcf 100644 --- a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c +++ b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -126,6 +127,10 @@ connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) __wasi_addr_t wasi_addr = { 0 }; __wasi_errno_t error; + if (NULL == addr) { + HANDLE_ERROR(__WASI_ERRNO_INVAL) + } + error = sockaddr_to_wasi_addr(addr, addrlen, &wasi_addr); HANDLE_ERROR(error) @@ -143,6 +148,79 @@ listen(int sockfd, int backlog) return __WASI_ERRNO_SUCCESS; } +ssize_t +recvmsg(int sockfd, struct msghdr *msg, int flags) +{ + // Prepare input parameters. + __wasi_iovec_t *ri_data = NULL; + size_t i = 0; + size_t ro_datalen; + __wasi_roflags_t ro_flags; + + if (NULL == msg) { + HANDLE_ERROR(__WASI_ERRNO_INVAL) + } + + // Validate flags. + if (flags != 0) { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + // __wasi_ciovec_t -> struct iovec + if (!(ri_data = malloc(sizeof(__wasi_iovec_t) * msg->msg_iovlen))) { + HANDLE_ERROR(__WASI_ERRNO_NOMEM) + } + + for (i = 0; i < msg->msg_iovlen; i++) { + ri_data[i].buf = msg->msg_iov[i].iov_base; + ri_data[i].buf_len = msg->msg_iov[i].iov_len; + } + + // Perform system call. + __wasi_errno_t error = __wasi_sock_recv(sockfd, ri_data, msg->msg_iovlen, 0, + &ro_datalen, &ro_flags); + free(ri_data); + HANDLE_ERROR(error) + + return ro_datalen; +} + +ssize_t +sendmsg(int sockfd, const struct msghdr *msg, int flags) +{ + // Prepare input parameters. + __wasi_ciovec_t *si_data = NULL; + size_t so_datalen = 0; + size_t i = 0; + + if (NULL == msg) { + HANDLE_ERROR(__WASI_ERRNO_INVAL) + } + + // This implementation does not support any flags. + if (flags != 0) { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + // struct iovec -> __wasi_ciovec_t + if (!(si_data = malloc(sizeof(__wasi_ciovec_t) * msg->msg_iovlen))) { + HANDLE_ERROR(__WASI_ERRNO_NOMEM) + } + + for (i = 0; i < msg->msg_iovlen; i++) { + si_data[i].buf = msg->msg_iov[i].iov_base; + si_data[i].buf_len = msg->msg_iov[i].iov_len; + } + + // Perform system call. + __wasi_errno_t error = + __wasi_sock_send(sockfd, si_data, msg->msg_iovlen, 0, &so_datalen); + free(si_data); + HANDLE_ERROR(error) + + return so_datalen; +} + int socket(int domain, int type, int protocol) { diff --git a/samples/socket-api/CMakeLists.txt b/samples/socket-api/CMakeLists.txt index 16cafdb4..12b97951 100644 --- a/samples/socket-api/CMakeLists.txt +++ b/samples/socket-api/CMakeLists.txt @@ -89,14 +89,17 @@ ExternalProject_Add(wasm-app INSTALL_COMMAND ${CMAKE_COMMAND} -E copy tcp_client.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build tcp_server.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build - tcp_client.wasm.dump ${CMAKE_CURRENT_SOURCE_DIR}/build - tcp_server.wasm.dump ${CMAKE_CURRENT_SOURCE_DIR}/build + send_recv.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build ) add_executable(tcp_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_server.c) target_link_libraries(tcp_server pthread) + add_executable(tcp_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_client.c) +add_executable(send_recv ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/send_recv.c) +target_link_libraries(send_recv pthread) + ############################################ ## Build iwasm with wasi and pthread support ############################################ diff --git a/samples/socket-api/README.md b/samples/socket-api/README.md index 320f4a6a..71f62ca8 100644 --- a/samples/socket-api/README.md +++ b/samples/socket-api/README.md @@ -1,5 +1,4 @@ -"socket-api" sample introduction -================================ +# "socket-api" sample introduction This sample demonstrates how to use WAMR socket-api to develop wasm network applications. Two wasm applications are provided: tcp-server and tcp-client, and this sample demonstrates @@ -19,26 +18,30 @@ cmake .. make ``` -The file `tcp_server.wasm`, `tcp_client.wasm` and `iwasm` will be created. -And also file `tcp_server` and `tcp_client` of native version are created. +`iwasm` and three Wasm modules, `tcp_server.wasm`, `tcp_client.wasm`, `send_recv.wasm` +will be generated. And their corresponding native version, `tcp_server`, +`tcp_client`, `send_recv` are generated too. -Note that iwasm is built with libc-wasi and lib-pthread enabled. +> Note that iwasm is built with libc-wasi and lib-pthread enabled. ## Run workload Start the tcp server, which opens port 1234 and waits for clients to connect. + ```bash cd build ./iwasm --addr-pool=0.0.0.0/15 tcp_server.wasm ``` Start the tcp client, which connects the server and receives message. + ```bash cd build ./iwasm --addr-pool=127.0.0.1/15 tcp_client.wasm ``` The output of client is like: + ```bash [Client] Create socket [Client] Connect socket @@ -54,4 +57,29 @@ Say Hi from the Server [Client] BYE ``` +`send_recv.wasm` contains a thread as a server and a thread as a client. They +send and receive data via 127.0.0.1:1234. + +```bash +$ ./iwasm --addr-pool=127.0.0.1/0 ./send_recv.wasm +``` + +The output is: + +```bash +Server is online ... +Client is running... +Start receiving. +Start sending. +Send 106 bytes successfully! +Receive 106 bytes successlly! +Data: + The stars shine down + It brings us light + Light comes down + To make us paths + It watches us + And mourns for us +``` + Refer to [socket api document](../../doc/socket_api.md) for more details. diff --git a/samples/socket-api/wasm-src/CMakeLists.txt b/samples/socket-api/wasm-src/CMakeLists.txt index b83c8f88..5c0ed7e2 100644 --- a/samples/socket-api/wasm-src/CMakeLists.txt +++ b/samples/socket-api/wasm-src/CMakeLists.txt @@ -78,3 +78,4 @@ endfunction() compile_with_clang(tcp_server.c) compile_with_clang(tcp_client.c) +compile_with_clang(send_recv.c) diff --git a/samples/socket-api/wasm-src/send_recv.c b/samples/socket-api/wasm-src/send_recv.c new file mode 100644 index 00000000..9ac0aad3 --- /dev/null +++ b/samples/socket-api/wasm-src/send_recv.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +static pthread_mutex_t lock = { 0 }; +static pthread_cond_t cond = { 0 }; +static bool server_is_ready = false; + +void * +run_as_server(void *arg) +{ + int sock = -1, on = 1; + struct sockaddr_in addr = { 0 }; + int addrlen = 0; + int new_sock = -1; + char *buf[] = { + "The stars shine down", "It brings us light", "Light comes down", + "To make us paths", "It watches us", "And mourns for us", + }; + struct iovec iov[] = { + { .iov_base = buf[0], .iov_len = strlen(buf[0]) + 1 }, + { .iov_base = buf[1], .iov_len = strlen(buf[1]) + 1 }, + { .iov_base = buf[2], .iov_len = strlen(buf[2]) + 1 }, + { .iov_base = buf[3], .iov_len = strlen(buf[3]) + 1 }, + { .iov_base = buf[4], .iov_len = strlen(buf[4]) + 1 }, + { .iov_base = buf[5], .iov_len = strlen(buf[5]) + 1 }, + }; + struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 6 }; + ssize_t send_len = 0; + + pthread_mutex_lock(&lock); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("Create a socket failed"); + goto RETURN; + } + +#ifndef __wasi__ + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) { + perror("Setsockopt failed"); + goto RETURN; + } +#endif + + /* 0.0.0.0:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + addrlen = sizeof(addr); + if (bind(sock, (struct sockaddr *)&addr, addrlen) < 0) { + perror("Bind failed"); + goto UNLOCK_SHUTDOWN; + } + + if (listen(sock, 0) < 0) { + perror("Listen failed"); + goto UNLOCK_SHUTDOWN; + } + + server_is_ready = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + + printf("Server is online ... \n"); + + new_sock = accept(sock, (struct sockaddr *)&addr, (socklen_t *)&addrlen); + if (new_sock < 0) { + perror("Accept failed"); + goto SHUTDOWN; + } + + printf("Start sending. \n"); + send_len = sendmsg(new_sock, &msg, 0); + if (send_len < 0) { + perror("Sendmsg failed"); + goto SHUTDOWN; + } + printf("Send %ld bytes successfully!\n", send_len); + +SHUTDOWN: + shutdown(sock, SHUT_RD); + return NULL; + +UNLOCK_SHUTDOWN: + shutdown(sock, SHUT_RD); +RETURN: + pthread_mutex_unlock(&lock); + return NULL; +} + +void * +run_as_client(void *arg) +{ + int sock = -1; + struct sockaddr_in addr = { 0 }; + char buf[256] = { 0 }; + struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; + struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 }; + ssize_t recv_len = 0; + + pthread_mutex_lock(&lock); + while (false == server_is_ready) { + pthread_cond_wait(&cond, &lock); + } + pthread_mutex_unlock(&lock); + + printf("Client is running...\n"); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("Create a socket failed"); + goto RETURN; + } + + /* 127.0.0.1:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Connect failed"); + goto UNLOCK_SHUTDOWN; + } + + printf("Start receiving. \n"); + recv_len = recvmsg(sock, &msg, 0); + if (recv_len < 0) { + perror("Recvmsg failed"); + goto SHUTDOWN; + } + + printf("Receive %ld bytes successlly!\n", recv_len); + printf("Data:\n"); + + uint8_t i = 0; + char *s = msg.msg_iov->iov_base; + for (i = 0; i < 6; i++) { + printf(" %s\n", s); + s += strlen(s) + 1; + } + +SHUTDOWN: + shutdown(sock, SHUT_RD); + return NULL; + +UNLOCK_SHUTDOWN: + shutdown(sock, SHUT_RD); +RETURN: + pthread_mutex_unlock(&lock); + return NULL; +} + +int +main(int argc, char *argv[]) +{ + pthread_t cs[2] = { 0 }; + uint8_t i = 0; + int ret = EXIT_SUCCESS; + + if (pthread_mutex_init(&lock, NULL)) { + perror("Initialize mutex failed"); + ret = EXIT_FAILURE; + goto RETURN; + } + + if (pthread_cond_init(&cond, NULL)) { + perror("Initialize condition failed"); + ret = EXIT_FAILURE; + goto DESTROY_MUTEX; + } + + if (pthread_create(&cs[0], NULL, run_as_server, NULL)) { + perror("Create a server thread failed"); + ret = EXIT_FAILURE; + goto DESTROY_COND; + } + + if (pthread_create(&cs[1], NULL, run_as_client, NULL)) { + perror("Create a client thread failed"); + ret = EXIT_FAILURE; + goto DESTROY_COND; + } + + for (i = 0; i < 2; i++) { + pthread_join(cs[i], NULL); + } + +DESTROY_COND: + pthread_cond_destroy(&cond); +DESTROY_MUTEX: + pthread_mutex_destroy(&lock); +RETURN: + return ret; +}