lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


2009/11/12 Jerome Vuarand <jerome.vuarand@gmail.com>:
> 2009/11/12 Diego Nehab <diego@tecgraf.puc-rio.br>:
>>> A quick test on a friend's mac shows that on OSX the
>>> behaviour is again different: on failure select returns,
>>> and connect returns nil,"connection failed", while on
>>> success select also returns, but connect returns
>>> nil,"already connected". Am I hitting some undefined use
>>> case of the BSD sockets API ?
>>
>> This is the expected behavior.  "Already connected" is an
>> error message that means there is no error. It is what you
>> should get in this case and means connection succeeded.
>> The failure you are getting is hitting you so quick that
>> select is returning right away, so no problem there either.
>
> If this is the expected behaviour this is weird that Linux is not
> behaving that way. On Linux, the second call to connect on a
> successful connection returns 1,nil.
>
>> Can you tell me more about the error you get when you try to
>> call connect() again on Windows?
>
> If the connection succeeded, I get nil,"already connected". If the
> connection failed though, select never returns. If I don't call
> select, and just call connect in a loop, I get one nil,"timeout", then
> nil,"Operation already in progress" for some time, and then it starts
> again with a single nil,"timeout" and many nil,"Operation already in
> progress", and so on.
>
> While trying to solve the problems, I realized that according to the
> MSDN [1] the normal way to detect that a non-blocking connect failed,
> is to pass the socket to select in the third fd_set, exceptfds, the
> one that detects errors. After looking at the LuaSocket sources, it
> seems that this third set is never used, and not bound by
> socket.select. So I wrote the following function, that I named
> socket.selectex:
>
> static int global_selectex(lua_State *L) {
>    int rtab, wtab, etab, itab, ret, ndirty;
>    t_socket max_fd;
>    fd_set rset, wset, eset;
>    t_timeout tm;
>    double t = luaL_optnumber(L, 4, -1);
>    FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
>    lua_settop(L, 4);
>    lua_newtable(L); itab = lua_gettop(L);
>    lua_newtable(L); rtab = lua_gettop(L);
>    lua_newtable(L); wtab = lua_gettop(L);
>    lua_newtable(L); etab = lua_gettop(L);
>    max_fd = collect_fd(L, 1, SOCKET_INVALID, itab, &rset);
>    ndirty = check_dirty(L, 1, rtab, &rset);
>    t = ndirty > 0? 0.0: t;
>    timeout_init(&tm, t, -1);
>    timeout_markstart(&tm);
>    max_fd = collect_fd(L, 2, max_fd, itab, &wset);
>    max_fd = collect_fd(L, 3, max_fd, itab, &eset);
>    ret = socket_select(max_fd+1, &rset, &wset, &eset, &tm);
>    if (ret > 0 || ndirty > 0) {
>        return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);
>        return_fd(L, &wset, max_fd+1, itab, wtab, 0);
>        return_fd(L, &eset, max_fd+1, itab, etab, 0);
>        make_assoc(L, rtab);
>        make_assoc(L, wtab);
>        make_assoc(L, etab);
>        return 3;
>    } else if (ret == 0) {
>        lua_pushstring(L, "timeout");
>        return 4;
>    } else {
>        lua_pushstring(L, "error");
>        return 4;
>    }
> }
>
> It takes another array before the timeout, and return another one
> before the msg if any. If I put the connecting socket in both write
> and error lists, it is returned in either one depending on the
> connection result. On success I don't have to call connect again to
> use the socket.
>
> I haven't tested it on Linux yet, but I think it should be feasible if
> you'd like to.

It seems that on Linux, the socket is signaled as writable by select
whether it is connected or connection failed. In both cases exceptfds
is returned empty. So the following script still exhibits different
behaviour on Windows and Linux :

#!/usr/bin/env lua

require 'socket'

function test(ip, port)
    local master = assert(socket.tcp())
    master:settimeout(0)
    print(master)
    local success,err = master:connect(ip, port)
    print(master, success, err)
    if not success and err=='timeout' then
        local _,send,errs = socket.selectex({}, {master}, {master})
        print(#send, #errs)
        success,err = master:connect(ip, port)
        print(master, success,err)
    end
end

print("valid address")
test('127.0.0.1', 80)
print()
print("invalid address")
test('127.0.0.1', 44444)