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:
parent
7cef6fcc7b
commit
d5bb8ecbc6
4 changed files with 297 additions and 0 deletions
|
@ -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
|
||||
|
||||
|
|
76
minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt
Normal file
76
minimal-examples/raw/minimal-raw-adopt-udp/CMakeLists.txt
Normal 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()
|
49
minimal-examples/raw/minimal-raw-adopt-udp/README.md
Normal file
49
minimal-examples/raw/minimal-raw-adopt-udp/README.md
Normal 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
|
||||
```
|
|
@ -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;
|
||||
}
|
Loading…
Add table
Reference in a new issue