From fd729b32a8966291f007567f72f3dc0158bdab35 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Mon, 5 Oct 2015 11:47:51 +0800 Subject: [PATCH] Added support for arbitrary datagram sizes. The maximum size is still constant per UDP object, but the size can be speficied at creation time. --- doc/udp.html | 34 ++++++++++++++++++++++++------ src/udp.c | 58 +++++++++++++++++++++++++++++++++------------------- src/udp.h | 5 +++-- 3 files changed, 68 insertions(+), 29 deletions(-) diff --git a/doc/udp.html b/doc/udp.html index a300f2f..22d7c72 100644 --- a/doc/udp.html +++ b/doc/udp.html @@ -42,7 +42,7 @@

-socket.udp() +socket.udp([buffersize])

@@ -62,6 +62,13 @@ The setpeername is used to connect the object.

+

+The optional buffersize parameter +specifies the size of the largest datagram that will +ever be received by the UDP object. The default value is +8192. +

+

In case of success, a new unconnected UDP object returned. In case of error, nil is returned, followed by @@ -85,7 +92,7 @@ href=#setoption>setoption will fail.

-socket.udp4() +socket.udp4([buffersize])

@@ -105,6 +112,13 @@ The setpeername is used to connect the object.

+

+The optional buffersize parameter +specifies the size of the largest datagram that will +ever be received by the UDP object. The default value is +8192. +

+

In case of success, a new unconnected UDP object returned. In case of error, nil is returned, followed by @@ -114,7 +128,7 @@ an error message.

-socket.udp6() +socket.udp6([buffersize])

@@ -134,6 +148,13 @@ The setpeername is used to connect the object.

+

+The optional buffersize parameter +specifies the size of the largest datagram that will +ever be received by the UDP object. The default value is +8192. +

+

In case of success, a new unconnected UDP object returned. In case of error, nil is returned, followed by @@ -238,9 +259,10 @@ specifies the maximum size of the datagram to be retrieved. If there are more than size bytes available in the datagram, the excess bytes are discarded. If there are less then size bytes available in the current datagram, the -available bytes are returned. If size is omitted, the -maximum datagram size is used (which is currently limited by the -implementation to 8192 bytes). +available bytes are returned. +If size is omitted, the +buffersize argument at creation time is used +(which defaults to 8192 bytes).

diff --git a/src/udp.c b/src/udp.c index 17d932a..9c27b60 100644 --- a/src/udp.c +++ b/src/udp.c @@ -41,6 +41,7 @@ static int meth_setpeername(lua_State *L); static int meth_close(lua_State *L); static int meth_setoption(lua_State *L); static int meth_getoption(lua_State *L); +static int meth_getbufferlength(lua_State *L); static int meth_settimeout(lua_State *L); static int meth_getfd(lua_State *L); static int meth_setfd(lua_State *L); @@ -63,6 +64,7 @@ static luaL_Reg udp_methods[] = { {"setfd", meth_setfd}, {"setoption", meth_setoption}, {"getoption", meth_getoption}, + {"getoption", meth_getbufferlength}, {"setpeername", meth_setpeername}, {"setsockname", meth_setsockname}, {"settimeout", meth_settimeout}, @@ -202,47 +204,55 @@ static int meth_sendto(lua_State *L) { \*-------------------------------------------------------------------------*/ static int meth_receive(lua_State *L) { p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); - char buffer[UDP_DATAGRAMSIZE]; - size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); + char buf[UDP_DATAGRAMSIZE]; + size_t len = MAX(udp->len, UDP_DATAGRAMSIZE); + char *dgram = len > sizeof(buf)? udp->buf: buf; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, len); int err; p_timeout tm = &udp->tm; - count = MIN(count, sizeof(buffer)); timeout_markstart(tm); - err = socket_recv(&udp->sock, buffer, count, &got, tm); + wanted = MIN(wanted, len); + err = socket_recv(&udp->sock, dgram, wanted, &got, tm); /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ - if (err == IO_CLOSED) - err = IO_DONE; - if (err != IO_DONE) { + if (err != IO_DONE && err != IO_CLOSED ) { lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); return 2; } - lua_pushlstring(L, buffer, got); + lua_pushlstring(L, dgram, got); + return 1; +} + +/*-------------------------------------------------------------------------*\ +* Receives data from a UDP socket +\*-------------------------------------------------------------------------*/ +static int meth_getbufferlength(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); + lua_pushinteger(L, MAX(UDP_DATAGRAMSIZE, udp->len)); return 1; } /*-------------------------------------------------------------------------*\ * Receives data and sender from a UDP socket \*-------------------------------------------------------------------------*/ -static int meth_receivefrom(lua_State *L) -{ +static int meth_receivefrom(lua_State *L) { p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); - char buffer[UDP_DATAGRAMSIZE]; - size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); - int err; - p_timeout tm = &udp->tm; + char buf[UDP_DATAGRAMSIZE]; + size_t len = MAX(udp->len, UDP_DATAGRAMSIZE); + char *dgram = len > sizeof(buf)? udp->buf: buf; + size_t got, wanted = (size_t) luaL_optnumber(L, 2, len); struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); char addrstr[INET6_ADDRSTRLEN]; char portstr[6]; + int err; + p_timeout tm = &udp->tm; timeout_markstart(tm); - count = MIN(count, sizeof(buffer)); - err = socket_recvfrom(&udp->sock, buffer, count, &got, (SA *) &addr, + wanted = MIN(wanted, len); + err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr, &addr_len, tm); /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ - if (err == IO_CLOSED) - err = IO_DONE; - if (err != IO_DONE) { + if (err != IO_DONE && err != IO_CLOSED) { lua_pushnil(L); lua_pushstring(L, udp_strerror(err)); return 2; @@ -254,7 +264,7 @@ static int meth_receivefrom(lua_State *L) lua_pushstring(L, gai_strerror(err)); return 2; } - lua_pushlstring(L, buffer, got); + lua_pushlstring(L, dgram, got); lua_pushstring(L, addrstr); lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10)); return 3; @@ -409,13 +419,19 @@ static int meth_setsockname(lua_State *L) { * Creates a master udp object \*-------------------------------------------------------------------------*/ static int udp_create(lua_State *L, int family) { + p_udp udp = NULL; + /* optional length for private datagram buffer. this is useful when + * you need larger datagrams than UDP_DATAGRAMSIZE */ + size_t len = (size_t) luaL_optinteger(L, 1, 0); + if (len <= UDP_DATAGRAMSIZE) len = 0; /* allocate udp object */ - p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); + udp = (p_udp) lua_newuserdata(L, sizeof(t_udp) + len - 1); auxiliar_setclass(L, "udp{unconnected}", -1); /* if family is AF_UNSPEC, we leave the socket invalid and * store AF_UNSPEC into family. This will allow it to later be * replaced with an AF_INET6 or AF_INET socket upon first use. */ udp->sock = SOCKET_INVALID; + udp->len = len; timeout_init(&udp->tm, -1, -1); udp->family = family; if (family != AF_UNSPEC) { diff --git a/src/udp.h b/src/udp.h index 2b831a5..da27a7a 100644 --- a/src/udp.h +++ b/src/udp.h @@ -8,7 +8,7 @@ * (AF_INET, SOCK_DGRAM). * * Two classes are defined: connected and unconnected. UDP objects are -* originally unconnected. They can be "connected" to a given address +* originally unconnected. They can be "connected" to a given address * with a call to the setpeername function. The same function can be used to * break the connection. \*=========================================================================*/ @@ -17,13 +17,14 @@ #include "timeout.h" #include "socket.h" -/* can't be larger than wsocket.c MAXCHUNK!!! */ #define UDP_DATAGRAMSIZE 8192 typedef struct t_udp_ { t_socket sock; t_timeout tm; int family; + size_t len; /* length of datagram buffer below */ + char buf[1]; /* allocate larger structure to hold actual buffer */ } t_udp; typedef t_udp *p_udp;