英文:
My uvlib default loop quits unexpectedly - why?
问题
I am developing a program which watches a UDP port for a "discovery" packet. When it gets the packet, it echoes the packet to the sender and then should wait for further commands from sender. This is implemented using uvlib (based on uvlib documentation, example here: http://docs.libuv.org/en/v1.x/guide/networking.html#udp). I initialize a receive socket (to await the discovery packet), and a broadcast socket (for replying to discovery). When UDP data comes in, the on_UDP_read callback is triggered. In this callback, I get the caller's IP and port, and send the echo packet.
All this works fine (the sending system receives the echoed packet); the problem is that the main loop (uv_run(loop, UV_RUN_DEFAULT) is supposed to keep running after the reply... however, it exits with a return code of zero. I can't simply start it again in a (kludge) loop, because then a new port is selected each time by the sending system (standard UDP behavior), so I can't (later) send the bulk data I want to send.
The question: why does the main loop halt, just as if I had issued the uv_stop command?
I'm wondering if my use of memory has a problem. The send buffer is allocated in the function like so: const uv_buf_t a[] . (Maybe this has to be done using malloc??)
Here is the program:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <uv.h>
#include <string.h>
#include<arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>
#include <math.h>
#include <sys/socket.h>
#include <unistd.h>
#include <time.h>
#include "de_signals.h"
#define BUFFERLEN 65 // Maximum length of buffer
#define PORT 1024 // Port to watch
uv_loop_t *loop;
uv_udp_t send_socket;
uv_udp_t recv_socket;
//sem_t mutex;
//pthread_t t1;
int sockfd; // socket definition for UDP
struct sockaddr_in servaddr;
static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
buf->base = malloc(suggested_size);
buf->len = suggested_size;
}
//struct sockaddr_in si_DE, si_main;
void on_UDP_send(uv_udp_send_t* req, int status)
{
printf("UDP send complete, status = %d\n", status);
// the following is supposed to free the send buffer, but causes a segmentation fault
//free(req);
return;
}
void on_UDP_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) {
puts("UDP data detected");
if (nread < 0) {
fprintf(stderr, "Read error %s\n", uv_err_name(nread));
uv_close((uv_handle_t*) req, NULL);
free(buf->base);
return;
}
char sender[17] = { 0 };
uv_ip4_name((const struct sockaddr_in*) addr, sender, 16);
fprintf(stderr, "Recv from %s\n", sender);
struct sockaddr_in *sin = (struct sockaddr_in *) addr;
char ip[INET_ADDRSTRLEN];
uint16_t port;
port = htons (sin->sin_port);
printf("port = %d \n",port);
int sentBytes;
char replybuf[60];
uv_ip4_name((const struct sockaddr_in*) addr, sender, 16);
fprintf(stderr, "Recv from %s\n", sender);
for(int i=0;i<nread;i++){fprintf(stderr,"%02X ",buf->base[i]);}
fprintf(stderr,"\n");
puts("create discovery reply buf");
char b[60];
for(int i=0;i<60;i++) {b[i] = 0;}
if((buf->base[0] & 0xFF) == 0xEF && (buf->base[1] & 0xFF) == 0xFE) {
fprintf(stderr,"discovery packet detected\n");
b[0] = 0xEF;
b[1] = 0xFE;
b[2] = 0x02;
}
if((buf->base[0] & 0xFF) == 0x53 && (buf->base[1] & 0xFF) == 0x3F) {
fprintf(stderr, "STATUS INQUIRY detected\n");
b[0] = 0x4F;
b[1] = 0x4B;
port = 1024; // temporary, until we have a better way
sleep(1); // let LH get socket open
}
////////////// reply ///////////////
uv_udp_send_t send_req;
puts("create discovery reply buf");
const uv_buf_t a[]={{.base = b, .len=60}};
for(int i=0;i<60;i++){fprintf(stderr,"%02X ",a[0].base[i]);};
fprintf(stderr,"\n");
puts("ready to send");
// Here we reply to sender, using sender's IP and port
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
struct hostent* hptr = gethostbyname(sender);
if(!hptr) puts ("gethostbyname error");
servaddr.sin_addr.s_addr = //INADDR_ANY;
((struct in_addr*) hptr->h_addr_list[0])->s_addr;
uv_udp_send(&send_req, &send_socket, a, 1, (const struct sockaddr *)&servaddr,
on_UDP_send);
puts("reply issued");
// free(buf->base);
uv_udp_recv_stop(req); // ???
puts("returning from processing UDP packet");
return;
}
/////////////////////////////////////////////////////////////////////////////////
int main() {
int retcode;
//while(1==1) // this is a kludgy extra loop until we find out why uv_run halts.
//{
puts("starting");
loop = uv_default_loop();
uv_udp_init(loop, &recv_socket);
struct sockaddr_in recv_addr;
uv_ip4_addr("0.0.0.0", 1024, &recv_addr);
uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
uv_udp_recv_start(&recv_socket, alloc_buffer, on_UDP_read);
uv_udp_init(loop, &send_socket);
struct sockaddr_in broadcast_addr;
uv_ip4_addr("0.0.0.0", 0, &broadcast_addr);
uv_udp_bind(&send_socket, (const struct sockaddr *)&broadcast_addr, 0);
uv_udp_set_broadcast(&send_socket, 1); puts("wait for UDP handshake\n");
// Creating socket file descriptor for UDP
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
puts("start main loop");
retcode = uv_run(loop, UV_RUN_DEFAULT
<details>
<summary>英文:</summary>
I am developing a program which watches a UDP port for a "discovery" packet. When it gets the packet, it echoes the packet to the sender and then should wait for further commands from sender.
This is implemented using uvlib (based on uvlib documentation, example here: <http://docs.libuv.org/en/v1.x/guide/networking.html#udp> ). I initialize a receive socket (to await the discovery packet), and a broadcast socket (for replying to discovery). When UDP data comes in, the on_UDP_read callback is triggered. In this callback, I get the caller's IP and port, and send the echo packet.
All this works fine (the sending system receives the echoed packet); the problem is that the main loop (uv_run(loop, UV_RUN_DEFAULT) is supposed to keep running after the reply... however, it exits with a return code of zero. I can't simply start it again in a (kludge) loop, because then a new port is selected each time by the sending system (standard UDP behavior), so I can't (later) send the bulk data I want to send.
**The question:** why does the main loop halt, just as if I had issued the uv_stop command?
I'm wondering if my use of memory has a problem. The send buffer is allocated in the function like so: const uv_buf_t a[] . (Maybe this has to be done using malloc??)
Here is the program:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <uv.h>
#include <string.h>
#include<arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>
#include <math.h>
#include <sys/socket.h>
#include <unistd.h>
#include <time.h>
#include "de_signals.h"
#define BUFFERLEN 65 // Maximum length of buffer
#define PORT 1024 // Port to watch
uv_loop_t *loop;
uv_udp_t send_socket;
uv_udp_t recv_socket;
//sem_t mutex;
//pthread_t t1;
int sockfd; // socket definition for UDP
struct sockaddr_in servaddr;
static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
buf->base = malloc(suggested_size);
buf->len = suggested_size;
}
//struct sockaddr_in si_DE, si_main;
void on_UDP_send(uv_udp_send_t* req, int status)
{
printf("UDP send complete, status = %d\n", status);
// the following is supposed to free the send buffer, but causes a segmentation fault
//free(req);
return;
}
void on_UDP_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr addr, unsigned flags) {
puts("UDP data detected");
if (nread < 0) {
fprintf(stderr, "Read error %s\n", uv_err_name(nread));
uv_close((uv_handle_t) req, NULL);
free(buf->base);
return;
}
char sender[17] = { 0 };
uv_ip4_name((const struct sockaddr_in*) addr, sender, 16);
fprintf(stderr, "Recv from %s\n", sender);
struct sockaddr_in *sin = (struct sockaddr_in *) addr;
char ip[INET_ADDRSTRLEN];
uint16_t port;
port = htons (sin->sin_port);
printf("port = %d \n",port);
int sentBytes;
char replybuf[60];
uv_ip4_name((const struct sockaddr_in*) addr, sender, 16);
fprintf(stderr, "Recv from %s\n", sender);
for(int i=0;i<nread;i++){fprintf(stderr,"%02X ",buf->base[i]);}
fprintf(stderr,"\n");
puts("create discovery reply buf");
char b[60];
for(int i=0;i<60;i++) {b[i] = 0;}
if((buf->base[0] & 0xFF) == 0xEF && (buf->base[1] & 0xFF) == 0xFE) {
fprintf(stderr,"discovery packet detected\n");
b[0] = 0xEF;
b[1] = 0xFE;
b[2] = 0x02;
}
if((buf->base[0] & 0xFF) == 0x53 && (buf->base[1] & 0xFF) == 0x3F) {
fprintf(stderr, "STATUS INQUIRY detected\n");
b[0] = 0x4F;
b[1] = 0x4B;
port = 1024; // temporary, until we have a better way
sleep(1); // let LH get socket open
}
////////////// reply ///////////////
uv_udp_send_t send_req;
puts("create discovery reply buf");
const uv_buf_t a[]={{.base = b, .len=60}};
for(int i=0;i<60;i++){fprintf(stderr,"%02X ",a[0].base[i]);};
fprintf(stderr,"\n");
puts("ready to send");
// Here we reply to sender, using sender's IP and port
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
struct hostent* hptr = gethostbyname(sender);
if(!hptr) puts ("gethostbyname error");
servaddr.sin_addr.s_addr = //INADDR_ANY;
((struct in_addr*) hptr->h_addr_list[0])->s_addr;
uv_udp_send(&send_req, &send_socket, a, 1, (const struct sockaddr *)&servaddr,
on_UDP_send);
puts("reply issued");
// free(buf->base);
uv_udp_recv_stop(req); // ???
puts("returning from processing UDP packet");
return;
}
/////////////////////////////////////////////////////////////////////////////////
int main() {
int retcode;
//while(1==1) // this is a kludgy extra loop until we find out why uv_run halts.
//{
puts("starting");
loop = uv_default_loop();
uv_udp_init(loop, &recv_socket);
struct sockaddr_in recv_addr;
uv_ip4_addr("0.0.0.0", 1024, &recv_addr);
uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
uv_udp_recv_start(&recv_socket, alloc_buffer, on_UDP_read);
uv_udp_init(loop, &send_socket);
struct sockaddr_in broadcast_addr;
uv_ip4_addr("0.0.0.0", 0, &broadcast_addr);
uv_udp_bind(&send_socket, (const struct sockaddr *)&broadcast_addr, 0);
uv_udp_set_broadcast(&send_socket, 1); puts("wait for UDP handshake\n");
// Creating socket file descriptor for UDP
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
puts("start main loop");
retcode = uv_run(loop, UV_RUN_DEFAULT);
fprintf(stderr,"Return code from uv_run loop = %d \n",retcode);
//}
puts("out of main loop");
return retcode;
}
</details>
# 答案1
**得分**: 1
I figured out part of this. 首先,“uvlib”不是包的名称;“libuv”才是。其次,循环停止的原因是因为在on_read回调函数的末尾有一个uv_udp_recv_stop(req)指令。当没有更多的工作要做时,libuv会退出。
剩余问题:如果删除uv_udp_recv_stop(req)指令,以期望可以使循环继续运行(应该是这样的),程序会崩溃并显示分段错误。我向libuv团队报告了这个问题(使用Github问题报告),但他们并没有提供帮助,因为他们对其在Github libuv存储库中发布的示例程序的正确性不承担任何责任。有趣的是,当您安装libuv并运行检查(即运行它们的测试)时,与UDP组播相关的测试都失败了。这使我认为libuv包在UDP领域确实存在一些错误;我报告了这个问题,但我猜libuv团队有更大的问题要解决。
<details>
<summary>英文:</summary>
I figured out part of this. First of all, "uvlib" is not the name of the package; "libuv" is. Second, the reason the loop quits is because there is a uv_udp_recv_stop(req) instruction at the end of the on_read callback routine. libuv exits when there is no more work to do.
REMAINING PROBLEM: if you remove the uv_udp_recv_stop(req) instruction with the idea that maybe that will leave the loop running (as it should), the program crashes with a Segmentation Fault. I reported this to the libuv team (using Github issue reporting), and they were not helpful, because they disclaim any responsibility for the correctness of the example programs posted in their libuv repository in github. Interestingly, when you install libuv and run a check (i.e., run their tests), the tests related to UDP multicast all fail. This suggests to me that the libuv package does indeed have some bugs in the UDP area; I reported this, but I guess the libuv team has bigger fish to fry.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论