mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
udp
This commit is contained in:
parent
1820212724
commit
7cef6fcc7b
8 changed files with 206 additions and 21 deletions
|
@ -743,7 +743,9 @@ callbacks on the named protocol
|
|||
|
||||
starting with LWS_CALLBACK_RAW_ADOPT_FILE.
|
||||
|
||||
`protocol-lws-raw-test` plugin provides a method for testing this with
|
||||
The minimal example `raw/minimal-raw-file` demonstrates how to use it.
|
||||
|
||||
`protocol-lws-raw-test` plugin also provides a method for testing this with
|
||||
`libwebsockets-test-server-v2.0`:
|
||||
|
||||
The plugin creates a FIFO on your system called "/tmp/lws-test-raw"
|
||||
|
@ -827,6 +829,46 @@ and in another window, connect to it using the test client
|
|||
The connection should succeed, and text typed in the netcat window (including a CRLF)
|
||||
will be received in the client.
|
||||
|
||||
@section rawudp RAW UDP socket integration
|
||||
|
||||
Lws provides an api to create, optionally bind, and adopt a RAW UDP
|
||||
socket (RAW here means an uninterpreted normal UDP socket, not a
|
||||
"raw socket").
|
||||
|
||||
```
|
||||
LWS_VISIBLE LWS_EXTERN struct lws *
|
||||
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
|
||||
const char *protocol_name, struct lws *parent_wsi);
|
||||
```
|
||||
|
||||
`flags` should be `LWS_CAUDP_BIND` if the socket will receive packets.
|
||||
|
||||
The callbacks `LWS_CALLBACK_RAW_ADOPT`, `LWS_CALLBACK_RAW_CLOSE`,
|
||||
`LWS_CALLBACK_RAW_RX` and `LWS_CALLBACK_RAW_WRITEABLE` apply to the
|
||||
wsi. But UDP is different than TCP in some fundamental ways.
|
||||
|
||||
For receiving on a UDP connection, data becomes available at
|
||||
`LWS_CALLBACK_RAW_RX` as usual, but because there is no specific
|
||||
connection with UDP, it is necessary to also get the source address of
|
||||
the data separately, using `struct lws_udp * lws_get_udp(wsi)`.
|
||||
You should take a copy of the `struct lws_udp` itself (not the
|
||||
pointer) and save it for when you want to write back to that peer.
|
||||
|
||||
Writing is also a bit different for UDP. By default, the system has no
|
||||
idea about the receiver state and so asking for a `callback_on_writable()`
|
||||
always believes that the socket is writeable... the callback will
|
||||
happen next time around the event loop.
|
||||
|
||||
With UDP, there is no single "connection". You need to write with sendto() and
|
||||
direct the packets to a specific destination. To return packets to a
|
||||
peer who sent something earlier and you copied his `struct lws_udp`, you
|
||||
use the .sa and .salen members as the last two parameters of the sendto().
|
||||
|
||||
The kernel may not accept to buffer / write everything you wanted to send.
|
||||
So you are responsible to watch the result of sendto() and resend the
|
||||
unsent part next time (which may involve adding new protocol headers to
|
||||
the remainder depending on what you are doing).
|
||||
|
||||
@section ecdh ECDH Support
|
||||
|
||||
ECDH Certs are now supported. Enable the CMake option
|
||||
|
|
|
@ -565,6 +565,7 @@ lws_create_vhost(struct lws_context *context,
|
|||
#endif
|
||||
struct lws_protocols *lwsp;
|
||||
int m, f = !info->pvo;
|
||||
char buf[20];
|
||||
#ifdef LWS_HAVE_GETENV
|
||||
char *p;
|
||||
#endif
|
||||
|
@ -721,10 +722,22 @@ lws_create_vhost(struct lws_context *context,
|
|||
vh->name, vh->iface, vh->count_protocols);
|
||||
} else
|
||||
#endif
|
||||
lwsl_notice("Creating Vhost '%s' port %d, %d protocols, IPv6 %s\n",
|
||||
vh->name, info->port, vh->count_protocols,
|
||||
LWS_IPV6_ENABLED(vh) ? "on" : "off");
|
||||
|
||||
{
|
||||
switch(info->port) {
|
||||
case CONTEXT_PORT_NO_LISTEN:
|
||||
strcpy(buf, "(serving disabled)");
|
||||
break;
|
||||
case CONTEXT_PORT_NO_LISTEN_SERVER:
|
||||
strcpy(buf, "(no listener)");
|
||||
break;
|
||||
default:
|
||||
lws_snprintf(buf, sizeof(buf), "port %u", info->port);
|
||||
break;
|
||||
}
|
||||
lwsl_notice("Creating Vhost '%s' %s, %d protocols, IPv6 %s\n",
|
||||
vh->name, buf, vh->count_protocols,
|
||||
LWS_IPV6_ENABLED(vh) ? "on" : "off");
|
||||
}
|
||||
mounts = info->mounts;
|
||||
while (mounts) {
|
||||
(void)mount_protocols[0];
|
||||
|
|
|
@ -80,6 +80,7 @@ __lws_free_wsi(struct lws *wsi)
|
|||
lws_free_set_NULL(wsi->rxflow_buffer);
|
||||
lws_free_set_NULL(wsi->trunc_alloc);
|
||||
lws_free_set_NULL(wsi->ws);
|
||||
lws_free_set_NULL(wsi->udp);
|
||||
|
||||
/* we may not have an ah, but may be on the waiting list... */
|
||||
lwsl_info("ah det due to close\n");
|
||||
|
@ -1330,6 +1331,12 @@ lws_protocol_get(struct lws *wsi)
|
|||
return wsi->protocol;
|
||||
}
|
||||
|
||||
LWS_VISIBLE const struct lws_udp *
|
||||
lws_get_udp(const struct lws *wsi)
|
||||
{
|
||||
return wsi->udp;
|
||||
}
|
||||
|
||||
LWS_VISIBLE struct lws *
|
||||
lws_get_network_wsi(struct lws *wsi)
|
||||
{
|
||||
|
@ -2694,12 +2701,71 @@ lws_get_addr_scope(const char *ipaddr)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if !defined(LWS_NO_SERVER)
|
||||
|
||||
LWS_EXTERN struct lws *
|
||||
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
|
||||
const char *protocol_name, struct lws *parent_wsi)
|
||||
{
|
||||
lws_sock_file_fd_type sock;
|
||||
struct addrinfo h, *r, *rp;
|
||||
struct lws *wsi = NULL;
|
||||
char buf[16];
|
||||
int n;
|
||||
|
||||
memset(&h, 0, sizeof(h));
|
||||
h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||
h.ai_socktype = SOCK_DGRAM;
|
||||
h.ai_protocol = IPPROTO_UDP;
|
||||
h.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
|
||||
|
||||
lws_snprintf(buf, sizeof(buf), "%u", port);
|
||||
n = getaddrinfo(NULL, buf, &h, &r);
|
||||
if (n) {
|
||||
lwsl_info("%s: getaddrinfo error: %s\n", __func__,
|
||||
gai_strerror(n));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (rp = r; rp; rp = rp->ai_next) {
|
||||
sock.sockfd = socket(rp->ai_family, rp->ai_socktype,
|
||||
rp->ai_protocol);
|
||||
if (sock.sockfd >= 0)
|
||||
break;
|
||||
}
|
||||
if (!rp) {
|
||||
lwsl_err("%s: unable to create INET socket\n", __func__);
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
if ((flags & LWS_CAUDP_BIND) &&
|
||||
bind(sock.sockfd, rp->ai_addr, rp->ai_addrlen) ==-1) {
|
||||
lwsl_err("%s: bind failed\n", __func__);
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock,
|
||||
protocol_name, parent_wsi);
|
||||
if (!wsi)
|
||||
lwsl_err("%s: udp adoption failed\n", __func__);
|
||||
|
||||
bail2:
|
||||
if (!wsi)
|
||||
close(sock.sockfd);
|
||||
bail1:
|
||||
freeaddrinfo(r);
|
||||
|
||||
bail:
|
||||
return wsi;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
LWS_EXTERN void
|
||||
lws_restart_ws_ping_pong_timer(struct lws *wsi)
|
||||
{
|
||||
if (!wsi->context->ws_ping_pong_interval)
|
||||
return;
|
||||
if (!lws_state_is_ws(wsi->state))
|
||||
if (!wsi->context->ws_ping_pong_interval ||
|
||||
!lws_state_is_ws(wsi->state))
|
||||
return;
|
||||
|
||||
wsi->ws->time_next_ping_check = (time_t)lws_now_secs();
|
||||
|
|
|
@ -4555,6 +4555,7 @@ enum pending_timeout {
|
|||
PENDING_TIMEOUT_KILLED_BY_PARENT = 23,
|
||||
PENDING_TIMEOUT_CLOSE_SEND = 24,
|
||||
PENDING_TIMEOUT_HOLDING_AH = 25,
|
||||
PENDING_TIMEOUT_UDP_IDLE = 26,
|
||||
|
||||
/****** add new things just above ---^ ******/
|
||||
|
||||
|
@ -4998,7 +4999,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
/**
|
||||
* lws_get_socket_fd() - returns the socket file descriptor
|
||||
*
|
||||
* You will not need this unless you are doing something special
|
||||
* This is needed to use sendto() on UDP raw sockets
|
||||
*
|
||||
* \param wsi: Websocket connection instance
|
||||
*/
|
||||
|
@ -5151,8 +5152,10 @@ typedef enum {
|
|||
LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */
|
||||
LWS_ADOPT_WS_PARENTIO = 8, /* flag: ws mode parent handles IO
|
||||
* if given must be only flag
|
||||
* wsi put directly into ws mode
|
||||
*/
|
||||
* wsi put directly into ws mode */
|
||||
LWS_ADOPT_FLAG_UDP = 16, /* flag: socket is UDP */
|
||||
|
||||
LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP,
|
||||
} lws_adoption_type;
|
||||
|
||||
typedef union {
|
||||
|
@ -5160,6 +5163,14 @@ typedef union {
|
|||
lws_filefd_type filefd;
|
||||
} lws_sock_file_fd_type;
|
||||
|
||||
struct lws_udp {
|
||||
struct sockaddr sa;
|
||||
socklen_t salen;
|
||||
|
||||
struct sockaddr sa_pending;
|
||||
socklen_t salen_pending;
|
||||
};
|
||||
|
||||
/*
|
||||
* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor
|
||||
* if socket descriptor, should already have been accepted from listen socket
|
||||
|
@ -5236,6 +5247,24 @@ lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd,
|
|||
LWS_VISIBLE LWS_EXTERN struct lws *
|
||||
lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, lws_sockfd_type accept_fd,
|
||||
const char *readbuf, size_t len);
|
||||
|
||||
#define LWS_CAUDP_BIND 1
|
||||
|
||||
/**
|
||||
* lws_create_adopt_udp() - create, bind and adopt a UDP socket
|
||||
*
|
||||
* \param vhost: lws vhost
|
||||
* \param port: UDP port to bind to, -1 means unbound
|
||||
* \param flags: 0 or LWS_CAUDP_NO_BIND
|
||||
* \param protocol_name: Name of protocol on vhost to bind wsi to
|
||||
* \param parent_wsi: NULL or parent wsi new wsi will be a child of
|
||||
*
|
||||
* Either returns new wsi bound to accept_fd, or closes accept_fd and
|
||||
* returns NULL, having cleaned up any new wsi pieces.
|
||||
* */
|
||||
LWS_VISIBLE LWS_EXTERN struct lws *
|
||||
lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags,
|
||||
const char *protocol_name, struct lws *parent_wsi);
|
||||
///@}
|
||||
|
||||
/** \defgroup net Network related helper APIs
|
||||
|
@ -5632,6 +5661,16 @@ lws_get_parent(const struct lws *wsi);
|
|||
LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT
|
||||
lws_get_child(const struct lws *wsi);
|
||||
|
||||
/**
|
||||
* lws_get_udp() - get wsi's udp struct
|
||||
*
|
||||
* \param wsi: lws connection
|
||||
*
|
||||
* Returns NULL or pointer to the wsi's UDP-specific information
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN const struct lws_udp * LWS_WARN_UNUSED_RESULT
|
||||
lws_get_udp(const struct lws *wsi);
|
||||
|
||||
/**
|
||||
* lws_parent_carries_io() - mark wsi as needing to send messages via parent
|
||||
*
|
||||
|
|
23
lib/output.c
23
lib/output.c
|
@ -198,6 +198,12 @@ handle_truncated_send:
|
|||
wsi->trunc_len = (unsigned int)(real_len - n);
|
||||
memcpy(wsi->trunc_alloc, buf + n, real_len - n);
|
||||
|
||||
if (lws_wsi_is_udp(wsi)) {
|
||||
/* stash original destination for fulfilling UDP partials */
|
||||
wsi->udp->sa_pending = wsi->udp->sa;
|
||||
wsi->udp->salen_pending = wsi->udp->salen;
|
||||
}
|
||||
|
||||
/* since something buffered, force it to get another chance to send */
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
|
@ -858,12 +864,19 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
|||
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_C_API_READ, 1);
|
||||
|
||||
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
|
||||
if (lws_wsi_is_udp(wsi)) {
|
||||
wsi->udp->salen = sizeof(wsi->udp->sa);
|
||||
n = recvfrom(wsi->desc.sockfd, (char *)buf, len, 0,
|
||||
&wsi->udp->sa, &wsi->udp->salen);
|
||||
} else
|
||||
n = recv(wsi->desc.sockfd, (char *)buf, len, 0);
|
||||
|
||||
if (n >= 0) {
|
||||
if (wsi->vhost)
|
||||
wsi->vhost->conn_stats.rx += n;
|
||||
lws_stats_atomic_bump(context, pt, LWSSTATS_B_READ, n);
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
return n;
|
||||
}
|
||||
#if LWS_POSIX
|
||||
|
@ -882,7 +895,13 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
|
|||
int n = 0;
|
||||
|
||||
#if LWS_POSIX
|
||||
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
|
||||
if (lws_wsi_is_udp(wsi)) {
|
||||
if (wsi->trunc_len)
|
||||
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa_pending, wsi->udp->salen_pending);
|
||||
else
|
||||
n = sendto(wsi->desc.sockfd, buf, len, 0, &wsi->udp->sa, wsi->udp->salen);
|
||||
} else
|
||||
n = send(wsi->desc.sockfd, (char *)buf, len, MSG_NOSIGNAL);
|
||||
// lwsl_info("%s: sent len %d result %d", __func__, len, n);
|
||||
if (n >= 0)
|
||||
return n;
|
||||
|
|
|
@ -1840,6 +1840,8 @@ struct lws_access_log {
|
|||
};
|
||||
#endif
|
||||
|
||||
#define lws_wsi_is_udp(___wsi) (!!___wsi->udp)
|
||||
|
||||
struct lws {
|
||||
/* structs */
|
||||
|
||||
|
@ -1881,6 +1883,7 @@ struct lws {
|
|||
#endif
|
||||
struct allocated_headers *ah;
|
||||
struct lws *ah_wait_list;
|
||||
struct lws_udp *udp;
|
||||
unsigned char *preamble_rx;
|
||||
#ifndef LWS_NO_CLIENT
|
||||
struct client_info_stash *stash;
|
||||
|
|
|
@ -70,10 +70,9 @@ lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
|
|||
}
|
||||
#endif
|
||||
rlen = sizeof(addr);
|
||||
if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen)) {
|
||||
lwsl_notice("%s: getpeername failed\n", __func__);
|
||||
if (getpeername(sockfd, (struct sockaddr*)&addr, &rlen))
|
||||
/* eg, udp doesn't have to have a peer */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (af == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
|
@ -111,6 +110,7 @@ lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
|
|||
peer = lws_zalloc(sizeof(*peer), "peer");
|
||||
if (!peer) {
|
||||
lws_context_unlock(context); /* === */
|
||||
lwsl_err("%s: OOM for new peer\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -2012,11 +2012,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) {
|
||||
peer = lws_get_or_create_peer(vh, fd.sockfd);
|
||||
|
||||
if (!peer) {
|
||||
lwsl_err("OOM creating peer\n");
|
||||
return NULL;
|
||||
}
|
||||
if (context->ip_limit_wsi &&
|
||||
if (peer && context->ip_limit_wsi &&
|
||||
peer->count_wsi >= context->ip_limit_wsi) {
|
||||
lwsl_notice("Peer reached wsi limit %d\n",
|
||||
context->ip_limit_wsi);
|
||||
|
@ -2092,6 +2088,13 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
|
|||
lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi,
|
||||
(int)(lws_intptr_t)fd.sockfd);
|
||||
|
||||
if (type & LWS_ADOPT_FLAG_UDP)
|
||||
/*
|
||||
* these can be >128 bytes, so just alloc for UDP
|
||||
*/
|
||||
new_wsi->udp = lws_malloc(sizeof(*new_wsi->udp),
|
||||
"udp struct");
|
||||
|
||||
if (type & LWS_ADOPT_HTTP)
|
||||
/* the transport is accepted...
|
||||
* give him time to negotiate */
|
||||
|
|
Loading…
Add table
Reference in a new issue