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
compiles with g++
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?
use mike's "don't set to blocking before closing unless needed" 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
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 <tt>require</tt> Lua function, which loads
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 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=receive>
client:<b>receive(</b>[pattern]<b>)</b>
client:<b>receive(</b>[pattern [, prefix]]<b>)</b>
</p>
<p class=description>
@ -283,6 +283,11 @@ the returned line. This is the default pattern;
of bytes from the socket.
</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>
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
@ -305,18 +310,18 @@ too.
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=send>
client:<b>send(</b>string<sub>1</sub> [,
string<sub>2</sub>, ... string<sub>N</sub>]<b>)</b>
client:<b>send(</b>data [, i [, j]]<b>)</b>
</p>
<p class=description>
Sends data through client object.
Sends <tt>data</tt> through client object.
</p>
<p class=parameters>
All parameters should be strings. For small strings, it is always better to
concatenate them in Lua (with the '<tt>..</tt>' operator) and pass the
result to LuaSocket instead of passing several independent strings.
<tt>Data</tt> is the string to be sent. The optional arguments
<tt>i</tt> and <tt>j</tt> work exactly like the standard
<tt>string.sub</tt> Lua function to allow the selection of a
substring to be sent.
</p>
<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.
</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 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<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 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);
}

View File

@ -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";
}
}

View File

@ -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)

View File

@ -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