1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

minimal: raw udp

This commit is contained in:
Andy Green 2018-03-24 08:08:02 +08:00
parent 7cef6fcc7b
commit d5bb8ecbc6
4 changed files with 297 additions and 0 deletions

View file

@ -1,6 +1,7 @@
|name|demonstrates|
---|---
minimal-raw-adopt-tcp|Shows how to have lws adopt an existing tcp socket something else had connected
minimal-raw-adopt-udp|Shows how to create a udp socket and read and write on it
minimal-raw-file|Shows how to adopt a file descriptor (device node, fifo, file, etc) into the lws event loop and handle events
minimal-raw-vhost|Shows how to set up a vhost that listens and accepts RAW socket connections

View file

@ -0,0 +1,76 @@
cmake_minimum_required(VERSION 2.8)
include(CheckCSourceCompiles)
set(SAMP lws-minimal-raw-adopt-udp)
set(SRCS minimal-raw-adopt-udp.c)
# If we are being built as part of lws, confirm current build config supports
# reqconfig, else skip building ourselves.
#
# If we are being built externally, confirm installed lws was configured to
# support reqconfig, else error out with a helpful message about the problem.
#
MACRO(require_lws_config reqconfig _val result)
if (DEFINED ${reqconfig})
if (${reqconfig})
set (rq 1)
else()
set (rq 0)
endif()
else()
set(rq 0)
endif()
if (${_val} EQUAL ${rq})
set(SAME 1)
else()
set(SAME 0)
endif()
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
if (${_val})
message("${SAMP}: skipping as lws being built without ${reqconfig}")
else()
message("${SAMP}: skipping as lws built with ${reqconfig}")
endif()
set(${result} 0)
else()
if (LWS_WITH_MINIMAL_EXAMPLES)
set(MET ${SAME})
else()
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
set(HAS_${reqconfig} 0)
else()
set(HAS_${reqconfig} 1)
endif()
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
set(MET 1)
else()
set(MET 0)
endif()
endif()
if (NOT MET)
if (${_val})
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
else()
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
endif()
endif()
endif()
ENDMACRO()
set(requirements 1)
require_lws_config(LWS_WITHOUT_SERVER 0 requirements)
if (requirements)
add_executable(${SAMP} ${SRCS})
if (websockets_shared)
target_link_libraries(${SAMP} websockets_shared)
add_dependencies(${SAMP} websockets_shared)
else()
target_link_libraries(${SAMP} websockets)
endif()
endif()

View file

@ -0,0 +1,49 @@
# lws minimal ws server raw adopt udp
This example demonstrates echoing packets on a UDP socket in lws.
A "foreign" UDP socket is created, bound (so it can "listen"), and
adopted into lws event loop. It acts like a tcp RAW mode connection in
lws and uses the same callbacks.
Writing is 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 if there are no pending partials.
With UDP, there is no "connection". You need to write with sendto() and
direct the packets to a specific destination. You can learn the source
of the last packet that arrived at the LWS_CALLBACK_RAW_RX callback by
getting a `struct lws_udp *` from `lws_get_udp(wsi)`. To be able to
send back to that guy, you should take a copy of the `struct lws_udp *` and
use the .sa and .salen members in your sendto().
However 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.
## build
```
$ cmake . && make
```
## usage
```
$ ./lws-minimal-raw-adopt-udp
$ ./lws-minimal-raw-adopt-udp
[2018/03/24 08:12:37:8869] USER: LWS minimal raw adopt udp | nc -u 127.0.0.1 7681
[2018/03/24 08:12:37:8870] NOTICE: Creating Vhost 'default' (no listener), 1 protocols, IPv6 off
[2018/03/24 08:12:37:8878] USER: LWS_CALLBACK_RAW_ADOPT
[2018/03/24 08:12:41:5656] USER: LWS_CALLBACK_RAW_RX (6)
[2018/03/24 08:12:41:5656] NOTICE:
[2018/03/24 08:12:41:5656] NOTICE: 0000: 68 65 6C 6C 6F 0A hello.
[2018/03/24 08:12:41:5656] NOTICE:
```
```
$ nc -u 127.0.0.1 7681
hello
hello
```

View file

@ -0,0 +1,171 @@
/*
* lws-minimal-raw-adopt-udp
*
* Copyright (C) 2018 Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* This demonstrates integrating a connected udp
* socket into the lws event loop as a RAW wsi. It's interesting in
* the kind of situation where you already have a connected socket
* in your application, and you need to hand it over to lws to deal with.
*
* Lws supports "adopting" these foreign sockets, and also has a helper API
* to create, bind, and adopt them inside lws.
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
static uint8_t sendbuf[4096];
static size_t sendlen;
struct lws_udp udp;
static int
callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
ssize_t n;
switch (reason) {
/* callbacks related to raw socket descriptor */
case LWS_CALLBACK_RAW_ADOPT:
lwsl_user("LWS_CALLBACK_RAW_ADOPT\n");
break;
case LWS_CALLBACK_RAW_CLOSE:
lwsl_user("LWS_CALLBACK_RAW_CLOSE\n");
break;
case LWS_CALLBACK_RAW_RX:
lwsl_user("LWS_CALLBACK_RAW_RX (%d)\n", (int)len);
lwsl_hexdump_level(LLL_NOTICE, in, len);
/*
* Take a copy of the buffer and the source socket address...
*/
udp = *(lws_get_udp(wsi));
sendlen = len;
if (sendlen > sizeof(sendbuf))
sendlen = sizeof(sendbuf);
memcpy(sendbuf, in, sendlen);
/*
* ... and we send it next time around the event loop. This
* can be extended to having a ringbuffer of different send
* buffers and targets queued.
*
* Note that UDP is ALWAYS writable as far as poll() knows
* because there is no mechanism like the tcp window to
* understand that packets are not being acknowledged. But
* this allows the event loop to share out the work.
*/
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_RAW_WRITEABLE:
if (!sendlen)
break;
/*
* We can write directly on the UDP socket, specifying
* the peer the write is directed to.
*
* However the kernel may only accept parts of large sendto()s,
* leaving you to try to resend the remainder later. However
* depending on how your protocol on top of UDP works, that
* may involve sticking new headers before the remainder.
*
* For clarity partial sends just drop the remainder here.
*/
n = sendto(lws_get_socket_fd(wsi), sendbuf, sendlen, 0, &udp.sa,
udp.salen);
if (n < (ssize_t)len)
lwsl_notice("%s: send returned %d\n", __func__, (int)n);
break;
default:
break;
}
return 0;
}
static struct lws_protocols protocols[] = {
{ "raw-test", callback_raw_test, 0, 0 },
{ NULL, NULL, 0, 0 } /* terminator */
};
static int interrupted;
void sigint_handler(int sig)
{
interrupted = 1;
}
int main(int argc, char **argv)
{
struct lws_context_creation_info info;
struct lws_context *context;
struct lws_vhost *vhost;
int n = 0;
lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
/* for LLL_ verbosity above NOTICE to be built into lws,
* lws must have been configured and built with
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
/* | LLL_DEBUG */, NULL);
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
lwsl_user("LWS minimal raw adopt udp | nc -u 127.0.0.1 7681\n");
signal(SIGINT, sigint_handler);
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}
info.port = CONTEXT_PORT_NO_LISTEN_SERVER;
info.protocols = protocols;
vhost = lws_create_vhost(context, &info);
if (!vhost) {
lwsl_err("lws vhost creation failed\n");
goto bail;
}
/*
* Create our own "foreign" UDP socket bound to 7681/udp
*/
if (!lws_create_adopt_udp(vhost, 7681, LWS_CAUDP_BIND,
protocols[0].name, NULL)) {
lwsl_err("%s: foreign socket adoption failed\n", __func__);
goto bail;
}
while (n >= 0 && !interrupted)
n = lws_service(context, 1000);
bail:
lws_context_destroy(context);
return 0;
}