Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

udp and getpeer #223

Closed
hreintke opened this issue Feb 14, 2015 · 13 comments
Closed

udp and getpeer #223

hreintke opened this issue Feb 14, 2015 · 13 comments
Labels

Comments

@hreintke
Copy link

Used the dev branch to compile a version with getpeer() option.
Works OK for tcp connections but get error
"PANIC: unprotected error in call to Lua API (servertest1.lua:2: attempt to call method 'getpeer' (a nil value))"
when using it on an UDP server connection.

@hreintke
Copy link
Author

Have been investigating further on.
This is the lua file I am using :

function pr(c,pl)
  print ("parameter info c")
  print(type(c), " ",getmetatable(c));
  print ("metadatainfo c");  
  mt = getmetatable(c);
  for n,s in pairs(mt) do print(n," ",s)  end
end

if srv ~= nil then srv:close() end
if udpsrv ~= nil then udpsrv:close() end

counter=1

srv=net.createServer(net.TCP)
srv:listen(80,function(conn) 
                conn:on("receive",pr)   
                conn:on("sent",function(conn) print("tcp package sent, Heap = "..node.heap()) conn:close() end)
               end)

udpsrv=net.createServer(net.UDP)
udpsrv:on("receive",pr)
udpsrv:on("sent", function(conn) print("udp packet sent \n") end)
udpsrv:listen(8000)

which results for UDP in

parameter info c
userdata        romtable: 0x4023d100
metadatainfo c
listen      lightfunction: 0x4025d98c
close       lightfunction: 0x4025d03c
on      lightfunction: 0x4025ce04
send        lightfunction: 0x4025cb04
__gc        lightfunction: 0x4025c52c
__index     romtable: 0x4023d100

And for TCP in :

parameter info c
userdata        romtable: 0x4023cff8
metadatainfo c
connect     lightfunction: 0x4025d974
close       lightfunction: 0x4025d024
on      lightfunction: 0x4025cdec
send        lightfunction: 0x4025caec
hold        lightfunction: 0x4025da10
unhold      lightfunction: 0x4025d9a4
dns     lightfunction: 0x4025dbf4
getpeer     lightfunction: 0x4025c54c
__gc        lightfunction: 0x4025c514
__index     romtable: 0x4023cff8

Still trying to find why there is this difference.
Herman

@hreintke
Copy link
Author

LS,
I have not enough experience with Pull Request to get that done but the updates in this comment lead to a working getpeer() for servers (UDP).
1: add getpeer to the server metatable

static const LUA_REG_TYPE net_server_map[] =
{
  { LSTRKEY( "listen" ), LFUNCVAL ( net_server_listen ) },
  { LSTRKEY( "close" ), LFUNCVAL ( net_server_close ) },
  { LSTRKEY( "on" ), LFUNCVAL ( net_udpserver_on ) },
  { LSTRKEY( "send" ), LFUNCVAL ( net_udpserver_send ) },
  // { LSTRKEY( "delete" ), LFUNCVAL ( net_server_delete ) },
  { LSTRKEY( "getpeer" ), LFUNCVAL ( net_socket_getpeer ) },
  { LSTRKEY( "__gc" ), LFUNCVAL ( net_server_delete ) },
#if LUA_OPTIMIZE_MEMORY > 0
  { LSTRKEY( "__index" ), LROVAL ( net_server_map ) },
#endif
  { LNILKEY, LNILVAL }
};

2 : Update getpeer to :

static int net_socket_getpeer( lua_State* L )
{
  NODE_DBG("Entered getpeer \n");
  lnet_userdata *nud;

  nud = (lnet_userdata *)luaL_testudata(L, 1, "net.server");
  if (nud == NULL)
  {
      nud = (lnet_userdata *)luaL_testudata(L, 1, "net.socket");
  }
  luaL_argcheck(L, nud, 1, "Server/Socket expected");

  if(nud!=NULL && nud->pesp_conn!=NULL ){
      char temp[20] = {0};
      int tempPort = 0;
      c_sprintf(temp, IPSTR, IP2STR((nud->pesp_conn->type == TCP) ? &(nud->pesp_conn->proto.tcp->remote_ip) : &(nud->pesp_conn->proto.udp->remote_ip) ) );
      tempPort = (nud->pesp_conn->type == TCP) ? nud->pesp_conn->proto.tcp->remote_port :  nud->pesp_conn->proto.udp->remote_port;
      if ( tempPort != 0 ) {
        lua_pushstring( L, temp );
        lua_pushinteger( L, tempPort );
      } else {
        lua_pushnil( L );
        lua_pushnil( L );
      }
  } else {
      lua_pushnil( L );
      lua_pushnil( L );
  }
  return 2;
}

3: As I needed to check userdata to both net.server and net.socket, I cannot use the luaL_checkudata function. I exits with error instead of "just returning NULL" when not found.
Searching internet, I found in lua 5.1 no direct solution but lua 5.2.3 includes a new function luaL_testudata. I added that to net.c. Meaning when upgrading lua this should be removed from net.c.

LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) {
  void *p = lua_touserdata(L, ud);
  if (p != NULL) {  /* value is a userdata? */
    if (lua_getmetatable(L, ud)) {  /* does it have a metatable? */
      luaL_getmetatable(L, tname);  /* get correct metatable */
      if (!lua_rawequal(L, -1, -2))  /* not the same? */
        p = NULL;  /* value is a userdata with wrong metatable */
      lua_pop(L, 2);  /* remove both metatables */
      return p;
    }
  }
  return NULL;  /* value is not a userdata with a metatable */
}

If you need more information, let me know.
Herman

@TerryE
Copy link
Collaborator

TerryE commented Nov 4, 2015

SDK 1.4 Release notes Item 24:

Modify the espconn pointer in the receive callback of UDP. Parameters remote_ip and remote_port in it are the remote IP and port set by espconn_create. If users want to obtain IP and ports of the current sender, please call espconn_get_connection_info to get relevant information.

So the SDK now facilitates this and so we could consider this enhancement for the 1.4-based dev

@msuzer
Copy link

msuzer commented Dec 5, 2015

As this is my first message on this project, thank you to all creaters/contributors of this project, nodemcu is a great firmware for esp modules.

@hreintke, I tried your patch and got "nil, nil" for getpeer() function. After some investigation, I found that both "temp" and "tempPort" are assigned 0 (in "net_socket_getpeer" function). I could not go further.

My environment: I use esp8266-01 and test my udp server code accross android phone, running "UDP Sender/Receiver" app from Google Play.

I need the peer IP to let the esp module reply to clients. Right now, I am planning to workaround this problem by appending client IP to its message and get it parsed in esp code so it can use this IP to create udp connection reply back to the sender.

However, I still want getpeer() function to work. Any help would be great.

@hreintke
Copy link
Author

hreintke commented Dec 5, 2015

@lazy21tr :
It was a long time ago when I submitted the issue and currently am I not using nodemcu anymore for creating esp8266 firmware.

As you can see in the above comment, the project took the SDK 1.4.0 functionality to solve the issue.
You can check #750 and report any issues there.

@hreintke hreintke closed this as completed Dec 5, 2015
@sekarpdkt
Copy link

Is this issue resolved. Still getting same error attempt to call method 'getpeer' (a nil value))
Code:

client=net.createConnection(net.UDP, 0)

port=8972
print("IP:"..wifi.sta.getip()..", Port:"..port)
srv=net.createServer(net.UDP)
srv:on("receive", function(srv, pl)
   -- rIP='192.168.42.1'
   rIP,rPort=srv:getpeer()
   print("From "..rIP..", reveived:"..pl)
    client:connect(8972,rIP)
    client:send("1234")
    client:close()
    client = nil
   end
)
srv:listen(port)

I tried to use both dev and master branch.

thanks

@TerryE
Copy link
Collaborator

TerryE commented Apr 8, 2016

OK, I will have a look at this and update this comment accordingly in 5 hours or so.

BTW, why are you doing this if you want to use the same port? You could just do srv:send("1234") in your receive cb and there's no need to close it since a UDP server shares it's socket between all inbound receives.

@TerryE TerryE added bug and removed enhancement labels Apr 8, 2016
@TerryE TerryE reopened this Apr 8, 2016
@TerryE
Copy link
Collaborator

TerryE commented Apr 8, 2016

OK, I've tracked this one down. It is related to #1080. The underlying issue is that the UDP implementation is a bit of a botch. A net.createServer() returns a net.server object which has methods listen,on,send and close, though send() and on("sent") only work in the case of UDP.

A net:server:on() call passes a net:server object to the called function, but the getpeer() method is associated with a net.socket object and this is only created for TCP connections. Catch-22.

As a quick workaround, I've just added the remote and local ip and ports as arguments to the receive callback and the remote IP:port is always reported as 0.0.0.0:0 in the espconn structure so I need to look at this further..

@sekarpdkt
Copy link

Hi... Thanks. Will srv:send("1234") work in case of UDP? Anyway I will try. Thanks.

@TerryE
Copy link
Collaborator

TerryE commented Apr 8, 2016

Yes and it will send back to the same port as you are listening on.

@TerryE
Copy link
Collaborator

TerryE commented Apr 8, 2016

OK, as well as this, there's a bug in app/lwip/app/espconn_udp.c, so the patch that I've added is

--- a/app/modules/net.c
+++ b/app/modules/net.c
@@ -145,6 +145,7 @@ static void net_socket_received(void *arg, char *pdata, unsigned short len)
 {
   NODE_DBG("net_socket_received is called.\n");
   struct espconn *pesp_conn = arg;
+  int nargs = 2;
   if(pesp_conn == NULL)
     return;
   lnet_userdata *nud = (lnet_userdata *)pesp_conn->reverse;
@@ -161,8 +162,18 @@ static void net_socket_received(void *arg, char *pdata, unsigned short len)
   // NODE_DBG(pdata);
   // NODE_DBG("\n");
   lua_pushlstring(gL, pdata, len);
-  // lua_pushinteger(gL, len);
-  lua_call(gL, 2, 0);
+  if (pesp_conn->type == ESPCONN_UDP) {
+    esp_udp *u = pesp_conn->proto.udp;
+    char ip_str[20] = {0};
+    c_sprintf(ip_str, IPSTR, IP2STR(&(u->remote_ip)));
+    lua_pushstring(gL, ip_str);   // the ip para
+    lua_pushinteger(gL, u->remote_port);
+    c_sprintf(ip_str, IPSTR, IP2STR(&(u->local_ip)));
+    lua_pushstring(gL, ip_str);   // the ip para
+    lua_pushinteger(gL, u->local_port);
+    nargs+=4;
+  }
+  lua_call(gL, nargs, 0);
 }

 static void net_socket_sent(void *arg)

--- a/app/lwip/app/espconn_udp.c
+++ b/app/lwip/app/espconn_udp.c
@@ -308,6 +308,11 @@ espconn_udp_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p,
                wifi_get_ip_info(0, &ipconfig);
        }

+       precv->pespconn->proto.udp->remote_ip[0] = ip4_addr1_16(addr);
+       precv->pespconn->proto.udp->remote_ip[1] = ip4_addr2_16(addr);
+       precv->pespconn->proto.udp->remote_ip[2] = ip4_addr3_16(addr);
+       precv->pespconn->proto.udp->remote_ip[3] = ip4_addr4_16(addr);
+       precv->pespconn->proto.udp->remote_port = port;
        precv->pespconn->proto.udp->local_ip[0] = ip4_addr1_16(&ipconfig.ip);
        precv->pespconn->proto.udp->local_ip[1] = ip4_addr2_16(&ipconfig.ip);
        precv->pespconn->proto.udp->local_ip[2] = ip4_addr3_16(&ipconfig.ip);

and now a srv:on("receive", function(sck, rec, srcIP, srcPort, lclIP, lclPort) -- ... works as expected. @jmattsson @pjsg, is this worth patching before #1080? I've done it this way so that this is backwards compatible with the previous srv:on("receive", function(sck, rec) -- . . . though the most common extended usecase would be srv:on("receive", function(sck, rec, srcIP) -- . . .

@sekarpdkt
Copy link

Yes.. srv:send("1234") worked.. I was not aware that we can listen on UDP client port also...
Node MCU Code:

client=net.createConnection(net.UDP, 0)
port=8972
print(("IP:"%s, Port: %u):format(wifi.sta.getip(), port))
srv=net.createServer(net.UDP)
srv:on("receive", function(srv, pl)
    print("received :"..pl)
    srv:send("1234") 
  end)
srv:listen(port)

Node JS code:

var PORT = 8972;

var dgram = require('dgram');
var server = dgram.createSocket('udp4');

server.on('listening', function () {
    var address = server.address();
    console.log('UDP Server listening on ' + address.address + ":" + address.port);
});

server.on('message', function (message, remote) {
    console.log(remote.address + ':' + remote.port +' - ' + message);

});

server.bind(PORT);


var HOST = '192.168.42.239';

var message = new Buffer('Are you there?');

server.send(message, 0, message.length, PORT, HOST, function(err, bytes) {
    if (err) throw err;
    console.log('UDP message sent to ' + HOST +':'+ PORT);
});

@sekarpdkt: Please use markup to make other readers lives easier!!

TerryE added a commit to TerryE/nodemcu-firmware that referenced this issue Apr 10, 2016
See issue nodemcu#223.  This fixes the bug in the Espressif LiWP UDP wrapper
and adds getpeer processing for UDP.
@TerryE
Copy link
Collaborator

TerryE commented Apr 10, 2016

I have decided not to add the extra parameters to the receive callback but to implement the net:server:getpeer() as the above commenters expect this to work. Since #1233 is now addressing this, I am closing this issue.

@TerryE TerryE closed this as completed Apr 10, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants