├── .travis.yml ├── LICENSE ├── README.md ├── echo-server.c ├── example.c ├── example2.c ├── test └── test.c ├── uv_msg_framing.c ├── uv_msg_framing.h └── uv_send_message.c /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | install: 4 | - git clone https://github.com/libuv/libuv 5 | - cd libuv 6 | - sh autogen.sh 7 | - ./configure 8 | - make 9 | - sudo make install 10 | - sudo ldconfig 11 | - cd .. 12 | 13 | script: 14 | - cd test 15 | - gcc test.c -o test -luv 16 | - ./test 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 litesync 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libuv_message_framing 2 | 3 | [![Build Status](https://travis-ci.org/litesync/libuv_message_framing.svg?branch=master)](https://travis-ci.org/litesync/libuv_message_framing) 4 | 5 | Message-based communication for libuv 6 | 7 | This code implements length-prefixed message framing on top of streams. 8 | 9 | It works with TCP, Unix domain sockets (Linux) and Named Pipes (Windows). 10 | 11 | 12 | ## Usage 13 | 14 | ### Stream Initialization for TCP 15 | 16 | ```C 17 | uv_msg_t* socket = malloc(sizeof(uv_msg_t)); 18 | uv_msg_init(loop, socket, UV_TCP); 19 | ``` 20 | 21 | ### Stream Initialization for Unix domain sockets or Named Pipes 22 | 23 | ```C 24 | uv_msg_t* socket = malloc(sizeof(uv_msg_t)); 25 | uv_msg_init(loop, socket, UV_NAMED_PIPE); 26 | ``` 27 | 28 | ### Sending Messages 29 | 30 | ```C 31 | uv_msg_send((uv_msg_write_t*)req, (uv_msg_t*) socket, msg, size, write_cb); 32 | ``` 33 | 34 | ### Receiving Messages 35 | 36 | ```C 37 | uv_msg_read_start((uv_msg_t*) socket, alloc_cb, msg_read_cb, free_cb); 38 | ``` 39 | 40 | 41 | ## Examples 42 | 43 | By default libuv does not handle memory management for requests. The above functions 44 | were implemented using the same principle so you are free to use the memory management 45 | you want with them. 46 | 47 | But for ease of use and understanding we included 2 examples that use the system 48 | malloc/free functions. 49 | 50 | The message reading implementation is the same. 51 | 52 | Both examples also have a user function called `send_message`, but implemented in 53 | a different way. 54 | 55 | ### Example 1 56 | 57 | The [example.c](example.c) has a basic `send_message` function that only accepts 58 | dynamically allocated messages. The callback must always be supplied and it must 59 | release the memory used for the message and for the request. 60 | 61 | ```C 62 | send_message(socket, msg, size, on_msg_sent); 63 | ``` 64 | 65 | ### Example 2 66 | 67 | The [example2.c](example2.c) has a more elaborated `send_message` function that 68 | accepts both static, transient and dynamic messages. This concept is 69 | inherited from [SQLite](https://www.sqlite.org/c3ref/c_static.html). 70 | 71 | ```C 72 | send_message(socket, msg, size, free_fn, on_msg_sent, user_data); 73 | ``` 74 | 75 | The `free_fn` argument can accept these 3 values: 76 | 77 | * UV_MSG_STATIC 78 | 79 | Use when the message pointer you pass to the function will be valid until after 80 | the message is sent. 81 | 82 | * UV_MSG_TRANSIENT 83 | 84 | Use when the message memory will be discarded soon, probably before the message 85 | is sent. The function will make a copy of the message. Remember that the 86 | message sending is asynchronous. 87 | 88 | * A pointer to the destructor or free function that must be automatically called 89 | to release the memory associated with the message upon the complete delivery or 90 | failure. 91 | 92 | The callback and user data arguments are optional. Examples: 93 | 94 | Sending a static message with no callback: 95 | 96 | ```C 97 | msg = "Hello Mom!"; 98 | send_message(socket, msg, strlen(msg)+1, UV_MSG_STATIC, 0, 0); 99 | ``` 100 | 101 | Sending a dynamically allocated message with a notification callback function: 102 | 103 | ```C 104 | msg = strdup("Hello Dad!"); 105 | send_message(socket, msg, strlen(msg)+1, free, on_msg_sent, extra_data); 106 | ``` 107 | 108 | 109 | ## Compiling 110 | 111 | ### On Linux 112 | 113 | Using TCP: 114 | 115 | ``` 116 | gcc echo-server.c -o echo-server -luv 117 | gcc example.c -o example -luv 118 | gcc example2.c -o example2 -luv 119 | ``` 120 | 121 | Using unix domain sockets: 122 | 123 | ``` 124 | gcc echo-server.c -o echo-server -luv -DUSE_PIPE_EXAMPLE 125 | gcc example.c -o example -luv -DUSE_PIPE_EXAMPLE 126 | gcc example2.c -o example2 -luv -DUSE_PIPE_EXAMPLE 127 | ``` 128 | 129 | ### On Windows 130 | 131 | Using TCP: 132 | 133 | ``` 134 | gcc echo-server.c -o echo-server -llibuv -lws2_32 135 | gcc example.c -o example -llibuv -lws2_32 136 | gcc example2.c -o example2 -llibuv -lws2_32 137 | ``` 138 | 139 | Using named pipes: 140 | 141 | ``` 142 | gcc echo-server.c -o echo-server -llibuv -lws2_32 -DUSE_PIPE_EXAMPLE 143 | gcc example.c -o example -llibuv -lws2_32 -DUSE_PIPE_EXAMPLE 144 | gcc example2.c -o example2 -llibuv -lws2_32 -DUSE_PIPE_EXAMPLE 145 | ``` 146 | 147 | 148 | ## Testing 149 | 150 | ### On Linux 151 | 152 | cd test 153 | gcc test.c -o test -luv 154 | LD_LIBRARY_PATH=/usr/local/lib ./test 155 | 156 | # or with valgrind: 157 | LD_LIBRARY_PATH=/usr/local/lib valgrind --leak-check=full --show-reachable=yes ./test 158 | 159 | ### On Windows 160 | 161 | cd test 162 | gcc test.c -o test -llibuv -lws2_32 163 | test 164 | 165 | 166 | ## Compatibility 167 | 168 | This code is compatible with implementations in other languages that encode the length in big endian. 169 | 170 | Some examples: 171 | 172 | * Node.js [frame-stream](https://github.com/davedoesdev/frame-stream) 173 | * Python [struct](https://gist.github.com/kroggen/70fba39c6198391195bcbcc22c2dcd94) 174 | 175 | 176 | ## TO DO 177 | 178 | * (maybe) use `uv_buf_t bufs[]` instead of `void *msg` on uv_msg_send() 179 | 180 | -------------------------------------------------------------------------------- /echo-server.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** xxxxxxxxx 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "uv_msg_framing.c" 9 | #include "uv_send_message.c" 10 | 11 | #define DEFAULT_PORT 7000 12 | 13 | #ifdef _WIN32 14 | # define PIPENAME "\\\\?\\pipe\\some.name" 15 | #elif defined (__android__) 16 | # define PIPENAME "/data/local/tmp/some.name" 17 | #else 18 | # define PIPENAME "/tmp/some.name" 19 | #endif 20 | 21 | /****************************************************************************/ 22 | 23 | void on_close(uv_handle_t *handle) { 24 | if (handle->type == UV_TCP || handle->type == UV_NAMED_PIPE) { 25 | free(handle); 26 | } 27 | } 28 | 29 | void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 30 | buf->base = (char*) malloc(suggested_size); 31 | buf->len = suggested_size; 32 | } 33 | 34 | void free_buffer(uv_handle_t* handle, void* ptr) { 35 | free(ptr); 36 | } 37 | 38 | void on_msg_sent(send_message_t *req, int status) { 39 | 40 | if ( status < 0 ) { 41 | printf("message send failed: %s user_data: %d\n", (char*)req->msg, (int)req->data); 42 | } else { 43 | printf("message sent: %s user_data: %d\n", (char*)req->msg, (int)req->data); 44 | } 45 | 46 | } 47 | 48 | void on_msg_received(uv_msg_t *client, void *msg, int size) { 49 | 50 | if (size < 0) { 51 | if (size != UV_EOF) { 52 | fprintf(stderr, "Read error: %s\n", uv_err_name(size)); 53 | } 54 | uv_close((uv_handle_t*) client, NULL); 55 | return; 56 | } 57 | 58 | printf("new message received (%d bytes): %s\n", size, (char*)msg); 59 | 60 | send_message(client, msg, size, UV_MSG_TRANSIENT, on_msg_sent, (void*)124); 61 | 62 | } 63 | 64 | void on_new_connection(uv_stream_t *server, int status) { 65 | 66 | if (status < 0) { 67 | fprintf(stderr, "New connection error %s\n", uv_strerror(status)); 68 | return; 69 | } 70 | 71 | uv_msg_t *client = malloc(sizeof(uv_msg_t)); 72 | 73 | #ifdef USE_PIPE_EXAMPLE 74 | uv_msg_init(server->loop, client, UV_NAMED_PIPE); 75 | #else 76 | uv_msg_init(server->loop, client, UV_TCP); 77 | #endif 78 | 79 | if (uv_accept(server, (uv_stream_t*) client) == 0) { 80 | /* new client connected! start reading messages on this stream (asynchronously) */ 81 | uv_msg_read_start(client, alloc_buffer, on_msg_received, free_buffer); 82 | } else { 83 | uv_close((uv_handle_t*) client, on_close); 84 | } 85 | 86 | } 87 | 88 | #ifdef USE_PIPE_EXAMPLE 89 | 90 | int main() { 91 | int rc; 92 | uv_loop_t *loop = uv_default_loop(); 93 | 94 | uv_msg_t* socket = malloc(sizeof(uv_msg_t)); 95 | rc = uv_msg_init(loop, socket, UV_NAMED_PIPE); 96 | 97 | rc = uv_pipe_bind((uv_pipe_t*)socket, PIPENAME); 98 | if (rc) { 99 | fprintf(stderr, "Bind error %s\n", uv_strerror(rc)); 100 | return 1; 101 | } 102 | 103 | rc = uv_listen((uv_stream_t*) socket, 16, on_new_connection); 104 | if (rc) { 105 | fprintf(stderr, "Listen error %s\n", uv_strerror(rc)); 106 | return 1; 107 | } 108 | 109 | return uv_run(loop, UV_RUN_DEFAULT); 110 | } 111 | 112 | #else 113 | 114 | int main() { 115 | int rc; 116 | uv_loop_t *loop = uv_default_loop(); 117 | 118 | uv_msg_t* socket = malloc(sizeof(uv_msg_t)); 119 | rc = uv_msg_init(loop, socket, UV_TCP); 120 | 121 | struct sockaddr_in addr; 122 | uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr); 123 | 124 | rc = uv_tcp_bind((uv_tcp_t*)socket, (const struct sockaddr*)&addr, 0); 125 | if (rc) { 126 | fprintf(stderr, "Bind error %s\n", uv_strerror(rc)); 127 | return 1; 128 | } 129 | 130 | rc = uv_listen((uv_stream_t*) socket, 16, on_new_connection); 131 | if (rc) { 132 | fprintf(stderr, "Listen error %s\n", uv_strerror(rc)); 133 | return 1; 134 | } 135 | 136 | return uv_run(loop, UV_RUN_DEFAULT); 137 | } 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** This example code must be run with the echo-server running. 3 | ** It is in this same folder. 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "uv_msg_framing.c" 10 | 11 | #define DEFAULT_PORT 7000 12 | 13 | #ifdef _WIN32 14 | # define PIPENAME "\\\\?\\pipe\\some.name" 15 | #elif defined (__android__) 16 | # define PIPENAME "/data/local/tmp/some.name" 17 | #else 18 | # define PIPENAME "/tmp/some.name" 19 | #endif 20 | 21 | /****************************************************************************/ 22 | 23 | void send_message(uv_msg_t* socket, char *msg, int size, uv_write_cb write_cb) { 24 | uv_msg_send_t *req = malloc(sizeof(uv_msg_send_t)); 25 | 26 | /* save the data pointer to release on completion */ 27 | req->data = msg; 28 | 29 | uv_msg_send(req, socket, msg, size, write_cb); 30 | } 31 | 32 | void on_msg_sent(uv_write_t *req, int status) { 33 | 34 | if ( status < 0 ) { 35 | puts("message write failed"); 36 | } else { 37 | puts("message sent"); 38 | } 39 | 40 | /* release the message data */ 41 | free(req->data); 42 | 43 | /* release the write request */ 44 | free(req); 45 | } 46 | 47 | /* buffer allocation and release. used with the message reading */ 48 | 49 | void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 50 | buf->base = (char*) malloc(suggested_size); 51 | buf->len = suggested_size; 52 | } 53 | 54 | void free_buffer(uv_handle_t* handle, void* ptr) { 55 | free(ptr); 56 | } 57 | 58 | void on_msg_received(uv_msg_t *client, void *msg, int size) { 59 | 60 | if (size < 0) { 61 | if (size != UV_EOF) { 62 | fprintf(stderr, "Read error: %s\n", uv_err_name(size)); 63 | } 64 | uv_close((uv_handle_t*) client, NULL); 65 | return; 66 | } 67 | 68 | printf("new message received (%d bytes): %s\n", size, (char*)msg); 69 | 70 | if (strcmp(msg, "Is it working?") == 0) { 71 | char *response = strdup("Yeaaah!"); 72 | send_message(client, response, strlen(response)+1, on_msg_sent); 73 | } 74 | 75 | } 76 | 77 | void on_connect(uv_connect_t *connect, int status) { 78 | uv_msg_t* socket = (uv_msg_t*) connect->handle; 79 | char *msg; 80 | 81 | free(connect); 82 | 83 | if (status < 0) { 84 | fprintf(stderr, "Connection error: %s\n", uv_strerror(status)); 85 | return; 86 | } 87 | 88 | uv_msg_read_start(socket, alloc_buffer, on_msg_received, free_buffer); 89 | 90 | msg = strdup("Hello World!"); 91 | send_message(socket, msg, strlen(msg)+1, on_msg_sent); 92 | 93 | msg = strdup("Is it working?"); 94 | send_message(socket, msg, strlen(msg)+1, on_msg_sent); 95 | 96 | } 97 | 98 | #ifdef USE_PIPE_EXAMPLE 99 | 100 | int main() { 101 | int rc; 102 | uv_loop_t *loop = uv_default_loop(); 103 | 104 | uv_msg_t* socket = malloc(sizeof(uv_msg_t)); 105 | rc = uv_msg_init(loop, socket, UV_NAMED_PIPE); 106 | 107 | uv_connect_t* connect = malloc(sizeof(uv_connect_t)); 108 | uv_pipe_connect(connect, (uv_pipe_t*)socket, PIPENAME, on_connect); 109 | 110 | return uv_run(loop, UV_RUN_DEFAULT); 111 | } 112 | 113 | #else 114 | 115 | int main() { 116 | int rc; 117 | uv_loop_t *loop = uv_default_loop(); 118 | 119 | uv_msg_t* socket = malloc(sizeof(uv_msg_t)); 120 | rc = uv_msg_init(loop, socket, UV_TCP); 121 | 122 | struct sockaddr_in dest; 123 | uv_ip4_addr("127.0.0.1", DEFAULT_PORT, &dest); 124 | 125 | uv_connect_t* connect = malloc(sizeof(uv_connect_t)); 126 | rc = uv_tcp_connect(connect, (uv_tcp_t*)socket, (const struct sockaddr*)&dest, on_connect); 127 | if (rc) { 128 | fprintf(stderr, "Connect error: %s\n", uv_strerror(rc)); 129 | return 1; 130 | } 131 | 132 | return uv_run(loop, UV_RUN_DEFAULT); 133 | } 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /example2.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** This example code must be run with the echo-server running. 3 | ** It is in this same folder. 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "uv_msg_framing.c" 10 | #include "uv_send_message.c" 11 | 12 | #define DEFAULT_PORT 7000 13 | 14 | #ifdef _WIN32 15 | # define PIPENAME "\\\\?\\pipe\\some.name" 16 | #elif defined (__android__) 17 | # define PIPENAME "/data/local/tmp/some.name" 18 | #else 19 | # define PIPENAME "/tmp/some.name" 20 | #endif 21 | 22 | /****************************************************************************/ 23 | 24 | void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 25 | buf->base = (char*) malloc(suggested_size); 26 | buf->len = suggested_size; 27 | } 28 | 29 | void free_buffer(uv_handle_t* handle, void* ptr) { 30 | free(ptr); 31 | } 32 | 33 | void on_msg_sent(send_message_t *req, int status) { 34 | 35 | if ( status < 0 ) { 36 | printf("message send failed: %s user_data: %d\n", (char*)req->msg, (int)req->data); 37 | } else { 38 | printf("message sent: %s user_data: %d\n", (char*)req->msg, (int)req->data); 39 | } 40 | 41 | } 42 | 43 | void on_msg_received(uv_msg_t *client, void *msg, int size) { 44 | 45 | if (size < 0) { 46 | if (size != UV_EOF) { 47 | fprintf(stderr, "Read error: %s\n", uv_err_name(size)); 48 | } 49 | uv_close((uv_handle_t*) client, NULL); 50 | return; 51 | } 52 | 53 | printf("new message received (%d bytes): %s\n", size, (char*)msg); 54 | 55 | if (strcmp(msg, "Is it working?") == 0) { 56 | char *response = "Yeaaah!"; 57 | send_message(client, response, strlen(response)+1, UV_MSG_STATIC, on_msg_sent, 0); 58 | } 59 | 60 | } 61 | 62 | void on_connect(uv_connect_t *connect, int status) { 63 | uv_msg_t* socket = (uv_msg_t*) connect->handle; 64 | char *msg; 65 | 66 | free(connect); 67 | 68 | if (status < 0) { 69 | fprintf(stderr, "Connection error: %s\n", uv_strerror(status)); 70 | return; 71 | } 72 | 73 | /* we are connected! start the reading messages on this stream (asynchronously) */ 74 | 75 | uv_msg_read_start(socket, alloc_buffer, on_msg_received, free_buffer); 76 | 77 | /* now send some messages */ 78 | 79 | msg = "Hello Mom!"; 80 | send_message(socket, msg, strlen(msg)+1, UV_MSG_STATIC, 0, 0); 81 | 82 | msg = "Hello Dad!"; 83 | send_message(socket, msg, strlen(msg)+1, UV_MSG_TRANSIENT, 0, 0); 84 | 85 | msg = strdup("Hello World!"); 86 | send_message(socket, msg, strlen(msg)+1, free, 0, 0); 87 | 88 | msg = "Hello Mom!"; 89 | send_message(socket, msg, strlen(msg)+1, UV_MSG_STATIC, on_msg_sent, (void*)123); 90 | 91 | msg = "Hello Dad!"; 92 | send_message(socket, msg, strlen(msg)+1, UV_MSG_TRANSIENT, on_msg_sent, (void*)124); 93 | 94 | msg = strdup("Hello World!"); 95 | send_message(socket, msg, strlen(msg)+1, free, on_msg_sent, (void*)125); 96 | 97 | msg = "Is it working?"; 98 | send_message(socket, msg, strlen(msg)+1, UV_MSG_STATIC, on_msg_sent, (void*)126); 99 | 100 | } 101 | 102 | #ifdef USE_PIPE_EXAMPLE 103 | 104 | int main() { 105 | int rc; 106 | uv_loop_t *loop = uv_default_loop(); 107 | 108 | uv_msg_t* socket = malloc(sizeof(uv_msg_t)); 109 | rc = uv_msg_init(loop, socket, UV_NAMED_PIPE); 110 | 111 | uv_connect_t* connect = malloc(sizeof(uv_connect_t)); 112 | uv_pipe_connect(connect, (uv_pipe_t*)socket, PIPENAME, on_connect); 113 | 114 | return uv_run(loop, UV_RUN_DEFAULT); 115 | } 116 | 117 | #else 118 | 119 | int main() { 120 | int rc; 121 | uv_loop_t *loop = uv_default_loop(); 122 | 123 | uv_msg_t* socket = malloc(sizeof(uv_msg_t)); 124 | rc = uv_msg_init(loop, socket, UV_TCP); 125 | 126 | struct sockaddr_in dest; 127 | uv_ip4_addr("127.0.0.1", DEFAULT_PORT, &dest); 128 | 129 | uv_connect_t* connect = malloc(sizeof(uv_connect_t)); 130 | rc = uv_tcp_connect(connect, (uv_tcp_t*)socket, (const struct sockaddr*)&dest, on_connect); 131 | if (rc) { 132 | fprintf(stderr, "Connect error: %s\n", uv_strerror(rc)); 133 | return 1; 134 | } 135 | 136 | return uv_run(loop, UV_RUN_DEFAULT); 137 | } 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | uv_loop_t *server_loop; 8 | uv_loop_t *client_loop; 9 | 10 | uv_async_t async_next_step; 11 | uv_timer_t timer; 12 | 13 | uv_thread_t reader_thread; 14 | uv_async_t stop_reader; 15 | uv_barrier_t barrier; 16 | 17 | #define CONNECTION_PORT 7357 18 | #define DEFAULT_BACKLOG 16 19 | #define DEFAULT_UV_SUGGESTED_SIZE 65536 20 | 21 | int expected_suggested_size; 22 | int expected_suggested_size2; 23 | int next_alloc_size; 24 | int next_alloc_size2; 25 | int alloc_called; 26 | int recvd_called; 27 | int free_called; 28 | int next_msg_letter; 29 | int waiting_reader; 30 | 31 | void print_bytes(char *oper, unsigned char *data, int size) { 32 | int i; 33 | printf("%s %d bytes:\n", oper, size); 34 | for(i=0; i < size; i++){ 35 | printf("%02x ", data[i]); 36 | } 37 | puts(""); 38 | } 39 | 40 | #define TESTING_UV_MSG_FRAMING 41 | #include "../uv_msg_framing.c" 42 | 43 | /* Common ********************************************************************/ 44 | 45 | void on_close(uv_handle_t *handle) { 46 | if (handle->type == UV_TCP) { 47 | free(handle); 48 | } 49 | } 50 | 51 | void on_walk(uv_handle_t *handle, void *arg) { 52 | uv_close(handle, on_close); 53 | } 54 | 55 | /* Reader Thread *************************************************************/ 56 | 57 | void check_msg(char *base, int size, char letter); 58 | 59 | void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 60 | printf("alloc_buffer called. suggested_size=%d expected=%d allocated=%d\n", suggested_size, expected_suggested_size, next_alloc_size); 61 | assert(suggested_size == expected_suggested_size); 62 | buf->base = (char*) malloc(next_alloc_size); 63 | buf->len = next_alloc_size; 64 | alloc_called++; 65 | // load the values for the next call 66 | expected_suggested_size = expected_suggested_size2; 67 | next_alloc_size = next_alloc_size2; 68 | } 69 | 70 | void free_buffer(uv_handle_t* handle, void* ptr) { 71 | printf("free_buffer called\n"); 72 | free(ptr); 73 | free_called++; 74 | } 75 | 76 | void on_msg_received(uv_msg_t *client, void *msg, int size) { 77 | 78 | printf("msg_received called. size=%d\n", size); 79 | 80 | if (size < 0) { 81 | if (size != UV_EOF) { 82 | fprintf(stderr, "Read error: %s\n", uv_err_name(size)); 83 | } 84 | uv_close((uv_handle_t*) client, on_close); 85 | return; 86 | } 87 | 88 | printf("new message here (%d bytes): %s\n", size, (char*)msg); 89 | 90 | check_msg(msg, size, next_msg_letter); 91 | next_msg_letter++; 92 | if( next_msg_letter == 'D' ) next_msg_letter = 'A'; 93 | 94 | recvd_called++; 95 | 96 | } 97 | 98 | void on_new_connection(uv_stream_t *server, int status) { 99 | if (status < 0) { 100 | fprintf(stderr, "New connection error %s\n", uv_strerror(status)); 101 | return; 102 | } 103 | 104 | uv_msg_t *client = malloc(sizeof(uv_msg_t)); 105 | uv_msg_init(server_loop, client, UV_TCP); 106 | 107 | if (uv_accept(server, (uv_stream_t*) client) == 0) { 108 | uv_msg_read_start(client, alloc_buffer, on_msg_received, free_buffer); 109 | } else { 110 | uv_close((uv_handle_t*) client, on_close); 111 | } 112 | } 113 | 114 | void stop_reader_cb(uv_async_t *handle) { 115 | uv_stop(server_loop); 116 | } 117 | 118 | void reader_start(void *arg) { 119 | 120 | server_loop = malloc(sizeof(uv_loop_t)); 121 | uv_loop_init(server_loop); 122 | 123 | uv_async_init(server_loop, &stop_reader, stop_reader_cb); 124 | 125 | uv_tcp_t *server = malloc(sizeof(uv_tcp_t)); 126 | uv_tcp_init(server_loop, server); 127 | 128 | struct sockaddr_in addr; 129 | uv_ip4_addr("0.0.0.0", CONNECTION_PORT, &addr); 130 | 131 | uv_tcp_bind(server, (const struct sockaddr*)&addr, 0); 132 | 133 | int r = uv_listen((uv_stream_t*) server, DEFAULT_BACKLOG, on_new_connection); 134 | if (r) { 135 | fprintf(stderr, "Listen error %s\n", uv_strerror(r)); 136 | return; 137 | } 138 | 139 | /* signal to the main thread the the listening socket is ready */ 140 | uv_barrier_wait(&barrier); 141 | 142 | uv_run(server_loop, UV_RUN_DEFAULT); 143 | 144 | /* cleanup */ 145 | puts("cleaning up reader thread"); 146 | uv_walk(server_loop, on_walk, NULL); 147 | uv_run(server_loop, UV_RUN_DEFAULT); 148 | uv_loop_close(server_loop); 149 | free(server_loop); 150 | } 151 | 152 | /* Writer ********************************************************************/ 153 | 154 | uv_msg_t *sendersocket; 155 | 156 | typedef struct { 157 | uv_write_t req; 158 | uv_buf_t buf; 159 | } write_req_t; 160 | 161 | void on_write_complete(uv_write_t *req, int status) { 162 | if ( status < 0 ) { 163 | assert(0 && "message write failed"); 164 | } 165 | free(req); 166 | } 167 | 168 | void send_data(uv_stream_t* stream, char *data, int size) { 169 | write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t)); 170 | req->buf = uv_buf_init(data, size); 171 | print_bytes("sending", data, size); 172 | uv_write((uv_write_t*) req, stream, &req->buf, 1, on_write_complete); 173 | } 174 | 175 | void on_connect(uv_connect_t *connect, int status) { 176 | uv_stream_t* socket; 177 | 178 | if (status < 0) { 179 | fprintf(stderr, "New connection error %s\n", uv_strerror(status)); 180 | return; 181 | } 182 | 183 | socket = connect->handle; 184 | puts("client connected"); 185 | free(connect); 186 | uv_stop(client_loop); 187 | } 188 | 189 | /*****************************************************************************/ 190 | 191 | void go_to_next_step(uv_async_t *handle) { 192 | 193 | if (waiting_reader != 1) { 194 | puts("next step ---------------------------------------------------------- AGAIN --"); 195 | return; 196 | } 197 | waiting_reader = 0; 198 | 199 | puts("next step ----------------------------------------------------------------------"); 200 | 201 | /* exit from the first loop run */ 202 | uv_stop(client_loop); 203 | 204 | } 205 | 206 | void timer_cb() { 207 | 208 | /* exit from the second loop run */ 209 | uv_stop(client_loop); 210 | 211 | } 212 | 213 | void wait_reader() { 214 | 215 | waiting_reader = 1; 216 | 217 | uv_run(client_loop, UV_RUN_DEFAULT); 218 | 219 | uv_timer_start(&timer, timer_cb, 50, 0); 220 | 221 | uv_run(client_loop, UV_RUN_DEFAULT); 222 | 223 | } 224 | 225 | /* Main **********************************************************************/ 226 | 227 | void cleanup() { 228 | 229 | /* cleanup */ 230 | puts("cleaning up main thread"); 231 | uv_async_send(&stop_reader); 232 | uv_walk(client_loop, on_walk, NULL); 233 | uv_run(client_loop, UV_RUN_DEFAULT); 234 | uv_loop_close(client_loop); 235 | 236 | uv_thread_join(&reader_thread); 237 | uv_barrier_destroy(&barrier); 238 | puts("done."); 239 | 240 | } 241 | 242 | int main() { 243 | int rc; 244 | 245 | uv_barrier_init(&barrier, 2); 246 | 247 | uv_thread_create(&reader_thread, reader_start, NULL); 248 | 249 | /* wait until the listening socket is ready on the reader thread */ 250 | uv_barrier_wait(&barrier); 251 | 252 | client_loop = uv_default_loop(); 253 | 254 | sendersocket = malloc(sizeof(uv_msg_t)); 255 | uv_msg_init(client_loop, sendersocket, UV_TCP); 256 | 257 | uv_connect_t* connect = malloc(sizeof(uv_connect_t)); 258 | 259 | struct sockaddr_in dest; 260 | uv_ip4_addr("127.0.0.1", CONNECTION_PORT, &dest); 261 | 262 | rc = uv_tcp_connect(connect, (uv_tcp_t*)sendersocket, (const struct sockaddr*)&dest, on_connect); 263 | if (rc) { 264 | fprintf(stderr, "Connect error %s\n", uv_strerror(rc)); 265 | return 1; 266 | } 267 | 268 | uv_async_init(client_loop, &async_next_step, go_to_next_step); 269 | 270 | uv_timer_init(client_loop, &timer); 271 | 272 | uv_run(client_loop, UV_RUN_DEFAULT); 273 | 274 | run_tests(); 275 | 276 | cleanup(); 277 | 278 | return 0; 279 | 280 | } 281 | 282 | /*****************************************************************************/ 283 | 284 | int get_msg_size(void *msg) { 285 | int *pint = (int*) msg; 286 | assert(pint != 0); 287 | return ntohl(pint[0]); 288 | } 289 | 290 | void check_msg(char *base, int size, char letter) { 291 | int i; 292 | 293 | puts("checking message content"); 294 | 295 | for(i=0; i < size-1; i++){ 296 | assert(base[i] == letter); 297 | i++; 298 | if( i < size-1 ){ 299 | assert(base[i] == i % 256); 300 | } 301 | } 302 | 303 | assert(base[size-1] == 0); 304 | 305 | } 306 | 307 | void create_test_msg(void *base, int size, char letter) { 308 | int *pint, i; 309 | char *ptr; 310 | 311 | pint = (int*) base; 312 | assert(pint != 0); 313 | pint[0] = htonl(size); 314 | 315 | ptr = base; 316 | ptr += 4; 317 | 318 | for(i=0; i < size-1; i++){ 319 | ptr[i] = letter; 320 | i++; 321 | if( i < size-1 ){ 322 | ptr[i] = i % 256; 323 | } 324 | } 325 | 326 | ptr[size-1] = 0; /* null terminator */ 327 | 328 | assert(get_msg_size(base) == size); 329 | 330 | } 331 | 332 | void test_coalesced_and_fragmented_messages() { 333 | int msg_size, entire_msg_size, next_chunk_size; 334 | int alloc_call_expected, recvd_call_expected; 335 | char *pmsg, *ptr, *stream_buffer; 336 | 337 | 338 | msg_size = 100; 339 | entire_msg_size = msg_size + 4; 340 | 341 | /* allocate space for 3 messages */ 342 | stream_buffer = malloc(3 * entire_msg_size); 343 | assert(stream_buffer != 0); 344 | 345 | /* write the first message */ 346 | ptr = stream_buffer; 347 | create_test_msg(ptr, msg_size, 'A'); 348 | /* write the second message */ 349 | ptr += entire_msg_size; 350 | create_test_msg(ptr, msg_size, 'B'); 351 | /* write the third message */ 352 | ptr += entire_msg_size; 353 | create_test_msg(ptr, msg_size, 'C'); 354 | 355 | 356 | printf("stream_buffer: %p, get_msg_size: %d, msg_size: %d\n", stream_buffer, get_msg_size(stream_buffer), msg_size); 357 | assert(get_msg_size(stream_buffer) == msg_size); 358 | 359 | /* points to the first message */ 360 | ptr = stream_buffer; 361 | next_msg_letter = 'A'; 362 | 363 | /* start sending the chunks */ 364 | 365 | 366 | /* send 2 bytes of the length */ 367 | next_chunk_size = 2; 368 | alloc_call_expected = 1; 369 | expected_suggested_size = DEFAULT_UV_SUGGESTED_SIZE; 370 | next_alloc_size = 32; 371 | recvd_call_expected = 0; 372 | 373 | alloc_called = 0; recvd_called = 0; free_called = 0; 374 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 375 | ptr += next_chunk_size; 376 | 377 | printf("stream_buffer: %p, get_msg_size: %d, msg_size: %d\n", stream_buffer, get_msg_size(stream_buffer), msg_size); 378 | print_bytes("buffer", stream_buffer, 8); 379 | assert(get_msg_size(stream_buffer) == msg_size); 380 | 381 | wait_reader(); 382 | assert(alloc_called == alloc_call_expected); 383 | assert(recvd_called == recvd_call_expected); 384 | 385 | printf("stream_buffer: %p, get_msg_size: %d, msg_size: %d\n", stream_buffer, get_msg_size(stream_buffer), msg_size); 386 | print_bytes("buffer", stream_buffer, 8); 387 | assert(get_msg_size(stream_buffer) == msg_size); 388 | 389 | 390 | /* send 1 more byte of the length */ 391 | next_chunk_size = 1; 392 | alloc_call_expected = 0; // allocation callback call not expected 393 | recvd_call_expected = 0; 394 | 395 | alloc_called = 0; recvd_called = 0; free_called = 0; 396 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 397 | ptr += next_chunk_size; 398 | wait_reader(); 399 | assert(alloc_called == alloc_call_expected); 400 | assert(recvd_called == recvd_call_expected); 401 | 402 | assert(get_msg_size(stream_buffer) == msg_size); 403 | 404 | 405 | /* send more 2 bytes, the last one from the length and the first from the message */ // -- what if there is no sufficient memory here - another test case 406 | next_chunk_size = 2; 407 | alloc_call_expected = 0; // allocation callback call not expected 408 | recvd_call_expected = 0; 409 | 410 | alloc_called = 0; recvd_called = 0; free_called = 0; 411 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 412 | ptr += next_chunk_size; 413 | wait_reader(); 414 | assert(alloc_called == alloc_call_expected); 415 | assert(recvd_called == recvd_call_expected); 416 | 417 | assert(get_msg_size(stream_buffer) == msg_size); 418 | 419 | 420 | /* send more 9 bytes from the message */ 421 | next_chunk_size = 9; 422 | alloc_call_expected = 1; 423 | expected_suggested_size = entire_msg_size; 424 | next_alloc_size = entire_msg_size; 425 | recvd_call_expected = 0; 426 | 427 | alloc_called = 0; recvd_called = 0; free_called = 0; 428 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 429 | ptr += next_chunk_size; 430 | wait_reader(); 431 | assert(alloc_called == alloc_call_expected); 432 | assert(recvd_called == recvd_call_expected); 433 | 434 | 435 | /* send more 80 bytes from the message. total send: 90 bytes */ 436 | next_chunk_size = 80; 437 | alloc_call_expected = 0; 438 | recvd_call_expected = 0; 439 | 440 | alloc_called = 0; recvd_called = 0; free_called = 0; 441 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 442 | ptr += next_chunk_size; 443 | wait_reader(); 444 | assert(alloc_called == alloc_call_expected); 445 | assert(recvd_called == recvd_call_expected); 446 | 447 | 448 | /* send more 64 bytes. the remaining 10 bytes from the first message + 4 bytes length + 50 bytes for the second message */ 449 | next_chunk_size = 64; 450 | alloc_call_expected = 2; //!? only for the second message -- if the second message is bigger, then it should call the alloc callback 451 | expected_suggested_size = DEFAULT_UV_SUGGESTED_SIZE; 452 | next_alloc_size = 30; //! if not enough buffer is supplied then another call may happen - new test case 453 | expected_suggested_size2 = entire_msg_size; 454 | next_alloc_size2 = entire_msg_size; 455 | recvd_call_expected = 1; 456 | 457 | alloc_called = 0; recvd_called = 0; free_called = 0; 458 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 459 | ptr += next_chunk_size; 460 | wait_reader(); 461 | assert(alloc_called == alloc_call_expected); 462 | assert(recvd_called == recvd_call_expected); 463 | 464 | 465 | /* send more 52 bytes. the remaining 50 bytes from the second message + 2 bytes length from the third */ 466 | next_chunk_size = 52; 467 | alloc_call_expected = 0; 468 | expected_suggested_size = DEFAULT_UV_SUGGESTED_SIZE; 469 | next_alloc_size = 4; 470 | recvd_call_expected = 1; 471 | 472 | alloc_called = 0; recvd_called = 0; free_called = 0; 473 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 474 | ptr += next_chunk_size; 475 | wait_reader(); 476 | assert(alloc_called >= alloc_call_expected); 477 | assert(recvd_called == recvd_call_expected); 478 | 479 | 480 | /* send more 12 bytes. the remaining 2 bytes from the length and 10 bytes from the message */ 481 | next_chunk_size = 12; 482 | alloc_call_expected = 1; 483 | expected_suggested_size = entire_msg_size; 484 | next_alloc_size = entire_msg_size; 485 | recvd_call_expected = 0; 486 | 487 | alloc_called = 0; recvd_called = 0; free_called = 0; 488 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 489 | ptr += next_chunk_size; 490 | wait_reader(); 491 | assert(alloc_called == alloc_call_expected); 492 | assert(recvd_called == recvd_call_expected); 493 | 494 | 495 | /* send more 10 bytes from the message. total sent: 20 bytes */ 496 | next_chunk_size = 10; 497 | alloc_call_expected = 0; 498 | recvd_call_expected = 0; 499 | 500 | alloc_called = 0; recvd_called = 0; free_called = 0; 501 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 502 | ptr += next_chunk_size; 503 | wait_reader(); 504 | assert(alloc_called == alloc_call_expected); 505 | assert(recvd_called == recvd_call_expected); 506 | 507 | 508 | /* send more 10 bytes from the message. total sent: 30 bytes */ 509 | next_chunk_size = 10; 510 | alloc_call_expected = 0; 511 | recvd_call_expected = 0; 512 | 513 | alloc_called = 0; recvd_called = 0; free_called = 0; 514 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 515 | ptr += next_chunk_size; 516 | wait_reader(); 517 | assert(alloc_called == alloc_call_expected); 518 | assert(recvd_called == recvd_call_expected); 519 | 520 | 521 | /* send the remaining 70 bytes from the message */ 522 | next_chunk_size = 70; 523 | alloc_call_expected = 0; 524 | recvd_call_expected = 1; 525 | expected_suggested_size = DEFAULT_UV_SUGGESTED_SIZE; 526 | next_alloc_size = DEFAULT_UV_SUGGESTED_SIZE; 527 | 528 | alloc_called = 0; recvd_called = 0; free_called = 0; 529 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 530 | ptr += next_chunk_size; 531 | wait_reader(); 532 | assert(alloc_called >= alloc_call_expected); 533 | assert(recvd_called == recvd_call_expected); 534 | 535 | 536 | 537 | // test case: sending more than 1 message: 2 messages + 2 bytes (incomplete msg size) 538 | 539 | /* points to the first message */ 540 | ptr = stream_buffer; 541 | 542 | /* send more 12 bytes. the remaining 2 bytes from the length and 10 bytes from the message */ 543 | next_chunk_size = entire_msg_size + entire_msg_size + 2; 544 | alloc_call_expected = 0; 545 | expected_suggested_size = DEFAULT_UV_SUGGESTED_SIZE; 546 | next_alloc_size = 50; 547 | expected_suggested_size2 = entire_msg_size; 548 | next_alloc_size2 = entire_msg_size; 549 | recvd_call_expected = 2; 550 | 551 | alloc_called = 0; recvd_called = 0; free_called = 0; 552 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 553 | ptr += next_chunk_size; 554 | wait_reader(); 555 | assert(alloc_called >= alloc_call_expected); 556 | assert(recvd_called == recvd_call_expected); 557 | 558 | 559 | /* send the remaining bytes from the message */ 560 | next_chunk_size = entire_msg_size - 2; 561 | alloc_call_expected = 0; 562 | recvd_call_expected = 1; 563 | 564 | alloc_called = 0; recvd_called = 0; free_called = 0; 565 | send_data((uv_stream_t*)sendersocket, ptr, next_chunk_size); 566 | ptr += next_chunk_size; 567 | wait_reader(); 568 | assert(alloc_called >= alloc_call_expected); 569 | assert(recvd_called == recvd_call_expected); 570 | 571 | 572 | // test case: send more bytes than allocated 573 | 574 | 575 | free(stream_buffer); 576 | 577 | puts("All tests PASS!"); 578 | 579 | } 580 | 581 | int run_tests() { 582 | 583 | test_coalesced_and_fragmented_messages(); 584 | 585 | } 586 | -------------------------------------------------------------------------------- /uv_msg_framing.c: -------------------------------------------------------------------------------- 1 | #include "uv_msg_framing.h" 2 | 3 | #ifdef DEBUGTRACE 4 | #define UVTRACE(X) printf X; 5 | #else 6 | #define UVTRACE(X) 7 | #endif 8 | 9 | 10 | /* Stream Initialization *****************************************************/ 11 | 12 | int uv_msg_init(uv_loop_t* loop, uv_msg_t* handle, int stream_type) { 13 | int rc; 14 | 15 | switch( stream_type ){ 16 | case UV_TCP: 17 | rc = uv_tcp_init(loop, (uv_tcp_t*) handle); 18 | break; 19 | case UV_NAMED_PIPE: 20 | rc = uv_pipe_init(loop, (uv_pipe_t*) handle, 0); 21 | break; 22 | default: 23 | return UV_EINVAL; 24 | } 25 | 26 | if( rc ) return rc; 27 | 28 | handle->buf = NULL; 29 | handle->alloc_size = 0; 30 | handle->filled = 0; 31 | handle->alloc_cb = NULL; 32 | handle->free_cb = NULL; 33 | handle->msg_read_cb = NULL; 34 | /* initialize the public member */ 35 | handle->data = NULL; 36 | 37 | return 0; 38 | } 39 | 40 | 41 | /* Message Writting **********************************************************/ 42 | 43 | #ifdef _WIN32 44 | static void uv_msg_sent(uv_write_t *req, int status) { 45 | free(req); 46 | } 47 | #endif 48 | 49 | int uv_msg_send(uv_msg_send_t *req, uv_msg_t *socket, void *msg, int size, uv_write_cb write_cb) { 50 | uv_stream_t *stream = (uv_stream_t*) socket; 51 | 52 | if ( !req || !stream || !msg || size <= 0 ) return UV_EINVAL; 53 | 54 | UVTRACE(("sending message: %s\n", (char*)msg)); 55 | 56 | req->msg_size = htonl(size); 57 | req->buf[0].base = (char*) &req->msg_size; 58 | req->buf[0].len = 4; 59 | req->buf[1] = uv_buf_init(msg, size); 60 | 61 | #ifdef _WIN32 62 | /* uv_write does not accept more than 1 buffer with Pipes on Windows 63 | https://github.com/libuv/libuv/issues/794 */ 64 | if (stream->type == UV_NAMED_PIPE) { 65 | int rc; 66 | uv_msg_send_t *req1 = malloc(sizeof(uv_msg_send_t)); 67 | if (!req1) return UV_ENOMEM; 68 | rc = uv_write((uv_write_t*) req1, stream, &req->buf[0], 1, uv_msg_sent); 69 | if (rc) { free(req1); return rc; } 70 | return uv_write((uv_write_t*) req, stream, &req->buf[1], 1, write_cb); 71 | } else 72 | #endif 73 | return uv_write((uv_write_t*) req, stream, &req->buf[0], 2, write_cb); 74 | 75 | } 76 | 77 | 78 | /* Message Reading ***********************************************************/ 79 | 80 | void uv_stream_msg_free_buffer(uv_msg_t *uvmsg) { 81 | if( uvmsg->free_cb ) uvmsg->free_cb((uv_handle_t*)uvmsg, uvmsg->buf); 82 | uvmsg->buf = 0; 83 | uvmsg->alloc_size = 0; 84 | } 85 | 86 | int uv_stream_msg_realloc(uv_handle_t *handle, size_t suggested_size) { 87 | uv_msg_t *uvmsg = (uv_msg_t*) handle; 88 | uv_buf_t buf = {0}; 89 | uvmsg->alloc_cb(handle, suggested_size, &buf); 90 | if( buf.base==0 || buf.len < suggested_size ) return 0; //! if buf.len < suggested_size and buf.base is valid it will be lost here (the allocated memory) 91 | memcpy(buf.base, uvmsg->buf, uvmsg->filled); 92 | if( uvmsg->free_cb ) uvmsg->free_cb(handle, uvmsg->buf); 93 | uvmsg->buf = buf.base; 94 | uvmsg->alloc_size = buf.len; 95 | return 1; 96 | } 97 | 98 | void uv_stream_msg_alloc(uv_handle_t *handle, size_t suggested_size, uv_buf_t *stream_buf) { 99 | uv_msg_t *uvmsg = (uv_msg_t*) handle; 100 | 101 | UVTRACE(("stream_msg_alloc uvmsg=%p\n", uvmsg)); 102 | if( uvmsg==0 ) return; 103 | 104 | if( uvmsg->buf==0 ){ 105 | uv_buf_t buf = {0}; 106 | uvmsg->alloc_cb(handle, suggested_size, &buf); 107 | uvmsg->buf = buf.base; 108 | if( uvmsg->buf==0 ) return; 109 | uvmsg->alloc_size = buf.len; 110 | } 111 | 112 | UVTRACE(("stream_msg_alloc uvmsg->buf=%p filled=%d\n", uvmsg->buf, uvmsg->filled)); 113 | 114 | if( uvmsg->filled >= 4 ){ 115 | int msg_size = ntohl(*(int*)uvmsg->buf); 116 | int entire_msg_size = msg_size + 4; 117 | UVTRACE(("stream_msg_alloc msg_size=%d\n", msg_size)); 118 | if( uvmsg->alloc_size < entire_msg_size ){ 119 | /* here the suggested size is exactly what it's needed to read the entire message */ 120 | if( !uv_stream_msg_realloc(handle, entire_msg_size) ){ 121 | stream_buf->base = 0; 122 | return; 123 | } 124 | } 125 | stream_buf->len = entire_msg_size - uvmsg->filled; 126 | } else { 127 | if( uvmsg->alloc_size < 4 ){ 128 | /* There is no enough space for the message length. Allocate the default size */ 129 | UVTRACE(("calling realloc - alloc_size: %d, filled: %d\n", uvmsg->alloc_size, uvmsg->filled)); 130 | if( !uv_stream_msg_realloc(handle, 64 * 1024) ){ 131 | stream_buf->base = 0; 132 | return; 133 | } 134 | } 135 | stream_buf->len = uvmsg->alloc_size - uvmsg->filled; 136 | } 137 | 138 | stream_buf->base = uvmsg->buf + uvmsg->filled; 139 | UVTRACE(("stream_msg_alloc base=%p len=%d\n", stream_buf->base, stream_buf->len)); 140 | } 141 | 142 | void uv_stream_msg_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { 143 | uv_msg_t *uvmsg = (uv_msg_t*) stream; 144 | char *ptr; 145 | 146 | UVTRACE(("uv_stream_msg_read: received %d bytes\n", nread)); 147 | UVTRACE(("uvmsg: %p uvmsg->buf: %p buf->base: %p\n", uvmsg, uvmsg->buf, buf->base)); 148 | 149 | if (uvmsg == 0) return; 150 | 151 | if (nread == 0) { 152 | /* Nothing read */ 153 | //! does it should release the ->buf here? 154 | //uv_stream_msg_free_buffer(uvmsg); 155 | return; 156 | } 157 | 158 | if (nread < 0) { 159 | /* Error */ 160 | uv_stream_msg_free_buffer(uvmsg); 161 | uvmsg->msg_read_cb((uv_msg_t*)stream, NULL, nread); 162 | return; 163 | } 164 | 165 | #ifdef TESTING_UV_MSG_FRAMING 166 | assert(buf->base == uvmsg->buf + uvmsg->filled); 167 | print_bytes("received", buf->base, nread); 168 | #endif 169 | 170 | uvmsg->filled += nread; 171 | 172 | UVTRACE(("alloc_size: %d, received: %d, filled: %d\n", uvmsg->alloc_size, nread, uvmsg->filled)); 173 | 174 | ptr = uvmsg->buf; 175 | 176 | while( uvmsg->filled >= 4 ){ 177 | int msg_size = ntohl(*(int*)ptr); 178 | int entire_msg = msg_size + 4; 179 | UVTRACE(("msg_size: %d, entire_msg: %d\n", msg_size, entire_msg)); 180 | if( uvmsg->filled >= entire_msg ){ 181 | uvmsg->msg_read_cb((uv_msg_t*)stream, ptr + 4, msg_size); 182 | if( uvmsg->filled > entire_msg ){ 183 | ptr += entire_msg; 184 | } 185 | uvmsg->filled -= entire_msg; 186 | } else { 187 | break; 188 | } 189 | } 190 | 191 | if( ptr > uvmsg->buf && uvmsg->filled > 0 ){ 192 | UVTRACE(("moving the buffer\n")); 193 | memmove(uvmsg->buf, ptr, uvmsg->filled); 194 | } else if( uvmsg->filled == 0 ){ 195 | UVTRACE(("releasing the buffer\n")); 196 | uv_stream_msg_free_buffer(uvmsg); 197 | } 198 | 199 | #ifdef TESTING_UV_MSG_FRAMING 200 | uv_async_send(&async_next_step); 201 | #endif 202 | } 203 | 204 | int uv_msg_read_start(uv_msg_t* stream, uv_alloc_cb alloc_cb, uv_msg_read_cb msg_read_cb, uv_free_cb free_cb) { 205 | 206 | stream->msg_read_cb = msg_read_cb; 207 | stream->alloc_cb = alloc_cb; 208 | stream->free_cb = free_cb; 209 | 210 | return uv_read_start((uv_stream_t*)stream, uv_stream_msg_alloc, uv_stream_msg_read); 211 | 212 | } 213 | -------------------------------------------------------------------------------- /uv_msg_framing.h: -------------------------------------------------------------------------------- 1 | #ifndef UV_MSG_FRAMING_H 2 | #define UV_MSG_FRAMING_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include 8 | 9 | 10 | typedef struct uv_msg_s uv_msg_t; 11 | typedef struct uv_msg_send_s uv_msg_send_t; 12 | 13 | 14 | /* Stream Initialization */ 15 | 16 | int uv_msg_init(uv_loop_t* loop, uv_msg_t* handle, int stream_type); 17 | 18 | 19 | /* Callback Functions */ 20 | 21 | typedef void (*uv_free_cb)(uv_handle_t* handle, void* ptr); 22 | 23 | typedef void (*uv_msg_read_cb)(uv_msg_t* stream, void *msg, int size); 24 | 25 | 26 | /* Functions */ 27 | 28 | int uv_msg_read_start(uv_msg_t* stream, uv_alloc_cb alloc_cb, uv_msg_read_cb msg_read_cb, uv_free_cb free_cb); 29 | 30 | int uv_msg_send(uv_msg_send_t* req, uv_msg_t* stream, void* msg, int size, uv_write_cb write_cb); 31 | 32 | 33 | /* Message Read Structure */ 34 | 35 | struct uv_msg_s { 36 | union { 37 | uv_tcp_t tcp; 38 | uv_pipe_t pipe; 39 | void *data; 40 | }; 41 | char *buf; 42 | int alloc_size; 43 | int filled; 44 | uv_alloc_cb alloc_cb; 45 | uv_free_cb free_cb; 46 | uv_msg_read_cb msg_read_cb; 47 | }; 48 | 49 | 50 | /* Message Write Structure */ 51 | 52 | struct uv_msg_send_s { 53 | union { 54 | uv_write_t req; 55 | void *data; 56 | }; 57 | uv_buf_t buf[2]; 58 | int msg_size; /* in network order! */ 59 | }; 60 | 61 | 62 | #ifdef __cplusplus 63 | } 64 | #endif 65 | #endif // UV_MSG_FRAMING_H 66 | -------------------------------------------------------------------------------- /uv_send_message.c: -------------------------------------------------------------------------------- 1 | /* This module does not use the uv_msg_send_ prefix name because it should be 2 | reserved for libuv, which does not handle memory management for requests. 3 | This module allocates and releases memory used for the requests. */ 4 | 5 | typedef void (*uv_free_fn) (void *ptr); 6 | #define UV_MSG_STATIC ((uv_free_fn)0) 7 | #define UV_MSG_TRANSIENT ((uv_free_fn)-1) 8 | 9 | typedef struct send_message_s send_message_t; 10 | 11 | typedef void (*send_message_cb) (send_message_t *req, int status); 12 | 13 | struct send_message_s { 14 | union { 15 | uv_msg_send_t req; 16 | void *data; 17 | }; 18 | void *msg; 19 | uv_free_fn free_fn; 20 | send_message_cb msg_send_cb; 21 | }; 22 | 23 | /****************************************************************************/ 24 | 25 | static void * memdup(void *source, int size) { 26 | void *ptr; 27 | if (source == NULL || size <= 0) return NULL; 28 | ptr = malloc(size); 29 | if (ptr == 0) return NULL; 30 | memcpy(ptr, source, size); 31 | return ptr; 32 | } 33 | 34 | static void send_message_completed(uv_write_t *wreq, int status) { 35 | send_message_t *req = (send_message_t *) wreq; 36 | 37 | /* call the callback function */ 38 | if (req->msg_send_cb) req->msg_send_cb(req, status); 39 | 40 | /* release the message data */ 41 | if (req->free_fn) req->free_fn(req->msg); 42 | 43 | /* release the write request */ 44 | free(req); 45 | } 46 | 47 | int send_message(uv_msg_t *socket, char *msg, int size, uv_free_fn free_fn, send_message_cb send_cb, void *user_data) { 48 | send_message_t *req = malloc(sizeof(send_message_t)); 49 | 50 | if (!req) return UV_ENOMEM; 51 | 52 | /* check if we need a copy of the message and save the free function pointer */ 53 | if (free_fn == UV_MSG_TRANSIENT) { 54 | msg = memdup(msg, size); 55 | if (!msg) { free(req); return UV_ENOMEM; }; 56 | req->free_fn = free; 57 | } else { 58 | req->free_fn = free_fn; 59 | } 60 | 61 | /* save the message pointer to release on completion */ 62 | req->msg = msg; 63 | 64 | /* save the user data */ 65 | req->data = user_data; 66 | 67 | /* the user callback is optional */ 68 | req->msg_send_cb = send_cb; 69 | 70 | /* send the message */ 71 | return uv_msg_send((uv_msg_send_t*)req, socket, msg, size, send_message_completed); 72 | } 73 | --------------------------------------------------------------------------------