Changed send function.

This commit is contained in:
Diego Nehab 2004-07-18 22:56:14 +00:00
parent e4e2223cff
commit c8b402e004
8 changed files with 118 additions and 70 deletions

4
FIX
View File

@ -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 fix bug that caused select return tables not to be associative on windows
compiles with g++ compiles with g++
new sample unix domain support new sample unix domain support

3
TODO
View File

@ -1,4 +1,7 @@
fix manual for send and receive
add thanks to mike
change sock:send to use indices just like string.sub? change sock:send to use indices just like string.sub?
use mike's "don't set to blocking before closing unless needed" patch? use mike's "don't set to blocking before closing unless needed" patch?
take a look at DB's smtp patch take a look at DB's smtp patch

View File

@ -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 usually available as a dynamic library which the interpreter can load
with the help of a loader module written in Lua. with the help of a loader module written in Lua.
Beginning with version 2.0 and following the Lua 5.0 trend, all LuaSocket 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. variables are ever created.
Namespaces are obtained with the <tt>require</tt> Lua function, which loads Namespaces are obtained with the <tt>require</tt> Lua function, which loads
and initializes any required library and returns the namespace. and initializes any required library and returns the namespace.

View File

@ -260,7 +260,7 @@ method returns <b><tt>nil</tt></b> followed by an error message.
<!-- receive ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- receive ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=receive> <p class=name id=receive>
client:<b>receive(</b>[pattern]<b>)</b> client:<b>receive(</b>[pattern [, prefix]]<b>)</b>
</p> </p>
<p class=description> <p class=description>
@ -283,6 +283,11 @@ the returned line. This is the default pattern;
of bytes from the socket. of bytes from the socket.
</ul> </ul>
<p class=parameters>
<tt>Prefix</tt> is an optional string to be concatenated to the beginning
of any received data before return.
</p>
<p class=return> <p class=return>
If successful, the method returns the received pattern. In case of error, If successful, the method returns the received pattern. In case of error,
the method returns <tt><b>nil</b></tt> followed by an error message which the method returns <tt><b>nil</b></tt> followed by an error message which
@ -305,18 +310,18 @@ too.
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=send> <p class=name id=send>
client:<b>send(</b>string<sub>1</sub> [, client:<b>send(</b>data [, i [, j]]<b>)</b>
string<sub>2</sub>, ... string<sub>N</sub>]<b>)</b>
</p> </p>
<p class=description> <p class=description>
Sends data through client object. Sends <tt>data</tt> through client object.
</p> </p>
<p class=parameters> <p class=parameters>
All parameters should be strings. For small strings, it is always better to <tt>Data</tt> is the string to be sent. The optional arguments
concatenate them in Lua (with the '<tt>..</tt>' operator) and pass the <tt>i</tt> and <tt>j</tt> work exactly like the standard
result to LuaSocket instead of passing several independent strings. <tt>string.sub</tt> Lua function to allow the selection of a
substring to be sent.
</p> </p>
<p class=return> <p class=return>
@ -341,6 +346,22 @@ Alas, it wasn't returning <tt><b>nil</b></tt> in case of
error. So it was changed again in beta. error. So it was changed again in beta.
</p> </p>
<p class=note>
<b>Also important</b>:
In order to better support non-blocking I/O and to discourage
bad practice, the <tt>send</tt> 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 <tt>string.sub</tt>.
</p>
<p class=note>
Note: Output is <em>not</em> buffered. For small strings,
it is always better to concatenate them in Lua
(with the '<tt>..</tt>' operator) and send the result in one call
instead of calling the method several times.
</p>
<!-- setoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- setoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=setoption> <p class=name id=setoption>

View File

@ -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 buf_meth_send(lua_State *L, p_buf buf) {
int top = lua_gettop(L); int top = lua_gettop(L);
size_t total = 0;
int arg, err = IO_DONE;
p_tm tm = tm_markstart(buf->tm); p_tm tm = tm_markstart(buf->tm);
for (arg = 2; arg <= top; arg++) { /* first arg is socket object */ int err = IO_DONE;
size_t sent, count; size_t size, sent;
const char *data = luaL_optlstring(L, arg, NULL, &count); const char *data = luaL_checklstring(L, 2, &size);
if (!data || err != IO_DONE) break; ssize_t start = (ssize_t) luaL_optnumber(L, 3, 1);
err = sendraw(buf, data, count, &sent); ssize_t end = (ssize_t) luaL_optnumber(L, 4, -1);
total += sent; 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 */ /* check if there was an error */
if (err != IO_DONE) { if (err != IO_DONE) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, buf->io->error(buf->io->ctx, err)); lua_pushstring(L, buf->io->error(buf->io->ctx, err));
lua_pushnumber(L, total); lua_pushnumber(L, sent);
} else { } else {
lua_pushnumber(L, total); lua_pushnumber(L, sent);
lua_pushnil(L); lua_pushnil(L);
lua_pushnil(L); lua_pushnil(L);
} }

View File

@ -373,12 +373,10 @@ static const char *wstrerror(int err) {
case WSANOTINITIALISED: case WSANOTINITIALISED:
return "Successful WSAStartup not yet performed"; return "Successful WSAStartup not yet performed";
case WSAEDISCON: return "Graceful shutdown in progress"; 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 WSAHOST_NOT_FOUND: return "Host not found";
case WSATRY_AGAIN: return "Nonauthoritative host not found"; case WSATRY_AGAIN: return "Nonauthoritative host not found";
case WSANO_RECOVERY: return "Nonrecoverable name lookup error"; case WSANO_RECOVERY: return "Nonrecoverable name lookup error";
case WSANO_DATA: return "Valid name, no data record of requested type"; case WSANO_DATA: return "Valid name, no data record of requested type";
case WSASYSCALLFAILURE: return "System call failure";
default: return "Unknown error"; default: return "Unknown error";
} }
} }

View File

@ -25,7 +25,7 @@ function remote(...)
s = string.gsub(s, "\n", ";") s = string.gsub(s, "\n", ";")
s = string.gsub(s, "%s+", " ") s = string.gsub(s, "%s+", " ")
s = string.gsub(s, "^%s*", "") s = string.gsub(s, "^%s*", "")
control:send(s, "\n") control:send(s .. "\n")
control:receive() control:receive()
end end
@ -120,7 +120,7 @@ function test_mixed(len)
local bp1, bp2, bp3, bp4 local bp1, bp2, bp3, bp4
remote (string.format("str = data:receive(%d)", remote (string.format("str = data:receive(%d)",
string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4))) 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 if err then fail(err) end
remote "data:send(str); data:close()" remote "data:send(str); data:close()"
bp1, err = data:receive() bp1, err = data:receive()
@ -144,9 +144,9 @@ function test_asciiline(len)
str10 = string.rep("aZb.c#dAe?", math.floor(len/10)) str10 = string.rep("aZb.c#dAe?", math.floor(len/10))
str = str .. str10 str = str .. str10
remote "str = data:receive()" remote "str = data:receive()"
sent, err = data:send(str, "\n") sent, err = data:send(str.."\n")
if err then fail(err) end if err then fail(err) end
remote "data:send(str, '\\n')" remote "data:send(str ..'\\n')"
back, err = data:receive() back, err = data:receive()
if err then fail(err) end if err then fail(err) end
if back == str then pass("lines match") if back == str then pass("lines match")
@ -162,9 +162,9 @@ function test_rawline(len)
math.floor(len/10)) math.floor(len/10))
str = str .. str10 str = str .. str10
remote "str = data:receive()" remote "str = data:receive()"
sent, err = data:send(str, "\n") sent, err = data:send(str.."\n")
if err then fail(err) end if err then fail(err) end
remote "data:send(str, '\\n')" remote "data:send(str..'\\n')"
back, err = data:receive() back, err = data:receive()
if err then fail(err) end if err then fail(err) end
if back == str then pass("lines match") if back == str then pass("lines match")
@ -457,7 +457,62 @@ function getstats_test()
print("ok") print("ok")
end 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("method registration")
test_methods(socket.tcp(), { test_methods(socket.tcp(), {
"accept", "accept",
@ -548,7 +603,6 @@ test_mixed(1)
test("binary line") test("binary line")
reconnect()
test_rawline(1) test_rawline(1)
test_rawline(17) test_rawline(17)
test_rawline(200) test_rawline(200)
@ -562,24 +616,6 @@ test_rawline(17)
test_rawline(1) test_rawline(1)
test("raw transfer") 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(1)
test_raw(17) test_raw(17)
test_raw(200) test_raw(200)

View File

@ -1,29 +1,14 @@
socket = require"socket" socket = require("socket");
host = host or "localhost";
host = host or "localhost" port = port or "8080";
port = port or "8080" server = assert(socket.bind(host, port));
ack = "\n";
server, error = socket.bind(host, port)
if not server then print("server: " .. tostring(error)) os.exit() end
ack = "\n"
while 1 do while 1 do
print("server: waiting for client connection..."); print("server: waiting for client connection...");
control, error = server:accept() control = assert(server:accept());
assert(control, error)
-- control:setoption("nodelay", true)
while 1 do while 1 do
command, error = control:receive() command = assert(control:receive());
if error then assert(control:send(ack));
control:close() (loadstring(command))();
print("server: closing connection...")
break
end
sent, error = control:send(ack)
if error then
control:close()
print("server: closing connection...")
break
end
(loadstring(command))()
end end
end end