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