1
0
mirror of https://github.com/libuv/libuv synced 2025-03-28 21:13:16 +00:00

udp: add support for UDP connected sockets

Add two new methods:

`uv_udp_connect()` to connect / disconnect an UDP handle.
`uv_udp_getpeername()` to get the remote peer address of a connected UDP
handle.

Modify `uv_udp_send()` and `uv_udp_try_send()` to accept a `NULL` `addr`
to send messages over an "UDP connection".

Refs: https://github.com/libuv/leps/pull/10
PR-URL: https://github.com/libuv/libuv/pull/1872
Backport-PR-URL: https://github.com/libuv/libuv/pull/2217
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Santiago Gimeno 2017-03-20 11:32:25 +01:00 committed by cjihrig
parent 03061d54f0
commit 90415a3394
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
13 changed files with 529 additions and 32 deletions

View File

@ -153,6 +153,7 @@ set(uv_test_sources
test/test-tty.c
test/test-udp-alloc-cb-fail.c
test/test-udp-bind.c
test/test-udp-connect.c
test/test-udp-create-socket-early.c
test/test-udp-dgram-too-big.c
test/test-udp-ipv6.c

View File

@ -281,6 +281,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-tty.c \
test/test-udp-alloc-cb-fail.c \
test/test-udp-bind.c \
test/test-udp-connect.c \
test/test-udp-create-socket-early.c \
test/test-udp-dgram-too-big.c \
test/test-udp-ipv6.c \

View File

@ -150,6 +150,44 @@ API
:returns: 0 on success, or an error code < 0 on failure.
.. c:function:: int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr)
Associate the UDP handle to a remote address and port, so every
message sent by this handle is automatically sent to that destination.
Calling this function with a `NULL` `addr` disconnects the handle.
Trying to call `uv_udp_connect()` on an already connected handle will result
in an `UV_EISCONN` error. Trying to disconnect a handle that is not
connected will return an `UV_ENOTCONN` error.
:param handle: UDP handle. Should have been initialized with
:c:func:`uv_udp_init`.
:param addr: `struct sockaddr_in` or `struct sockaddr_in6`
with the address and port to associate to.
:returns: 0 on success, or an error code < 0 on failure.
.. versionadded:: 1.27.0
.. c:function:: int uv_udp_getpeername(const uv_udp_t* handle, struct sockaddr* name, int* namelen)
Get the remote IP and port of the UDP handle on connected UDP handles.
On unconnected handles, it returns `UV_ENOTCONN`.
:param handle: UDP handle. Should have been initialized with
:c:func:`uv_udp_init` and bound.
:param name: Pointer to the structure to be filled with the address data.
In order to support IPv4 and IPv6 `struct sockaddr_storage` should be
used.
:param namelen: On input it indicates the data of the `name` field. On
output it indicates how much of it was filled.
:returns: 0 on success, or an error code < 0 on failure
.. versionadded:: 1.27.0
.. c:function:: int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen)
Get the local IP and port of the UDP handle.
@ -247,6 +285,12 @@ API
(``0.0.0.0`` or ``::``) it will be changed to point to ``localhost``.
This is done to match the behavior of Linux systems.
For connected UDP handles, `addr` must be set to `NULL`, otherwise it will
return `UV_EISCONN` error.
For connectionless UDP handles, `addr` cannot be `NULL`, otherwise it will
return `UV_EDESTADDRREQ` error.
:param req: UDP request handle. Need not be initialized.
:param handle: UDP handle. Should have been initialized with
@ -266,15 +310,25 @@ API
.. versionchanged:: 1.19.0 added ``0.0.0.0`` and ``::`` to ``localhost``
mapping
.. versionchanged:: 1.27.0 added support for connected sockets
.. c:function:: int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr)
Same as :c:func:`uv_udp_send`, but won't queue a send request if it can't
be completed immediately.
For connected UDP handles, `addr` must be set to `NULL`, otherwise it will
return `UV_EISCONN` error.
For connectionless UDP handles, `addr` cannot be `NULL`, otherwise it will
return `UV_EDESTADDRREQ` error.
:returns: >= 0: number of bytes sent (it matches the given buffer size).
< 0: negative error code (``UV_EAGAIN`` is returned when the message
can't be sent immediately).
.. versionchanged:: 1.27.0 added support for connected sockets
.. c:function:: int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb)
Prepare for receiving data. If the socket has not previously been bound

View File

@ -630,7 +630,11 @@ UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock);
UV_EXTERN int uv_udp_bind(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned int flags);
UV_EXTERN int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr);
UV_EXTERN int uv_udp_getpeername(const uv_udp_t* handle,
struct sockaddr* name,
int* namelen);
UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle,
struct sockaddr* name,
int* namelen);

View File

@ -244,9 +244,9 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
addrlen = sizeof(sa);
memset(&sa, 0, addrlen);
err = uv__getsockpeername((const uv_handle_t*) handle,
func,
(struct sockaddr*) &sa,
(int*) &addrlen);
func,
(struct sockaddr*) &sa,
(int*) &addrlen);
if (err < 0) {
*size = 0;
return err;

View File

@ -227,9 +227,14 @@ static void uv__udp_sendmsg(uv_udp_t* handle) {
assert(req != NULL);
memset(&h, 0, sizeof h);
h.msg_name = &req->addr;
h.msg_namelen = (req->addr.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
if (req->addr.ss_family == AF_UNSPEC) {
h.msg_name = NULL;
h.msg_namelen = 0;
} else {
h.msg_name = &req->addr;
h.msg_namelen = req->addr.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
}
h.msg_iov = (struct iovec*) req->bufs;
h.msg_iovlen = req->nbufs;
@ -383,6 +388,50 @@ static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
}
int uv__udp_connect(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned int addrlen) {
int err;
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
if (err)
return err;
do {
errno = 0;
err = connect(handle->io_watcher.fd, addr, addrlen);
} while (err == -1 && errno == EINTR);
if (err)
return UV__ERR(errno);
handle->flags |= UV_HANDLE_UDP_CONNECTED;
return 0;
}
int uv__udp_disconnect(uv_udp_t* handle) {
int r;
struct sockaddr addr;
memset(&addr, 0, sizeof(addr));
addr.sa_family = AF_UNSPEC;
do {
errno = 0;
r = connect(handle->io_watcher.fd, &addr, sizeof(addr));
} while (r == -1 && errno == EINTR);
if (r == -1 && errno != EAFNOSUPPORT)
return UV__ERR(errno);
handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
return 0;
}
int uv__udp_send(uv_udp_send_t* req,
uv_udp_t* handle,
const uv_buf_t bufs[],
@ -395,9 +444,11 @@ int uv__udp_send(uv_udp_send_t* req,
assert(nbufs > 0);
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
if (err)
return err;
if (addr) {
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
if (err)
return err;
}
/* It's legal for send_queue_count > 0 even when the write_queue is empty;
* it means there are error-state requests in the write_completed_queue that
@ -407,7 +458,10 @@ int uv__udp_send(uv_udp_send_t* req,
uv__req_init(handle->loop, req, UV_UDP_SEND);
assert(addrlen <= sizeof(req->addr));
memcpy(&req->addr, addr, addrlen);
if (addr == NULL)
req->addr.ss_family = AF_UNSPEC;
else
memcpy(&req->addr, addr, addrlen);
req->send_cb = send_cb;
req->handle = handle;
req->nbufs = nbufs;
@ -459,9 +513,13 @@ int uv__udp_try_send(uv_udp_t* handle,
if (handle->send_queue_count != 0)
return UV_EAGAIN;
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
if (err)
return err;
if (addr) {
err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
if (err)
return err;
} else {
assert(handle->flags & UV_HANDLE_UDP_CONNECTED);
}
memset(&h, 0, sizeof h);
h.msg_name = (struct sockaddr*) addr;
@ -608,6 +666,7 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) {
uv__io_init(&handle->io_watcher, uv__udp_io, fd);
QUEUE_INIT(&handle->write_queue);
QUEUE_INIT(&handle->write_completed_queue);
return 0;
}
@ -636,6 +695,9 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
return err;
handle->io_watcher.fd = sock;
if (uv__udp_is_connected(handle))
handle->flags |= UV_HANDLE_UDP_CONNECTED;
return 0;
}
@ -851,6 +913,15 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr)
return 0;
}
int uv_udp_getpeername(const uv_udp_t* handle,
struct sockaddr* name,
int* namelen) {
return uv__getsockpeername((const uv_handle_t*) handle,
getpeername,
name,
namelen);
}
int uv_udp_getsockname(const uv_udp_t* handle,
struct sockaddr* name,

View File

@ -317,17 +317,20 @@ int uv_tcp_connect(uv_connect_t* req,
}
int uv_udp_send(uv_udp_send_t* req,
uv_udp_t* handle,
const uv_buf_t bufs[],
unsigned int nbufs,
const struct sockaddr* addr,
uv_udp_send_cb send_cb) {
int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) {
unsigned int addrlen;
if (handle->type != UV_UDP)
return UV_EINVAL;
/* Disconnect the handle */
if (addr == NULL) {
if (!(handle->flags & UV_HANDLE_UDP_CONNECTED))
return UV_ENOTCONN;
return uv__udp_disconnect(handle);
}
if (addr->sa_family == AF_INET)
addrlen = sizeof(struct sockaddr_in);
else if (addr->sa_family == AF_INET6)
@ -335,6 +338,66 @@ int uv_udp_send(uv_udp_send_t* req,
else
return UV_EINVAL;
if (handle->flags & UV_HANDLE_UDP_CONNECTED)
return UV_EISCONN;
return uv__udp_connect(handle, addr, addrlen);
}
int uv__udp_is_connected(uv_udp_t* handle) {
struct sockaddr_storage addr;
int addrlen;
if (handle->type != UV_UDP)
return 0;
addrlen = sizeof(addr);
if (uv_udp_getpeername(handle, (struct sockaddr*) &addr, &addrlen) != 0)
return 0;
return addrlen > 0;
}
int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) {
unsigned int addrlen;
if (handle->type != UV_UDP)
return UV_EINVAL;
if (addr != NULL && (handle->flags & UV_HANDLE_UDP_CONNECTED))
return UV_EISCONN;
if (addr == NULL && !(handle->flags & UV_HANDLE_UDP_CONNECTED))
return UV_EDESTADDRREQ;
if (addr != NULL) {
if (addr->sa_family == AF_INET)
addrlen = sizeof(struct sockaddr_in);
else if (addr->sa_family == AF_INET6)
addrlen = sizeof(struct sockaddr_in6);
else
return UV_EINVAL;
} else {
addrlen = 0;
}
return addrlen;
}
int uv_udp_send(uv_udp_send_t* req,
uv_udp_t* handle,
const uv_buf_t bufs[],
unsigned int nbufs,
const struct sockaddr* addr,
uv_udp_send_cb send_cb) {
int addrlen;
addrlen = uv__udp_check_before_send(handle, addr);
if (addrlen < 0)
return addrlen;
return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
}
@ -343,17 +406,11 @@ int uv_udp_try_send(uv_udp_t* handle,
const uv_buf_t bufs[],
unsigned int nbufs,
const struct sockaddr* addr) {
unsigned int addrlen;
int addrlen;
if (handle->type != UV_UDP)
return UV_EINVAL;
if (addr->sa_family == AF_INET)
addrlen = sizeof(struct sockaddr_in);
else if (addr->sa_family == AF_INET6)
addrlen = sizeof(struct sockaddr_in6);
else
return UV_EINVAL;
addrlen = uv__udp_check_before_send(handle, addr);
if (addrlen < 0)
return addrlen;
return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen);
}

View File

@ -103,6 +103,7 @@ enum {
/* Only used by uv_udp_t handles. */
UV_HANDLE_UDP_PROCESSING = 0x01000000,
UV_HANDLE_UDP_CONNECTED = 0x02000000,
/* Only used by uv_pipe_t handles. */
UV_HANDLE_NON_OVERLAPPED_PIPE = 0x01000000,
@ -142,6 +143,14 @@ int uv__udp_bind(uv_udp_t* handle,
unsigned int addrlen,
unsigned int flags);
int uv__udp_connect(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned int addrlen);
int uv__udp_disconnect(uv_udp_t* handle);
int uv__udp_is_connected(uv_udp_t* handle);
int uv__udp_send(uv_udp_send_t* req,
uv_udp_t* handle,
const uv_buf_t bufs[],

View File

@ -36,6 +36,17 @@ const unsigned int uv_active_udp_streams_threshold = 0;
/* A zero-size buffer for use by uv_udp_read */
static char uv_zero_[] = "";
int uv_udp_getpeername(const uv_udp_t* handle,
struct sockaddr* name,
int* namelen) {
return uv__getsockpeername((const uv_handle_t*) handle,
getpeername,
name,
namelen,
0);
}
int uv_udp_getsockname(const uv_udp_t* handle,
struct sockaddr* name,
@ -815,6 +826,9 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
if (uv__udp_is_bound(handle))
handle->flags |= UV_HANDLE_BOUND;
if (uv__udp_is_connected(handle))
handle->flags |= UV_HANDLE_UDP_CONNECTED;
return 0;
}
@ -892,6 +906,50 @@ int uv__udp_bind(uv_udp_t* handle,
}
int uv__udp_connect(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned int addrlen) {
const struct sockaddr* bind_addr;
int err;
if (!(handle->flags & UV_HANDLE_BOUND)) {
if (addrlen == sizeof(uv_addr_ip4_any_))
bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
else if (addrlen == sizeof(uv_addr_ip6_any_))
bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
else
return UV_EINVAL;
err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
if (err)
return uv_translate_sys_error(err);
}
err = connect(handle->socket, addr, addrlen);
if (err)
return uv_translate_sys_error(err);
handle->flags |= UV_HANDLE_UDP_CONNECTED;
return 0;
}
int uv__udp_disconnect(uv_udp_t* handle) {
int err;
struct sockaddr addr;
memset(&addr, 0, sizeof(addr));
err = connect(handle->socket, &addr, sizeof(addr));
if (err)
return uv_translate_sys_error(err);
handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
return 0;
}
/* This function is an egress point, i.e. it returns libuv errors rather than
* system errors.
*/
@ -912,6 +970,7 @@ int uv__udp_send(uv_udp_send_t* req,
bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
else
return UV_EINVAL;
err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
if (err)
return uv_translate_sys_error(err);
@ -937,9 +996,11 @@ int uv__udp_try_send(uv_udp_t* handle,
assert(nbufs > 0);
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
if (err)
return err;
if (addr != NULL) {
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
if (err)
return err;
}
/* Already sending a message.*/
if (handle->send_queue_count != 0)

View File

@ -132,6 +132,7 @@ TEST_DECLARE (tcp_write_ready)
TEST_DECLARE (udp_alloc_cb_fail)
TEST_DECLARE (udp_bind)
TEST_DECLARE (udp_bind_reuseaddr)
TEST_DECLARE (udp_connect)
TEST_DECLARE (udp_create_early)
TEST_DECLARE (udp_create_early_bad_bind)
TEST_DECLARE (udp_create_early_bad_domain)
@ -153,6 +154,7 @@ TEST_DECLARE (udp_no_autobind)
TEST_DECLARE (udp_open)
TEST_DECLARE (udp_open_twice)
TEST_DECLARE (udp_open_bound)
TEST_DECLARE (udp_open_connect)
TEST_DECLARE (udp_try_send)
TEST_DECLARE (pipe_bind_error_addrinuse)
TEST_DECLARE (pipe_bind_error_addrnotavail)
@ -623,6 +625,7 @@ TASK_LIST_START
TEST_ENTRY (udp_alloc_cb_fail)
TEST_ENTRY (udp_bind)
TEST_ENTRY (udp_bind_reuseaddr)
TEST_ENTRY (udp_connect)
TEST_ENTRY (udp_create_early)
TEST_ENTRY (udp_create_early_bad_bind)
TEST_ENTRY (udp_create_early_bad_domain)
@ -647,6 +650,8 @@ TASK_LIST_START
TEST_HELPER (udp_open, udp4_echo_server)
TEST_ENTRY (udp_open_twice)
TEST_ENTRY (udp_open_bound)
TEST_ENTRY (udp_open_connect)
TEST_HELPER (udp_open_connect, udp4_echo_server)
TEST_ENTRY (pipe_bind_error_addrinuse)
TEST_ENTRY (pipe_bind_error_addrnotavail)

182
test/test-udp-connect.c Normal file
View File

@ -0,0 +1,182 @@
/* Copyright libuv project and contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "task.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CHECK_HANDLE(handle) \
ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client)
static uv_udp_t server;
static uv_udp_t client;
static uv_buf_t buf;
static struct sockaddr_in lo_addr;
static int cl_send_cb_called;
static int sv_recv_cb_called;
static int close_cb_called;
static void alloc_cb(uv_handle_t* handle,
size_t suggested_size,
uv_buf_t* buf) {
static char slab[65536];
CHECK_HANDLE(handle);
ASSERT(suggested_size <= sizeof(slab));
buf->base = slab;
buf->len = sizeof(slab);
}
static void close_cb(uv_handle_t* handle) {
CHECK_HANDLE(handle);
ASSERT(uv_is_closing(handle));
close_cb_called++;
}
static void cl_send_cb(uv_udp_send_t* req, int status) {
int r;
ASSERT(req != NULL);
ASSERT(status == 0);
CHECK_HANDLE(req->handle);
if (++cl_send_cb_called == 1) {
uv_udp_connect(&client, NULL);
r = uv_udp_send(req, &client, &buf, 1, NULL, cl_send_cb);
ASSERT(r == UV_EDESTADDRREQ);
r = uv_udp_send(req,
&client,
&buf,
1,
(const struct sockaddr*) &lo_addr,
cl_send_cb);
ASSERT(r == 0);
}
}
static void sv_recv_cb(uv_udp_t* handle,
ssize_t nread,
const uv_buf_t* rcvbuf,
const struct sockaddr* addr,
unsigned flags) {
if (nread > 0) {
ASSERT(nread == 4);
ASSERT(addr != NULL);
ASSERT(memcmp("EXIT", rcvbuf->base, nread) == 0);
if (++sv_recv_cb_called == 4) {
uv_close((uv_handle_t*) &server, close_cb);
uv_close((uv_handle_t*) &client, close_cb);
}
}
}
TEST_IMPL(udp_connect) {
uv_udp_send_t req;
struct sockaddr_in ext_addr;
struct sockaddr_in tmp_addr;
int r;
int addrlen;
ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &lo_addr));
r = uv_udp_init(uv_default_loop(), &server);
ASSERT(r == 0);
r = uv_udp_bind(&server, (const struct sockaddr*) &lo_addr, 0);
ASSERT(r == 0);
r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb);
ASSERT(r == 0);
r = uv_udp_init(uv_default_loop(), &client);
ASSERT(r == 0);
buf = uv_buf_init("EXIT", 4);
ASSERT(0 == uv_ip4_addr("8.8.8.8", TEST_PORT, &ext_addr));
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &lo_addr));
r = uv_udp_connect(&client, (const struct sockaddr*) &lo_addr);
ASSERT(r == 0);
r = uv_udp_connect(&client, (const struct sockaddr*) &ext_addr);
ASSERT(r == UV_EISCONN);
addrlen = sizeof(tmp_addr);
r = uv_udp_getpeername(&client, (struct sockaddr*) &tmp_addr, &addrlen);
ASSERT(r == 0);
/* To send messages in connected UDP sockets addr must be NULL */
r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &lo_addr);
ASSERT(r == UV_EISCONN);
r = uv_udp_try_send(&client, &buf, 1, NULL);
ASSERT(r == 4);
r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &ext_addr);
ASSERT(r == UV_EISCONN);
r = uv_udp_connect(&client, NULL);
ASSERT(r == 0);
r = uv_udp_connect(&client, NULL);
ASSERT(r == UV_ENOTCONN);
addrlen = sizeof(tmp_addr);
r = uv_udp_getpeername(&client, (struct sockaddr*) &tmp_addr, &addrlen);
ASSERT(r == UV_ENOTCONN);
/* To send messages in disconnected UDP sockets addr must be set */
r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &lo_addr);
ASSERT(r == 4);
r = uv_udp_try_send(&client, &buf, 1, NULL);
ASSERT(r == UV_EDESTADDRREQ);
r = uv_udp_connect(&client, (const struct sockaddr*) &lo_addr);
ASSERT(r == 0);
r = uv_udp_send(&req,
&client,
&buf,
1,
(const struct sockaddr*) &lo_addr,
cl_send_cb);
ASSERT(r == UV_EISCONN);
r = uv_udp_send(&req, &client, &buf, 1, NULL, cl_send_cb);
ASSERT(r == 0);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT(close_cb_called == 2);
ASSERT(sv_recv_cb_called == 4);
ASSERT(cl_send_cb_called == 2);
ASSERT(client.send_queue_size == 0);
ASSERT(server.send_queue_size == 0);
MAKE_VALGRIND_HAPPY();
return 0;
}

View File

@ -129,6 +129,7 @@ static void send_cb(uv_udp_send_t* req, int status) {
ASSERT(status == 0);
send_cb_called++;
uv_close((uv_handle_t*)req->handle, close_cb);
}
@ -245,3 +246,53 @@ TEST_IMPL(udp_open_bound) {
MAKE_VALGRIND_HAPPY();
return 0;
}
TEST_IMPL(udp_open_connect) {
struct sockaddr_in addr;
uv_buf_t buf = uv_buf_init("PING", 4);
uv_udp_t client;
uv_udp_t server;
uv_os_sock_t sock;
int r;
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
startup();
sock = create_udp_socket();
r = uv_udp_init(uv_default_loop(), &client);
ASSERT(r == 0);
r = connect(sock, (const struct sockaddr*) &addr, sizeof(addr));
ASSERT(r == 0);
r = uv_udp_open(&client, sock);
ASSERT(r == 0);
r = uv_udp_init(uv_default_loop(), &server);
ASSERT(r == 0);
r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0);
ASSERT(r == 0);
r = uv_udp_recv_start(&server, alloc_cb, recv_cb);
ASSERT(r == 0);
r = uv_udp_send(&send_req,
&client,
&buf,
1,
NULL,
send_cb);
ASSERT(r == 0);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT(send_cb_called == 1);
ASSERT(close_cb_called == 2);
ASSERT(client.send_queue_size == 0);
MAKE_VALGRIND_HAPPY();
return 0;
}

View File

@ -137,6 +137,7 @@
'test-tty.c',
'test-udp-alloc-cb-fail.c',
'test-udp-bind.c',
'test-udp-connect.c',
'test-udp-create-socket-early.c',
'test-udp-dgram-too-big.c',
'test-udp-ipv6.c',