/* TODO:Fix documentation or find a better home for this example code. The net library Exposes various structs, constants, and functions for passing messages A list of protocols that can be used when creating sockets. The number values below are only for refrence. You should use net.PAIR, net.BUS, ect. @usage --Server local s = net.newsocket(net.REP) s:bind("tcp://127.0.0.1:8765") function s:receive(stream) local message = stream:readstring() s:send("pong",function(stream) stream:writestring(message .. " world!") end) end @usage --Client local c = net.newsocket(net.REQ) c:connect("tcp://127.0.0.1:8765") c:receive(stream) print(stream:readstring()) end c:send(function(stream) stream:writestring("Hello,") end) */ extern "C" { # include # include # include } #include #include #include #include extern "C" { # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include } #include "load_net.hpp" #include #include #include "stream.hpp" /*** 1 to 1 protocol. @field net.PAIR */ #define PAIR 1 /*** Many to many protocol. When this socket sends messages, it does not receive a copy of the message it just sent. @field net.BUS */ #define BUS 2 /*** Publish protocol. The first half of the pub/sub protocol. @field net.PUB */ #define PUB 3 /*** Subscribe protocol The second half of the pub/sub protocol. @field net.SUB */ #define SUB 4 /*** Pull protocol. The first half of the push/pull protocol. @field net.PULL */ #define PULL 5 /*** Push protocol. The second half of the push/pull protocol. @field net.PUSH */ #define PUSH 6 /*** Request protocol. The first half of the request/reply protocol. @field net.REQ */ #define REQ 7 /*** Reply protocol. The second half of the request/reply protocol. @field net.REP */ #define REP 8 /*** Respond protocol. The second half of the survey/respond protocol. @field net.RESPOND */ #define RESPOND 9 /*** Survey protocol. The first half of the survey/respond protocol. @field net.SURVEY */ #define SURVEY 10 //Some defines for things that the library dosn't have /*** Read an integer from the stream @function stream:readint() @treturn number The number read from the stream */ // stream:readInt() int lstream_readint(lua_State* L){//self lua_getfield(L,-1,"data");//self,lightuserdata if(!lua_islightuserdata(L,-1)){ lua_pushstring(L,"data field was not light userdata"); lua_error(L); } struct stream* s = (struct stream*)lua_touserdata(L,-1);//self,lightuserdata stream_print(s); int i = stream_readInt(s); lua_pop(L,2);// lua_pushinteger(L,i);//int return 1; } /*** Read double percision float from the stream @function stream:readdouble() @treturn number The number read from the stream */ // stream:readdouble() int lstream_readdouble(lua_State* L){ lua_getfield(L,-1,"data");//self,lightuserdata struct stream* s = (struct stream*)lua_touserdata(L,-1);//self,lightuserdata double d = stream_readDouble(s);//self,lightuserdata lua_pop(L,2);// lua_pushinteger(L,d);//double return 1; } /*** Read some data from the stream @function stream:readdata() @tparam number size The size of the data to read from the stream @treturn string The data read from the stream */ // stream:readdata(size) int lstream_readdata(lua_State* L){ long size = lua_tonumber(L,-1);//{stream},size lua_pop(L,1);//{stream} lua_getfield(L,-1,"data");//{stream},ud_stream struct stream* s = (struct stream*)lua_touserdata(L,-1);//{stream},ud_stream char buf[size]; stream_readData(s,size,buf); lua_pop(L,2);// lua_pushlstring(L,buf,size);//data return 1; } /*** Read a string from the stream @function stream:readstring() @treturn string The string read from the stream */ // stream:readstring() int lstream_readstring(lua_State* L){ lua_getfield(L,-1,"data");//{stream},ud_stream struct stream* s = (struct stream*)lua_touserdata(L,-1);//{stream},ud_stream char* str = stream_readString(s); lua_pop(L,2); lua_pushstring(L,str);//data return 1; } /*** Write an integer to the stream @function stream:writeint() @tparam number i The integer to write to the stream */ // stream:writeint(number) int lstream_writeint(lua_State* L){//self,number lua_getfield(L,-2,"data");//self,number,lightuserdata struct stream* s = (struct stream*)lua_touserdata(L,-1);//self,number,lightuserdata printf("About to print stream...\n"); stream_print(s); printf("Done printing stream\n"); int num = lua_tointeger(L,-2);//self,number,lightuserdata stream_writeInt(s,num); lua_pop(L,3);// return 0; } /*** Write a double percision float to the stream @function stream:writedouble() @tparam number d The number to write to the stream */ // stream:writeDouble(number) int lstream_writedouble(lua_State* L){ lua_getfield(L,-2,"data");//{stream},number,ud_stream struct stream* s = (struct stream*)lua_touserdata(L,-1);//{stream},number,ud_stream lua_pop(L,1);//{stream},number double d = lua_tonumber(L,-1);//{stream},number stream_writeDouble(s,d);//{stream},number lua_pop(L,2);// return 0; } /*** Get the pipe from this stream. Gets the pipe from this stream, using the return value can be used with setpipe() later to identify clients of a PAIR pipe @function stream:getpipe() @treturn pipe The pipe connected to the associated stream */ //stream:getpipe() :: pipe int lstream_getpipe(lua_State* L){ lua_getfield(L,-1,"data");//{stream},ud_stream struct stream* s = (struct stream*)lua_touserdata(L,-1);//{stream},ud_stream lua_pop(L,2);// lua_newtable(L);//{} lua_pushlightuserdata(L,s->pipe);//{},ud_pipe lua_setfield(L,-2,"pipe");//{pipe} return 1; } /*** Sets the pipe for the stream. Sets the pipe for the stream, so that content is delivered to a specific client. @function stream:setpipe() @tparam pipe pipe The pipe for the specific client, gotten with stream:getpipe() */ //stream:setpipe(pipe) int lstream_setpipe(lua_State* L){ lua_getfield(L,-1,"pipe");//{stream},{pipe},ud_pipe nng_pipe *pipe = (nng_pipe*)lua_touserdata(L,-1);//{stream},{pipe},ud_pipe lua_pop(L,2);//{stream} lua_getfield(L,-1,"data");//{stream},ud_stream struct stream *s = (struct stream*)lua_touserdata(L,-1); lua_pop(L,2); s->pipe = pipe; return 0; } /*** Write some data to the stream @function stream:writedata() @tparam string data The number to write to the stream @treturn number The number of bytes written to the stream */ // stream:writedata(data) int lstream_writedata(lua_State* L){ size_t datalen = 0; const char* data = lua_tolstring(L,-1,&datalen);//{stream},"data" lua_pop(L,1);//{stream} lua_getfield(L,-1,"data");//{stream},ud_stream struct stream* s = (struct stream*)lua_touserdata(L,-1);//{stream},ud_stream stream_writeData(s,data,datalen); lua_pop(L,2);// lua_pushinteger(L,datalen);//size return 1; } /*** Write a string to the stream @function stream:writestring() @tparam string s The string to the stream */ // stream:writestring(string) int lstream_writestring(lua_State* L){ size_t slen = 0; const char* str = lua_tolstring(L,-1,&slen);//{stream},"string" slen = strlen(str); char buf[slen]; strcpy(buf,str); lua_pop(L,1);//{stream} lua_getfield(L,-1,"data");//{stream},ud_stream struct stream* s = (struct stream*)lua_touserdata(L,-1);//{stream},ud_stream stream_writeString(s,buf,slen); lua_pop(L,2); return 0; } bool socket_can_receive(int type){ switch(type){ case PAIR: case BUS: case SUB: case PULL: case REQ: case REP: case RESPOND: case SURVEY: return true; default: return false; } } //socket:block_recv() int block_recv(lua_State *L){//{socket} lua_getfield(L,-1,"fd");//{socket},ud_socket nng_socket *socket = (nng_socket*)lua_touserdata(L,-1); lua_pop(L,2);// nng_msg *msgp; int err = nng_recvmsg(*socket,&msgp,0); if(err){ printf("Net error: %s\n\t\n",nng_strerror(err)); lua_pushstring(L,"Error while receving message:");//Err lua_pushstring(L,nng_strerror(err));//Err,msg lua_concat(L,2);//Full_err lua_error(L); }else{ char* buf = (char*)nng_msg_body(msgp); size_t size = nng_msg_len(msgp); struct stream* stream = stream_create(); stream->length = size; stream->data = buf; //stream_print(stream); lua_newtable(L);//{} lua_pushlightuserdata(L,stream);//{},ud_stream lua_setfield(L,-2,"data");//{data=stream} luaL_getmetatable(L,"net.stream");//{data=stream},{net.stream} lua_setmetatable(L,-2);//{stream} } return 1; } void gameloop_net(lua_State* L){ assert(lua_gettop(L) == 0); //printf("Doing net of gameloop,starting with %d args\n",lua_gettop(L)); //printf("Got net\n"); lua_getglobal(L,"net");//{net} lua_getfield(L,-1,"sockets");//{net},{sockets} lua_pushnil(L);//{net},{sockets},nil //printf("Found sockets\n"); while(lua_next(L,-2) != 0){ //printf("Got first socket value\n"); //{net},{sockets},{socket},true //printf("%s - %s\n",lua_typename(L,lua_type(L,-2)),lua_typename(L,lua_type(L,-1))); lua_getfield(L,-2,"fd");//{net},{sockets},{socket},true,fd nng_socket *socket = (nng_socket*)lua_touserdata(L,-1); lua_getfield(L,-3,"n");//{net},{sockets},{socket},true,fd,type int stype = lua_tonumber(L,-1); //printf("Got a socket of type %d\n",stype); lua_pop(L,3);//{net},{sockets},{socket}//Keep key for next iteration of lua_next() if(!socket_can_receive(stype)){ //printf("Socket cannot receive, breaking!\n"); continue; } //printf("About to push errorfunc\n"); pusherrorfunc(L);//{net},{sockets},{socket},errfunc() //printf("Done pushing errorfunc\n"); lua_getfield(L,-2,"receive");//{net},{sockets},{socket},errfunc(),(socket.receive | nil) //printf("Got field\n"); if(lua_isnil(L,-1)){//Make sure we have a receive //printf("Listen-able socket type %d has no .receive method\n",stype); lua_pop(L,2); continue; } //{net},{sockets},{socket},socket.receive //printf("Right before recv\n"); nng_msg *msgp; //printf("About to recvmsg(), socket: %p, msgp: %p\n",socket,msgp); int err = nng_recvmsg(*socket,&msgp,NNG_FLAG_NONBLOCK); //size_t recvsize; //char *buf; //int err = nng_recv(*socket,&buf,&recvsize,NNG_FLAG_NONBLOCK); //printf("Done with recvmsg() err: %d:\n",err); if(err){ switch(err){ case NNG_EAGAIN: break; //case NNG_ESTATE: //if(stype == REQ) break; default: printf("Net error: %s\n\tSocket type:%d\n",nng_strerror(err),stype); lua_pushstring(L,"Error while receving message:"); lua_pushstring(L,nng_strerror(err)); lua_concat(L,2); lua_error(L); } lua_pop(L,2); }else{ //printf("Actually receving message\n"); char* buf = (char*)nng_msg_body(msgp); //printf("Got message body\n"); size_t size = nng_msg_len(msgp); //size_t size = recvsize; //printf("Got mesage body\n"); struct stream* stream = stream_create(); stream->length = size; stream->data = buf; //stream_print(stream); //printf("Created stream and everything\n"); lua_pushvalue(L,-4);//{net},{sockets},{socket},errfunc(),socket.receive(),{socket},{} lua_newtable(L);//{net},{sockets},{socket},errfunc(),socket.receive(),{socket},{} lua_pushlightuserdata(L,stream);//{net},{sockets},{socket},errfunc(),socket.receive(),{socket},{},ud_stream lua_setfield(L,-2,"data");//{net},{sockets},{socket},errfunc(),socket.receive(),{socket},{data=stream} luaL_getmetatable(L,"net.stream");//{net},{sockets},{socket},errfunc(),socket.receive(),{socket},{data=stream},{net.stream} lua_setmetatable(L,-2);//{net},{sockets},{socket},errfunc(),socket.receive(),{socket},{stream} //printf("About to call receive function\n"); lua_pcall(L,2,0,-4);//{net},{sockets},{socket},errfunc() lua_pop(L,1); //printf("Finished calling receive function\n"); //printf("Finished calling gameloop, buf is %p, size is %zu\n",buf,size); nng_msg_free(msgp); //nng_free(buf,size); //printf("called nn_freemsg\n"); free(stream);//We manually set stream->data so free_stream would crash here //printf("Called free on stream\n"); } } printf("There are %d items left on the lua stack\n",lua_gettop(L)); lua_pop(L,2); printf("Done with net game loop\n"); assert(lua_gettop(L) == 0); } /*** Bind a socket to a network interface @function socket:bind @tparam string where Where to connect this socket to */ //bind("where") int bindsocket(lua_State*L){ const char* s = lua_tostring(L,-1);//{self},"where" lua_pop(L,1);//{self} lua_getfield(L,-1,"fd");//{self},num_socket nng_socket *fd = (nng_socket*)lua_touserdata(L,-1); lua_pop(L,1);//{self} printf("Binding socket %p to %s\n",fd,s); int err = nng_listen(*fd,s,NULL,0); if(err != 0){ const char* errstr = nng_strerror(err); char* failmsg = (char*)"Failed to bind socket: "; int faillen = strlen(failmsg + 2); char buf[faillen + strlen(errstr)]; sprintf(buf,"%s%s\n",failmsg,errstr); printf("Socket error:%s\n",buf); lua_pushstring(L,buf); lua_error(L); } lua_pushlightuserdata(L,fd);//{self},endpoint lua_setfield(L,-2,"endpoint");//{self} printf("Done binding socket\n"); return 0; } /*** Connects a socket to another @function socket:connect() @tparam string endpoint The endpoint to connect this socket to */ //socket:connect("endpoint") int connectsocket(lua_State* L){ printf("Connect called\n"); const char* s = lua_tostring(L,-1);//{self},"endpoint" lua_pop(L,1);//{self} lua_getfield(L,-1,"fd");//{self},ud_nng_socket nng_socket *fd = (nng_socket*)lua_touserdata(L,-1);//{self},ud_nng_socket printf("Got socket: %p\n",fd); lua_pop(L,2); int err = nng_dial(*fd,s,NULL,0); if(err != 0){ printf("Connect error: %s\n",nng_strerror(err)); } return 0; } /*** Sends some message to the other side of a connected or bound socket @function socket:send @tparam function callback The function to call to send the data */ //socket:send(function(stream)) int send(lua_State* L){ lua_getfield(L,-2,"fd");//{socket},function(stream),int_socketdescriptor nng_socket *fd = (nng_socket*)lua_touserdata(L,-1);//socket,function(stream),int_socketdescriptor lua_pop(L,1);//socket,function(stream) struct stream* s = stream_create(); lua_newtable(L);//socket,function(stream),streamtable luaL_getmetatable(L,"net.stream");//socket,function(stream),streamtable,net.stream lua_setmetatable(L,-2);//socket,function(stream),streamtable lua_pushlightuserdata(L,s);//socket,function(stream),streamtable,lightudata lua_setfield(L,-2,"data");//socket,function(stream),streamtable lua_call(L,1,0);//socket, printf("Finished the stream call\n"); lua_pop(L,1);// printf("About to nng_send\n\tfd:%p\n\t%s\n\t%ld\n",fd,s->data,s->length); //int err = nng_send(*fd, s->data, s->length,0); int err = nng_send(*fd, s->data, s->length,0); printf("Finished nn_send\n"); stream_free(s); if(err != 0){ printf("Errored somewhere\n"); const char* errstr = nng_strerror(err); char* failmsg = (char*)"Failed to send socket: "; int faillen = strlen(failmsg + 2); char buf[faillen + strlen(errstr)]; lua_pushstring(L,buf); lua_error(L); } printf("No error in nn_send\n"); return 0; } /*** @function socket:receive() @tparam function callback The function to call when this message is received */ //LUA: //socket:receive(function(stream)) //int netreceive(lua_State* L){ //if(lua_type(L,-2) != LUA_TTABLE){ //lua_pushstring(L,"Expected argument #1 to socket.receive to be socket"); //lua_error(L); //} //printf("two\n"); //if(lua_type(L,-1) != LUA_TFUNCTION){ //lua_pushstring(L,"Expected argument #2 to socket.receive to be function"); //lua_error(L); //} //lua_getfield(L,-1,"fd");//{socket},fd //nng_socket *fd = (nng_socket*)lua_touserdata(L,-1);//{socket},fd //lua_pop(L,1);//{socket} //lua_getfield(L,-1,"n");//{socket},type //int sockettype = lua_tonumber(L,-1); //lua_pop(L,2);// //netfuncs[fd] = func;// //nettypes[fd] = sockettype; //return 0; //} /*** @function socket:close() Closes the socket */ //socket:close() :: nil int netclose(lua_State* L){//{socket} lua_getfield(L,-1,"fd");//{socket},fd nng_socket *fd = (nng_socket*)lua_touserdata(L,-1);//{socket},fd lua_pop(L,1);//{socket} nng_close(*fd); free(fd); lua_getglobal(L,"net");//{socket},{net} lua_getfield(L,-1,"sockets");//{socket},{net},{sockets} lua_pushvalue(L,-3);//{socket},{net},{sockets},{socket} lua_pushnil(L);//{socket},{net},{sockets},{socket},nil lua_settable(L,-3);//{socket},{net},{sockets} lua_pop(L,2);//{socket} lua_pushboolean(L,1);//{socket},true lua_setfield(L,-2,"closed");//{socket} lua_pop(L,1); return 0; } /*** The opaque socket descriptor for this socket. @field socket.fd The descriptor @domain shared */ /*** @function net.newsocket @return net.socket @domain shared */ //net.newsocket(type) int socketFactory(lua_State*L){ int nargs = lua_gettop(L); if(nargs != 1){ printf("net.newsocket() expected 1 argument, got %d\n",nargs); return 0; } int type = lua_tonumber(L,-1); lua_pop(L,1); printf("Creating a socket with type: %d, NNG_PAIR:%d, NNG_BUS:%d\n",type,PAIR,BUS); nng_socket *socket = (nng_socket*) malloc(sizeof(nng_socket)); int err; switch(type){ case PAIR: err = nng_pair_open(socket); break; case BUS : err = nng_bus_open(socket); break; case PUB : err = nng_pub_open(socket); break; case SUB : err = nng_sub_open(socket); break; case PULL: err = nng_pull_open(socket); break; case PUSH: err = nng_push_open(socket); break; case REQ : err = nng_req_open(socket); break; case REP : err = nng_rep_open(socket); break; case SURVEY : err = nng_surveyor_open(socket); break; case RESPOND : err = nng_respondent_open(socket); break; default: printf("Unknown socket type: %d",type); break; } if(err != 0){ printf("Failed to create socket: %s\n",nng_strerror(err)); lua_pushstring(L,"Failed to create socket:"); lua_pushstring(L,nng_strerror(err)); lua_concat(L,2); lua_error(L); } printf("Socket created: %p\n",socket); lua_newtable(L);//{} lua_pushlightuserdata(L,socket);//{},c lua_setfield(L,-2,"fd");//{fd=c} lua_pushnumber(L,type);//{fd=c},type lua_setfield(L,-2,"n");//{fd=c,n=type} printf("Metatable set\n"); luaL_getmetatable(L,"net.pair_socket");//{fd=c,n=type},{m_net.pair_socket} lua_setmetatable(L,-2);//{socket} lua_getglobal(L,"net");//{socket},{net} lua_getfield(L,-1,"sockets");//{socket},{net},{sockets} lua_pushvalue(L,-3);//{socket},{net},{sockets},{socket} lua_pushboolean(L,1);//{socket},{net},{sockets},{socket},true lua_settable(L,-3);//{socket},{net},{sockets} lua_pop(L,2);//{socket} printf("Finished making the socket, returning\n"); return 1; } static const struct luaL_Reg stream_m[] = { {"writeint",lstream_writeint}, {"writedouble",lstream_writedouble}, {"writedata",lstream_writedata}, {"writestring",lstream_writestring}, {"readint",lstream_readint}, {"readdouble",lstream_readdouble}, {"readdata",lstream_readdata}, {"readstring",lstream_readstring}, {"getpipe",lstream_getpipe}, {"setpipe",lstream_setpipe}, {NULL,NULL} }; static const struct luaL_Reg pair_socket_m[] = { {"bind", bindsocket}, {"connect", connectsocket}, {"send",send}, {"block_recv",block_recv}, //{"receive", netreceive}, {"close",netclose}, {NULL,NULL} }; static const struct luaL_Reg net_f[] = { {"newsocket", socketFactory}, {NULL,NULL} }; int notify_gc(lua_State* L){ printf("Garbage collector called!"); return 0; } #define set_const(l,x) lua_pushstring(l,#x);lua_pushinteger(l,x);lua_settable(l,-3); void loadNetLibs(lua_State* L){ //Initalize the socket->function map //A table to hold all our net funcs lua_newtable(L);//{} lua_newtable(L);//{},{} lua_setfield(L,-2,"sockets");//{sockets = {}} //Push some enums //Protocols set_const(L,PAIR); set_const(L,BUS); set_const(L,PUB); set_const(L,SUB); set_const(L,PULL); set_const(L,PUSH); set_const(L,REQ); set_const(L,REP); set_const(L,RESPOND); set_const(L,SURVEY); luaL_register(L,NULL,net_f); //Set the table to gobal "net" lua_setglobal(L,"net"); //Create the metatable for streams luaL_newmetatable(L,"net.stream"); //net.stream lua_newtable(L);//net.stream,{} luaL_register(L,NULL,stream_m);//net.stream,{net.stream} lua_setfield(L,-2,"__index");//{m_net.stream} //lua_pushcfunction(L,notify_gc);//{m_net.stream},notify_gc //lua_setfield(L,-2,"__gc");//{m_net.stream} lua_pop(L,1);// //Create the metatable for sockets luaL_newmetatable(L,"net.pair_socket");//net.socket lua_newtable(L);//net.socket,{} luaL_register(L,NULL,pair_socket_m);//net.socket,{net.socket} lua_setfield(L,-2,"__index");//{m_net.socket} lua_pop(L,1);// }