[erlang-questions] inet_res:getbyname/2 and udp:connect/3
Tony Rogvall
tony@REDACTED
Thu Jun 17 13:15:15 CEST 2010
On 17 jun 2010, at 00.33, Per Hedeland wrote:
> Tony Rogvall <tony@REDACTED> wrote:
>> Cool!
>> send is failing on subsequent send, but recv is not ???? This is strange
>> times.
>
> Erlang gen_udp:recv() is not failing.
No, I meant it returned ok instead of {error, econnrefused}
>
>> Do you think this behavior is documented anywhere?
>
> 'man inet_drv' maybe?
>
> man inet_drv
No manual entry for inet_drv
I was thinking in terms of how to get connected UDP working on more than one platform.
I have been testing a similar C program (as the one you write) on Linux and Darwin and think it is a bit confusing
to say the least.
If I test poll under Darwin (10.3.0) it will timeout, I have set the timeout to 5s and the timeout is early. So it is clearly a bug.
./cudp
poll: timeout (too early)
poll: revents=0
(poll under Darwin exist but is not very useful since it can only be used for sockets and pipes, also buggy ;-)
if I run select under Darwin it will work as intended. BTW select is normally chosen as poll method for Darwin.
./cudp
select: readable
read:select:: Connection refused
under Linux (2.6.32-22) select is also working.
./cudp
select: readable
read:select:: Connection refused
Poll is also working but return only POLLERR in revents.
./cudp
poll: revents=8 (POLLERR)
read:poll:: Connection refused
This may be the problem, I have not read erl_check_io.c in depth but someone may have ;-)
>> A small C program may clear this issue for us ?
>
> A small C program (below) works perfectly:
>
> $ ./conn_udp
> recv: Connection refused
>
> I might point out that the "connect UDP socket in resolver code" trick
> was invented in BSD code at a point in time when not much else even
> *had* TCP/IP.:-)
>
> But inet_drv is "slightly" more complex than that program...
>
>> To get the {error, econnrefused} speed things up when trying servers
>> where DNS servers may have crashed.
>
> Yes, but it's not DNS-specific of course, it can be useful for any
> UDP-based protocol.
>
>> But in the general case, server may be down/burned, routers is
>> down/corrupt, this will not help since no one will send
>> the ICMP reply message on the network.
>
> True - and add to that misconfigured firewalls that drop all ICMP
> packets and there is probably not much usefulness left. Maybe just as a
> way to quickly find out if you are running a local server.
>
> --Per
>
> conn_udp.c---------------------------------------------
>
> #include <sys/types.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
> #include <arpa/inet.h>
> #include <stdlib.h>
> #include <stdio.h>
>
> int error(char *str)
> {
> perror(str);
> exit(1);
> }
>
> int main()
> {
> struct sockaddr_in addr;
> int fd;
> char buf[1];
>
> addr.sin_addr.s_addr = inet_addr("127.0.0.1");
> addr.sin_family = AF_INET;
> addr.sin_port = htons(12345);
> buf[0] = 'x';
> if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
> error("socket");
> if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
> error("connect");
> if (send(fd, buf, sizeof(buf), 0) < 0)
> error("send");
> if (recv(fd, buf, sizeof(buf), 0) < 0)
> error("recv");
>
> return 0;
> }
cudp.c ----------------------------------------
//
// Connected UDP test
//
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
// #define USE_SELECT
// #define USE_POLL
#ifdef USE_SELECT
#include <sys/select.h>
#endif
#ifdef USE_POLL
#include <poll.h>
#endif
#include <netinet/in.h>
#define TIMEOUT 5000
#define LOCAL_PORT 0
#define LOCAL_ADDR INADDR_ANY
#define REMOTE_PORT 53
#define REMOTE_ADDR INADDR_LOOPBACK
main()
{
struct sockaddr_in laddr;
struct sockaddr_in raddr;
int s;
socklen_t len;
char obuf[3];
char ibuf[3];
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
laddr.sin_family = AF_INET;
laddr.sin_port = htons(LOCAL_PORT);
laddr.sin_addr.s_addr = htonl(LOCAL_ADDR);
len = sizeof(laddr);
if (bind(s, (struct sockaddr*) &laddr, len) < 0) {
perror("bind");
exit(1);
}
raddr.sin_family = AF_INET;
raddr.sin_port = htons(REMOTE_PORT);
raddr.sin_addr.s_addr = htonl(REMOTE_ADDR);
len = sizeof(raddr);
if (connect(s, (struct sockaddr*) &raddr, len) < 0) {
perror("bind");
exit(1);
}
obuf[0] = 1;
obuf[1] = 2;
obuf[2] = 3;
if (write(s, obuf, 3) < 0) {
perror("write");
exit(1);
}
#ifdef USE_POLL
{
int n;
struct pollfd fds[1];
fds[0].fd = s;
fds[0].events = POLLIN; // |POLLOUT;
fds[0].revents = 0;
if ((n=poll(fds, 1, TIMEOUT)) < 0) {
perror("poll");
exit(1);
}
if (n == 0) {
fprintf(stderr, "poll: timeout\n");
fprintf(stderr, "poll: revents=%x\n", fds[0].revents);
exit(1);
}
if (fds[0].revents & (POLLIN|POLLOUT|POLLERR)) {
fprintf(stderr, "poll: revents=%x\n", fds[0].revents);
if (read(s, ibuf, 3) < 0) {
perror("read:poll:");
exit(1);
}
}
else {
fprintf(stderr, "poll: no event\n");
exit(1);
}
}
#elif defined(USE_SELECT)
{
int n;
fd_set readfds;
fd_set writefds;
fd_set errorfds;
struct timeval timeout;
FD_ZERO(&readfds);
FD_SET(s, &readfds);
FD_ZERO(&writefds);
// FD_SET(s, &writefds);
FD_ZERO(&errorfds);
FD_SET(s, &errorfds);
timeout.tv_sec = TIMEOUT / 1000;
timeout.tv_usec = (TIMEOUT % 1000)*1000;
if (select(s+1, &readfds, &writefds, &errorfds, &timeout) < 0) {
perror("select");
exit(1);
}
if (n == 0) {
fprintf(stderr, "select: timeout\n");
exit(1);
}
if (FD_ISSET(s, &readfds))
fprintf(stderr, "select: readable\n");
if (FD_ISSET(s, &writefds))
fprintf(stderr, "select: writable\n");
if (FD_ISSET(s, &errorfds))
fprintf(stderr, "select: error\n");
if (FD_ISSET(s, &readfds)) {
if (read(s, ibuf, 3) < 0) {
perror("read:select:");
exit(1);
}
}
else {
fprintf(stderr, "select: no event\n");
exit(1);
}
}
#else
if (read(s, ibuf, 3) < 0) {
perror("read");
exit(1);
}
#endif
close(s);
exit(0);
}
More information about the erlang-questions
mailing list