|
| 1 | +#include "AsyncUDP.h" |
| 2 | +#include "Utility.h" |
| 3 | + |
| 4 | +void _asyncudp_async_cb(uv_async_t *handle) { |
| 5 | + AsyncUDP *udp = (AsyncUDP *)handle->data; |
| 6 | + udp->_DO_NOT_CALL_async_cb(); |
| 7 | +} |
| 8 | + |
| 9 | +AsyncUDP::AsyncUDP() { |
| 10 | + _handler = NULL; |
| 11 | + _connected = false; |
| 12 | + uv_loop_init(&_loop); |
| 13 | + _async.data = this; |
| 14 | + uv_async_init(&_loop, &_async, _asyncudp_async_cb); |
| 15 | +} |
| 16 | + |
| 17 | +AsyncUDP::~AsyncUDP() { |
| 18 | + _quit.store(true); |
| 19 | + uv_async_send(&_async); |
| 20 | + _ioThread.join(); |
| 21 | + uv_loop_close(&_loop); |
| 22 | +} |
| 23 | + |
| 24 | +asyncUDPSendTask::asyncUDPSendTask(uint8_t *data, size_t len, IPAddress addr, uint16_t port) { |
| 25 | + this->data = (uint8_t*)malloc(len); |
| 26 | + memcpy(this->data, data, len); |
| 27 | + this->len = len; |
| 28 | + this->addr = addr; |
| 29 | + this->port = port; |
| 30 | +} |
| 31 | + |
| 32 | +void _asyncudp_alloc_buffer_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { |
| 33 | + buf->base = (char *)malloc(suggested_size); |
| 34 | + buf->len = suggested_size; |
| 35 | +} |
| 36 | + |
| 37 | +void _asyncudp_on_read_cb(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) { |
| 38 | + AsyncUDP *udp = (AsyncUDP *)handle->data; |
| 39 | + udp->_DO_NOT_CALL_uv_on_read(handle, nread, buf, addr, flags); |
| 40 | +} |
| 41 | + |
| 42 | +void AsyncUDP::_DO_NOT_CALL_uv_on_read(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) { |
| 43 | + _handlerMutex.lock(); |
| 44 | + auto h = _handler; |
| 45 | + _handlerMutex.unlock(); |
| 46 | + if (h) { |
| 47 | + AsyncUDPPacket packet((uint8_t*)buf->base, nread); |
| 48 | + h(packet); |
| 49 | + } |
| 50 | + free(buf->base); |
| 51 | +} |
| 52 | + |
| 53 | +bool AsyncUDP::listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl) { |
| 54 | + if (_connected) { |
| 55 | + return false; |
| 56 | + } |
| 57 | + if (uv_udp_init(&_loop, &_socket) < 0) { |
| 58 | + portduinoError("FIXME: implement proper error handling; uv_udp_init failed"); |
| 59 | + } |
| 60 | + _socket.data = this; |
| 61 | + // FIXME: don't do bytes → string → bytes IP conversion |
| 62 | + int maxIpLength = 3*4+3; // 3 digits per octet, 4 octets, 3 dots |
| 63 | + char addr_str[maxIpLength+1]; // +1 for null terminator |
| 64 | + snprintf(addr_str, maxIpLength, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); |
| 65 | + addr_str[maxIpLength] = '\0'; |
| 66 | + struct sockaddr uvAddr; |
| 67 | + uv_ip4_addr(addr_str, port, (struct sockaddr_in *)&uvAddr); |
| 68 | + if (uv_udp_bind(&_socket, (const struct sockaddr *)&uvAddr, 0) < 0) { |
| 69 | + portduinoError("FIXME: implement proper error handling; uv_udp_bind failed"); |
| 70 | + } |
| 71 | + if (uv_udp_set_multicast_loop(&_socket, false) < 0) { |
| 72 | + portduinoError("FIXME: implement proper error handling; uv_udp_set_multicast_loop failed"); |
| 73 | + } |
| 74 | + if (uv_udp_set_multicast_ttl(&_socket, ttl) < 0) { |
| 75 | + portduinoError("FIXME: implement proper error handling; uv_udp_set_multicast_ttl failed"); |
| 76 | + } |
| 77 | + if (uv_udp_set_membership(&_socket, addr_str, NULL, UV_JOIN_GROUP) < 0) { |
| 78 | + portduinoError("FIXME: implement proper error handling; uv_udp_set_membership failed"); |
| 79 | + } |
| 80 | + if (uv_udp_recv_start(&_socket, _asyncudp_alloc_buffer_cb, _asyncudp_on_read_cb) < 0) { |
| 81 | + portduinoError("FIXME: implement proper error handling; uv_udp_recv_start failed"); |
| 82 | + } |
| 83 | + |
| 84 | + _ioThread = std::thread([this](){ |
| 85 | + uv_run(&_loop, UV_RUN_DEFAULT); |
| 86 | + }); |
| 87 | + |
| 88 | + _listenIP = addr; |
| 89 | + _connected = true; |
| 90 | + return true; |
| 91 | +} |
| 92 | + |
| 93 | +size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port) { |
| 94 | + auto task = std::make_unique<asyncUDPSendTask>((uint8_t*)data, len, addr, port); |
| 95 | + _sendQueueMutex.lock(); |
| 96 | + _sendQueue.push_back(std::move(task)); |
| 97 | + _sendQueueMutex.unlock(); |
| 98 | + uv_async_send(&_async); |
| 99 | + return len; |
| 100 | +} |
| 101 | + |
| 102 | +void AsyncUDP::_DO_NOT_CALL_async_cb() { |
| 103 | + _sendQueueMutex.lock(); |
| 104 | + while (!_sendQueue.empty()) { |
| 105 | + auto task = std::move(_sendQueue.back()); |
| 106 | + _sendQueue.pop_back(); |
| 107 | + _sendQueueMutex.unlock(); |
| 108 | + _doWrite(task->data, task->len, task->addr, task->port); |
| 109 | + _sendQueueMutex.lock(); |
| 110 | + } |
| 111 | + _sendQueueMutex.unlock(); |
| 112 | + if (_quit.load()) { |
| 113 | + uv_udp_recv_stop(&_socket); |
| 114 | + // FIXME: don't do bytes → string → bytes IP conversion |
| 115 | + int maxIpLength = 3*4+3; // 3 digits per octet, 4 octets, 3 dots |
| 116 | + char addr_str[maxIpLength+1]; // +1 for null terminator |
| 117 | + snprintf(addr_str, maxIpLength, "%d.%d.%d.%d", _listenIP[0], _listenIP[1], _listenIP[2], _listenIP[3]); |
| 118 | + addr_str[maxIpLength] = '\0'; |
| 119 | + uv_udp_set_membership(&_socket, addr_str, NULL, UV_LEAVE_GROUP); |
| 120 | + uv_stop(&_loop); |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +void _asyncudp_send_cb(uv_udp_send_t *req, int status) { |
| 125 | + free(req); |
| 126 | +} |
| 127 | + |
| 128 | +void AsyncUDP::_doWrite(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port) { |
| 129 | + // FIXME: don't do bytes → string → bytes IP conversion |
| 130 | + int maxIpLength = 3*4+3; // 3 digits per octet, 4 octets, 3 dots |
| 131 | + char addr_str[maxIpLength+1]; // +1 for null terminator |
| 132 | + snprintf(addr_str, maxIpLength, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); |
| 133 | + addr_str[maxIpLength] = '\0'; |
| 134 | + |
| 135 | + // FIXME: implement error handling rather than raising SIGSEGV |
| 136 | + struct sockaddr uvAddr; |
| 137 | + uv_ip4_addr(addr_str, port, (struct sockaddr_in *)&uvAddr); |
| 138 | + |
| 139 | + uv_udp_send_t *req = (uv_udp_send_t *)malloc(sizeof(uv_udp_send_t)); |
| 140 | + uv_buf_t msg; |
| 141 | + msg.base = (char *)data; |
| 142 | + msg.len = len; |
| 143 | + if (uv_udp_send(req, &_socket, &msg, 1, (const struct sockaddr *)&uvAddr, _asyncudp_send_cb) < 0) { |
| 144 | + portduinoError("FIXME: implement proper error handling; uv_udp_send failed"); |
| 145 | + } |
| 146 | +} |
0 commit comments