diff --git a/FIX b/FIX index 08f1452..f0ad3aa 100644 --- a/FIX +++ b/FIX @@ -1,3 +1,7 @@ +UDP has a reasonable maximum datagram size +receive accepts the prefix optional argument +send doesn't support multiple arguments anymore +send allows the selection of the substring to be sent fix bug that caused select return tables not to be associative on windows compiles with g++ new sample unix domain support diff --git a/TODO b/TODO index ec8f25e..23de9d7 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,7 @@ +fix manual for send and receive +add thanks to mike + change sock:send to use indices just like string.sub? use mike's "don't set to blocking before closing unless needed" patch? take a look at DB's smtp patch diff --git a/doc/introduction.html b/doc/introduction.html index 6468148..20c4d36 100644 --- a/doc/introduction.html +++ b/doc/introduction.html @@ -113,7 +113,7 @@ The core LuaSocket is almost entirely implemented in C. It is usually available as a dynamic library which the interpreter can load with the help of a loader module written in Lua. Beginning with version 2.0 and following the Lua 5.0 trend, all LuaSocket -functionality is defined inside tables (or rather a namespaces). No global +functionality is defined inside tables (or rather namespaces). No global variables are ever created. Namespaces are obtained with the require Lua function, which loads and initializes any required library and returns the namespace. diff --git a/doc/tcp.html b/doc/tcp.html index 5f520e9..30e06f4 100644 --- a/doc/tcp.html +++ b/doc/tcp.html @@ -260,7 +260,7 @@ method returns nil followed by an error message.

-client:receive([pattern]) +client:receive([pattern [, prefix]])

@@ -283,6 +283,11 @@ the returned line. This is the default pattern; of bytes from the socket. +

+Prefix is an optional string to be concatenated to the beginning +of any received data before return. +

+

If successful, the method returns the received pattern. In case of error, the method returns nil followed by an error message which @@ -305,18 +310,18 @@ too.

-client:send(string1 [, -string2, ... stringN]) +client:send(data [, i [, j]])

-Sends data through client object. +Sends data through client object.

-All parameters should be strings. For small strings, it is always better to -concatenate them in Lua (with the '..' operator) and pass the -result to LuaSocket instead of passing several independent strings. +Data is the string to be sent. The optional arguments +i and j work exactly like the standard +string.sub Lua function to allow the selection of a +substring to be sent.

@@ -341,6 +346,22 @@ Alas, it wasn't returning nil in case of error. So it was changed again in beta.

+

+Also important: +In order to better support non-blocking I/O and to discourage +bad practice, the send method now only sends one string +per call. The other optional arguments allow the user to select +a substring to be sent in a much more efficient way than +using string.sub. +

+ +

+Note: Output is not buffered. For small strings, +it is always better to concatenate them in Lua +(with the '..' operator) and send the result in one call +instead of calling the method several times. +

+

diff --git a/src/buffer.c b/src/buffer.c index 33fae84..30be156 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -64,23 +64,24 @@ int buf_meth_getstats(lua_State *L, p_buf buf) { \*-------------------------------------------------------------------------*/ int buf_meth_send(lua_State *L, p_buf buf) { int top = lua_gettop(L); - size_t total = 0; - int arg, err = IO_DONE; p_tm tm = tm_markstart(buf->tm); - for (arg = 2; arg <= top; arg++) { /* first arg is socket object */ - size_t sent, count; - const char *data = luaL_optlstring(L, arg, NULL, &count); - if (!data || err != IO_DONE) break; - err = sendraw(buf, data, count, &sent); - total += sent; - } + int err = IO_DONE; + size_t size, sent; + const char *data = luaL_checklstring(L, 2, &size); + ssize_t start = (ssize_t) luaL_optnumber(L, 3, 1); + ssize_t end = (ssize_t) luaL_optnumber(L, 4, -1); + if (start < 0) start = size+start+1; + if (end < 0) end = size+end+1; + if (start < 1) start = 1; + if (end > size) end = size; + if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent); /* check if there was an error */ if (err != IO_DONE) { lua_pushnil(L); lua_pushstring(L, buf->io->error(buf->io->ctx, err)); - lua_pushnumber(L, total); + lua_pushnumber(L, sent); } else { - lua_pushnumber(L, total); + lua_pushnumber(L, sent); lua_pushnil(L); lua_pushnil(L); } diff --git a/src/wsocket.c b/src/wsocket.c index d9da6fc..511dbdc 100644 --- a/src/wsocket.c +++ b/src/wsocket.c @@ -373,12 +373,10 @@ static const char *wstrerror(int err) { case WSANOTINITIALISED: return "Successful WSAStartup not yet performed"; case WSAEDISCON: return "Graceful shutdown in progress"; - case WSATYPE_NOT_FOUND: return "Class type not found"; case WSAHOST_NOT_FOUND: return "Host not found"; case WSATRY_AGAIN: return "Nonauthoritative host not found"; case WSANO_RECOVERY: return "Nonrecoverable name lookup error"; case WSANO_DATA: return "Valid name, no data record of requested type"; - case WSASYSCALLFAILURE: return "System call failure"; default: return "Unknown error"; } } diff --git a/test/testclnt.lua b/test/testclnt.lua index f98a504..356350a 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -25,7 +25,7 @@ function remote(...) s = string.gsub(s, "\n", ";") s = string.gsub(s, "%s+", " ") s = string.gsub(s, "^%s*", "") - control:send(s, "\n") + control:send(s .. "\n") control:receive() end @@ -120,7 +120,7 @@ function test_mixed(len) local bp1, bp2, bp3, bp4 remote (string.format("str = data:receive(%d)", string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4))) - sent, err = data:send(p1, p2, p3, p4) + sent, err = data:send(p1..p2..p3..p4) if err then fail(err) end remote "data:send(str); data:close()" bp1, err = data:receive() @@ -144,9 +144,9 @@ function test_asciiline(len) str10 = string.rep("aZb.c#dAe?", math.floor(len/10)) str = str .. str10 remote "str = data:receive()" - sent, err = data:send(str, "\n") + sent, err = data:send(str.."\n") if err then fail(err) end -remote "data:send(str, '\\n')" +remote "data:send(str ..'\\n')" back, err = data:receive() if err then fail(err) end if back == str then pass("lines match") @@ -162,9 +162,9 @@ function test_rawline(len) math.floor(len/10)) str = str .. str10 remote "str = data:receive()" - sent, err = data:send(str, "\n") + sent, err = data:send(str.."\n") if err then fail(err) end -remote "data:send(str, '\\n')" +remote "data:send(str..'\\n')" back, err = data:receive() if err then fail(err) end if back == str then pass("lines match") @@ -457,7 +457,62 @@ function getstats_test() print("ok") end + ------------------------------------------------------------------------ +function test_nonblocking(size) + reconnect() +remote(string.format([[ + data:send(string.rep("a", %d)) + socket.sleep(0.5) + data:send(string.rep("b", %d)) +]], size, size)) + local err = "timeout" + local part = "" + local str + data:settimeout(0) + while 1 do + str, err, part = data:receive(2*size - string.len(part), part) + if err ~= "timeout" then break end + end + assert(str == (string.rep("a", size) .. string.rep("b", size))) + reconnect() +remote(string.format([[ + str = data:receive(%d) + socket.sleep(0.5) + str = data:receive(%d, str) + str = data:receive("*l", str) + data:send(str) + data:send("\n") +]], size, size)) + data:settimeout(0) + local sofar = 1 + while 1 do + _, err, part = data:send(str, sofar) + if err ~= "timeout" then break end + sofar = sofar + part + end + data:send("\n") + data:settimeout(-1) + local back = data:receive() + assert(back == str) + print("ok") +end + + +------------------------------------------------------------------------ +test("non-blocking transfer") +test_nonblocking(1) +test_nonblocking(17) +test_nonblocking(200) +test_nonblocking(4091) +test_nonblocking(80199) +test_nonblocking(8000000) +test_nonblocking(80199) +test_nonblocking(4091) +test_nonblocking(200) +test_nonblocking(17) +test_nonblocking(1) + test("method registration") test_methods(socket.tcp(), { "accept", @@ -548,7 +603,6 @@ test_mixed(1) test("binary line") -reconnect() test_rawline(1) test_rawline(17) test_rawline(200) @@ -562,24 +616,6 @@ test_rawline(17) test_rawline(1) test("raw transfer") -reconnect() -test_raw(1) -test_raw(17) -test_raw(200) -test_raw(4091) -test_raw(80199) -test_raw(8000000) -test_raw(80199) -test_raw(4091) -test_raw(200) -test_raw(17) -test_raw(1) - -test("non-blocking transfer") -reconnect() --- the value is not important, we only want --- to test non-blocking I/O anyways -data:settimeout(200) test_raw(1) test_raw(17) test_raw(200) diff --git a/test/testsrvr.lua b/test/testsrvr.lua index 969f95b..23e3850 100644 --- a/test/testsrvr.lua +++ b/test/testsrvr.lua @@ -1,29 +1,14 @@ -socket = require"socket" - -host = host or "localhost" -port = port or "8080" - -server, error = socket.bind(host, port) -if not server then print("server: " .. tostring(error)) os.exit() end -ack = "\n" +socket = require("socket"); +host = host or "localhost"; +port = port or "8080"; +server = assert(socket.bind(host, port)); +ack = "\n"; while 1 do print("server: waiting for client connection..."); - control, error = server:accept() - assert(control, error) - -- control:setoption("nodelay", true) + control = assert(server:accept()); while 1 do - command, error = control:receive() - if error then - control:close() - print("server: closing connection...") - break - end - sent, error = control:send(ack) - if error then - control:close() - print("server: closing connection...") - break - end - (loadstring(command))() + command = assert(control:receive()); + assert(control:send(ack)); + (loadstring(command))(); end end