On Tuesday, November 7, 2017 at 12:55:47 AM UTC-8, Ben Noordhuis wrote:
>
> On Tue, Nov 7, 2017 at 2:23 AM, Russell Haley <[email protected] 
> <javascript:>> wrote: 
> > On Monday, November 6, 2017 at 12:14:18 PM UTC-8, Russell Haley wrote: 
> >> On Monday, November 6, 2017 at 11:49:43 AM UTC-8, Ben Noordhuis wrote: 
> >>> 
> >>> On Mon, Nov 6, 2017 at 7:56 PM, Russell Haley <[email protected]> 
> wrote: 
> >>> > Hello, 
> >>> > 
> >>> > I'm new to C patterns and I am experimenting with libuv to write a 
> >>> > simulator. The idea is to create a context struct to be passed 
> around 
> >>> > as a 
> >>> > pointer/handle to various timed and event based callback functions. 
> I 
> >>> > would 
> >>> > like this simulator to have a "heartbeat" that sends UDP every x 
> >>> > period, as 
> >>> > well as respond to UDP messages received on a different port. I 
> would 
> >>> > like 
> >>> > the system to decides at runtime if there are flags in the context 
> that 
> >>> > block it from sending (i.e. handle->send=0;). 
> >>> > 
> >>> > For bonus points, I would like to be able to start a TCP based 
> module 
> >>> > if I 
> >>> > receive a certain message on the UDP receive port and then stop it 
> once 
> >>> > the 
> >>> > routine completes. I am hoping I can just drop a routine on the 
> default 
> >>> > loop 
> >>> > (from within my uv_udp_recv_cb) for processing until completion 
> which 
> >>> > would 
> >>> > be signaled in a TCP callback (haven't delved into the TCP stuff 
> >>> > yet...). 
> >>> > 
> >>> > After about 1 hour of searching I haven't found any examples of what 
> I 
> >>> > am 
> >>> > trying to do. Most of the example patterns are very simplistic. This 
> >>> > dearth 
> >>> > of information leads me to one of two conclusions: 1) I am trying to 
> do 
> >>> > something the wrong way or 2) nobody in the entirety of using libuv 
> has 
> >>> > ever 
> >>> > ever ever tried to do what I am attempting now. I assume the problem 
> is 
> >>> > the 
> >>> > former. ;) 
> >>> > 
> >>> > What I have so far: 
> >>> > 
> >>> > - An example udp listener from the docs. It listens faithfully on 
> the 
> >>> > port I 
> >>> > assign. 
> >>> > - A udp client, again from the docs. My first kick simply sends a 
> >>> > message 
> >>> > and then exists . My next planned attempt was to use a timer to send 
> >>> > the 
> >>> > information repeatedly. I have realized that the timer callback 
> needs 
> >>> > to 
> >>> > have my context handle, which prompted this question. I will finish 
> my 
> >>> > example code and then post it here while awaiting a possible answer. 
> >>> > 
> >>> > Please let me know if I can provide some more background (context? 
> tee 
> >>> > hee). 
> >>> > 
> >>> > Regards, 
> >>> > 
> >>> > Russ 
> >>> 
> >>> It's not entirely clear to me what you want to accomplish but if you 
> >>> want to attach state/context to a handle or request, you can use the 
> >>> handle->data and req->data fields. or embed them in another struct and 
> >>> use container_of() to look up the embedder.  You can find a 
> >>> container_of() definition in the libuv sources. 
> >> 
> >> 
> >> Thanks so much for responding. I just stumbled over your answer in uv.h 
> >> (described in UV_HANDLE_FIELDS) and then found it in the documentation. 
> I 
> >> missed the context section at the bottom of the basics page.  :( 
> >> 
> >> Thanks for the answer and sorry for the noise! 
> >> 
> >> Russ 
> > 
> > 
> > Okay, a little more noise. I'm getting a segmentation fault in 
> libuv.so.1 
> > that I can't seem to debug (unsurprising with my limited gdb skills). 
> The 
> > following code is my udp client. It's supposed to: 
> > 1) Set up the context in main() 
> > 2) Start a timer to tick once, then once per second. 
> > 3) On each tick, send a udp message and augment the counter. 
> > 4) If the counter is 10, stop. 
> > 
> > udp-spammer.c: 
> > 
> > #include <unistd.h> 
> > #include <stdio.h> 
> > #include <uv.h> 
> > #include <stdlib.h> 
> > #include <string.h> 
> > 
> > #define UDP_SEND_PORT           49284 
> > #define UDP_REC_PORT            49280 
> > 
> > #define VMDS_ADDR "127.0.0.1" 
> > #define TRU_ADDR "127.0.0.1" 
> > #define ON 1 
> > #define OFF 0 
> > 
> > typedef struct context_t { 
> >     uv_udp_t* send_ctx; 
> >     int count; 
> >     int send; 
> >     uv_buf_t* message; 
> > }context_t; 
> > 
> > 
> > static void s_alloc_cb(uv_handle_t* handle, size_t suggested_size, 
> uv_buf_t* 
> > buf) { 
> >   buf->base = malloc(suggested_size); 
> >   buf->len = suggested_size; 
> > } 
> > 
> > void on_send (uv_udp_send_t* req, int status) 
> > { 
> >     printf("Status: %d\n",status); 
> > } 
> > 
> > uv_buf_t make_sd_msg(char* msg) 
> > { 
> >     uv_buf_t buf = uv_buf_init(msg, strlen(msg)); 
> >     return buf; 
> > } 
> > 
> > void spam(uv_timer_t* h) 
> > { 
> >     context_t* ctx = (context_t*)h->data; 
> >     if(ctx == NULL) 
> >     { 
> >         printf("Esplode!"); 
> >         exit(1); 
> >     } 
> > 
> >     struct sockaddr_in broadcast_addr; 
> >     uv_ip4_addr(VMDS_ADDR, 0, &broadcast_addr); 
> >     uv_udp_bind(ctx->send_ctx, (const struct sockaddr *)&broadcast_addr, 
> 0); 
> > 
> >     uv_udp_send_t send_req; 
> > 
> >     struct sockaddr_in remote_addr; 
> >     uv_ip4_addr(TRU_ADDR, UDP_REC_PORT, &remote_addr); 
> >     uv_udp_send(&send_req, ctx->send_ctx, ctx->message, 1, (const struct 
> > sockaddr *)&remote_addr, on_send); 
> > 
> >     ctx->count++; 
> >     if(ctx->count >= 10) 
> >     { 
> >         uv_timer_stop(h); 
> >     } 
> > } 
> > 
> > int main() { 
> > 
> >     uv_loop_t *loop; 
> >     uv_timer_t timed; 
> >     context_t ctx; 
> > 
> >     loop = uv_default_loop(); 
> > 
> >     ctx.send = 1; 
> >     ctx.count = 0; 
> >     uv_buf_t msg =  make_sd_msg("Hello Lucy!\n\0"); 
> >     ctx.message = &msg; 
> >     uv_udp_t snd; 
> >     uv_udp_init(loop, &snd); 
> >     ctx.send_ctx = &snd; 
> > 
> >     timed.data = &ctx; 
> > 
> >     uv_timer_init(loop, &timed); 
> >     uv_timer_start(&timed, spam, 0,1000); 
> > 
> >     return uv_run(loop, UV_RUN_DEFAULT); 
> > } 
> > 
> > 
> > 
> > So far, it sends the first UDP message and then I get a SIGSEV in 
> > libuv.so.1. bt isn't telling me much that I can use: 
> > 
> > 0xb7fad4bd in ?? () from /usr/lib/i386-linux-gnu/libuv.so.1 
> > (gdb) bt 
> > #0  0xb7fad4bd in ?? () from /usr/lib/i386-linux-gnu/libuv.so.1 
> > #1  0xb7fad852 in ?? () from /usr/lib/i386-linux-gnu/libuv.so.1 
> > #2  0xb7fa0be9 in uv_run () from /usr/lib/i386-linux-gnu/libuv.so.1 
> > #3  0x08048ad4 in main () at udp-spammer.c:86 
> > 
> > Any help would be grand. 
> > 
> > Thanks! 
> > Russ 
>
> The `uv_udp_t send_req` in spam() is stack-allocated.  It goes out of 
> scope and gets clobbered the moment that function returns.  Allocate 
> it statically or on the heap instead. 
>
> Build libuv from source if you want debug symbols, or check if your 
> distro has a libuv-dbg package or installs debug symbols separately 
> (e.g, debuginfo-install on Fedora.) 
>

Thanks for your help Ben. Here is the working code: 

#include <unistd.h>
#include <stdio.h>
#include <uv.h>
#include <stdlib.h>
#include <string.h>

#define UDP_SEND_PORT           49284
#define UDP_REC_PORT            49280

#define VMDS_ADDR "127.0.0.1"
#define TRU_ADDR "127.0.0.1"
#define ON 1
#define OFF 0

typedef struct context_t {
    uv_udp_t* send_ctx;
    uv_udp_send_t* send_req;
    int count;
    int send;
    uv_buf_t* message;
}context_t;


static void s_alloc_cb(uv_handle_t* handle, size_t suggested_size, 
uv_buf_t* buf) {
  buf->base = malloc(suggested_size);
  buf->len = suggested_size;
}

void on_send (uv_udp_send_t* req, int status)
{
    printf("Status: %d\n",status);
}

uv_buf_t make_sd_msg(char* msg, size_t len)
{
    uv_buf_t buf = uv_buf_init(msg, len);
    return buf;
}

void spam(uv_timer_t* h)
{
    context_t* ctx = (context_t*)h->data;
    if(ctx == NULL)
    {
        printf("Esplode!");
        exit(1);
    }

    if(ctx->count == 10)
    {
        //free(ctx->message);
        char* gn = "Good Night!";
        *ctx->message = make_sd_msg(gn,strlen(gn));

    }
    else if(ctx->count == 11)
    {
        uv_timer_stop(h);
        return;
    }

    struct sockaddr_in broadcast_addr;
    uv_ip4_addr(VMDS_ADDR, 0, &broadcast_addr);
    uv_udp_bind(ctx->send_ctx, (const struct sockaddr *)&broadcast_addr, 0);

    struct sockaddr_in remote_addr;
    uv_ip4_addr(TRU_ADDR, UDP_REC_PORT, &remote_addr);
    uv_udp_send(ctx->send_req, ctx->send_ctx, ctx->message, 1, (const 
struct sockaddr *)&remote_addr, on_send);

    ctx->count++;
}

int main() {
    int res = -1;
    int restart = 1;
    uv_loop_t *loop;
    uv_timer_t timed;
    context_t ctx;
    uv_udp_t udp;
    uv_udp_send_t send_req;

    loop = uv_default_loop();

    ctx.send = 1;
    ctx.count = 0;
    char* msg = "Hello Sally!\n\0";
    uv_buf_t mbuf =  make_sd_msg(msg, strlen(msg));
    ctx.message = &mbuf;

    uv_udp_init(loop, &udp);
    ctx.send_ctx = &udp;
    ctx.send_req = &send_req;
    timed.data = &ctx;

    uv_timer_init(loop, &timed);
    uv_timer_start(&timed, spam, 0,1000);

    res = uv_run(loop, UV_RUN_DEFAULT);

    if(restart)
    {
        ctx.count = 0;
        char* msg = "Hello Sally!\n\0";
        uv_buf_t mbuf =  make_sd_msg(msg, strlen(msg));
        ctx.message = &mbuf;
    }
    uv_timer_start(&timed, spam, 0,1000);
    res = uv_run(loop, UV_RUN_DEFAULT);

    return res;
}

and the server side code:

#include <unistd.h>
#include <stdio.h>
#include "uv.h"
#include <stdlib.h>

#define UDP_SEND_PORT           49284   
#define UDP_REC_PORT            49280  

#define VMDS_ADDR "127.0.0.1"
#define ON 1
#define OFF 0

uv_loop_t *loop;
uv_udp_t send_socket;
uv_udp_t recv_socket;

void on_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const 
struct sockaddr *addr, unsigned flags) {
    if (nread < 0) {
        fprintf(stderr, "Read error %s\n", uv_err_name(nread));
        uv_close((uv_handle_t*) req, NULL);
        free(buf->base);
        return;
    }
    else if(nread == 0)
    {
    fprintf(stderr,"Zero bytes read.");
    return;
    }
    char sender[17] = { 0 };
    uv_ip4_name((const struct sockaddr_in*) addr, sender, 16);
    fprintf(stderr, "Recv from %s\n", sender);
    printf("Bytes read: %d\n",nread);
    printf("%s\n",buf->base);

    free(buf->base);
}

static void s_alloc_cb(uv_handle_t* handle, size_t suggested_size, 
uv_buf_t* buf) {
  buf->base = malloc(suggested_size);
  buf->len = suggested_size;
}

void on_send (uv_udp_send_t* req, int status)
{

}

uv_buf_t make_sd_msg(char* msg)
{
    uv_buf_t buf = uv_buf_init(msg, sizeof(msg));
    return buf;
}

int stop()
{
    //uv_udp_recv_stop(req);
}

int main() {
    loop = uv_default_loop();
    uv_udp_init(loop, &recv_socket);
    struct sockaddr_in recv_addr;

    uv_ip4_addr(VMDS_ADDR, UDP_REC_PORT, &recv_addr);
    uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, 
UV_UDP_REUSEADDR);
    uv_udp_recv_start(&recv_socket, s_alloc_cb, on_read);

    return uv_run(loop, UV_RUN_DEFAULT);
}

and a generic makefile that worked for both client and server (changing the 
module name). I removed my paths so not sure if it still works.

src = $(wildcard *.c)
obj = $(src:.c=.o)

MODULE = uv-client


CFLAGS = -ggdb 
LDFLAGS = -luv

$(MODULE): $(obj)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

.PHONY: clean
clean:
rm -f $(obj) $(MODULE)

-- 
You received this message because you are subscribed to the Google Groups 
"libuv" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/libuv.
For more options, visit https://groups.google.com/d/optout.

Reply via email to