Merge branch 'agnostic'

Seems safe to move to master.
master
Diego Nehab 2015-08-25 15:43:48 -03:00
commit 4110e4125d
14 changed files with 461 additions and 412 deletions

View File

@ -160,9 +160,11 @@ Support, Manual">
<a href="socket.html#setsize">_SETSIZE</a>, <a href="socket.html#setsize">_SETSIZE</a>,
<a href="socket.html#source">source</a>, <a href="socket.html#source">source</a>,
<a href="tcp.html#socket.tcp">tcp</a>, <a href="tcp.html#socket.tcp">tcp</a>,
<a href="tcp.html#socket.tcp4">tcp4</a>,
<a href="tcp.html#socket.tcp6">tcp6</a>, <a href="tcp.html#socket.tcp6">tcp6</a>,
<a href="socket.html#try">try</a>, <a href="socket.html#try">try</a>,
<a href="udp.html#socket.udp">udp</a>, <a href="udp.html#socket.udp">udp</a>,
<a href="udp.html#socket.udp4">udp4</a>,
<a href="udp.html#socket.udp6">udp6</a>, <a href="udp.html#socket.udp6">udp6</a>,
<a href="socket.html#version">_VERSION</a>. <a href="socket.html#version">_VERSION</a>.
</blockquote> </blockquote>

View File

@ -1,10 +1,10 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd"> "http://www.w3.org/TR/html4/strict.dtd">
<html> <html>
<head> <head>
<meta name="description" content="LuaSocket: The TCP/IP support"> <meta name="description" content="LuaSocket: The TCP/IP support">
<meta name="keywords" content="Lua, LuaSocket, Socket, TCP, Library, Network, Support"> <meta name="keywords" content="Lua, LuaSocket, Socket, TCP, Library, Network, Support">
<title>LuaSocket: TCP/IP support</title> <title>LuaSocket: TCP/IP support</title>
<link rel="stylesheet" href="reference.css" type="text/css"> <link rel="stylesheet" href="reference.css" type="text/css">
</head> </head>
@ -28,7 +28,7 @@
<a href="index.html#download">download</a> &middot; <a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot; <a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot; <a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a> <a href="reference.html">reference</a>
</p> </p>
</center> </center>
<hr> <hr>
@ -36,21 +36,54 @@
<!-- tcp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- tcp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id="tcp">TCP</h2> <h2 id="tcp">TCP</h2>
<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="socket.tcp"> <p class=name id="socket.tcp">
socket.<b>tcp()</b> socket.<b>tcp()</b>
</p> </p>
<p class=description> <p class=description>
Creates and returns an IPv4 TCP master object. A master object can Creates and returns an TCP master object. A master object can
be transformed into a server object with the method be transformed into a server object with the method
<a href=#listen><tt>listen</tt></a> (after a call to <a <a href=#listen><tt>listen</tt></a> (after a call to <a
href=#bind><tt>bind</tt></a>) or into a client object with href=#bind><tt>bind</tt></a>) or into a client object with
the method <a href=#connect><tt>connect</tt></a>. The only other the method <a href=#connect><tt>connect</tt></a>. The only other
method supported by a master object is the method supported by a master object is the
<a href=#close><tt>close</tt></a> method.</p>
<p class=return>
In case of success, a new master object is returned. In case of error,
<b><tt>nil</tt></b> is returned, followed by an error message.
</p>
<p class=note>
Note: The choice between IPv4 and IPv6 happens during a call to
<a href=#bind><tt>bind</tt></a> or <a
href=#bind><tt>connect</tt></a>, depending on the address
family obtained from the resolver.
</p>
<p class=note>
Note: Before the choice between IPv4 and IPv6 happens,
the internal socket object is invalid and therefore <a
href=#setoption><tt>setoption</tt></a> will fail.
</p>
<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="socket.tcp4">
socket.<b>tcp4()</b>
</p>
<p class=description>
Creates and returns an IPv4 TCP master object. A master object can
be transformed into a server object with the method
<a href=#listen><tt>listen</tt></a> (after a call to <a
href=#bind><tt>bind</tt></a>) or into a client object with
the method <a href=#connect><tt>connect</tt></a>. The only other
method supported by a master object is the
<a href=#close><tt>close</tt></a> method.</p> <a href=#close><tt>close</tt></a> method.</p>
<p class=return> <p class=return>
@ -60,17 +93,17 @@ In case of success, a new master object is returned. In case of error,
<!-- socket.tcp6 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- socket.tcp6 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="socket.tcp6"> <p class=name id="socket.tcp6">
socket.<b>tcp6()</b> socket.<b>tcp6()</b>
</p> </p>
<p class=description> <p class=description>
Creates and returns an IPv6 TCP master object. A master object can Creates and returns an IPv6 TCP master object. A master object can
be transformed into a server object with the method be transformed into a server object with the method
<a href=#listen><tt>listen</tt></a> (after a call to <a <a href=#listen><tt>listen</tt></a> (after a call to <a
href=#bind><tt>bind</tt></a>) or into a client object with href=#bind><tt>bind</tt></a>) or into a client object with
the method <a href=#connect><tt>connect</tt></a>. The only other the method <a href=#connect><tt>connect</tt></a>. The only other
method supported by a master object is the method supported by a master object is the
<a href=#close><tt>close</tt></a> method.</p> <a href=#close><tt>close</tt></a> method.</p>
<p class=return> <p class=return>
@ -85,7 +118,7 @@ Note: The TCP object returned will have the option
<!-- accept +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- accept +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="accept"> <p class=name id="accept">
server:<b>accept()</b> server:<b>accept()</b>
</p> </p>
@ -95,9 +128,9 @@ object and returns a client object representing that connection.
</p> </p>
<p class=return> <p class=return>
If a connection is successfully initiated, a client object is returned. If a connection is successfully initiated, a client object is returned.
If a timeout condition is met, the method returns <b><tt>nil</tt></b> If a timeout condition is met, the method returns <b><tt>nil</tt></b>
followed by the error string '<tt>timeout</tt>'. Other errors are followed by the error string '<tt>timeout</tt>'. Other errors are
reported by <b><tt>nil</tt></b> followed by a message describing the error. reported by <b><tt>nil</tt></b> followed by a message describing the error.
</p> </p>
@ -107,28 +140,28 @@ with a server object in
the <tt>recvt</tt> parameter before a call to <tt>accept</tt> does the <tt>recvt</tt> parameter before a call to <tt>accept</tt> does
<em>not</em> guarantee <tt>accept</tt> will return immediately. Use the <a <em>not</em> guarantee <tt>accept</tt> will return immediately. Use the <a
href=#settimeout><tt>settimeout</tt></a> method or <tt>accept</tt> href=#settimeout><tt>settimeout</tt></a> method or <tt>accept</tt>
might block until <em>another</em> client shows up. might block until <em>another</em> client shows up.
</p> </p>
<!-- bind +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- bind +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="bind"> <p class=name id="bind">
master:<b>bind(</b>address, port<b>)</b> master:<b>bind(</b>address, port<b>)</b>
</p> </p>
<p class=description> <p class=description>
Binds a master object to <tt>address</tt> and <tt>port</tt> on the Binds a master object to <tt>address</tt> and <tt>port</tt> on the
local host. local host.
<p class=parameters> <p class=parameters>
<tt>Address</tt> can be an IP address or a host name. <tt>Address</tt> can be an IP address or a host name.
<tt>Port</tt> must be an integer number in the range [0..64K). <tt>Port</tt> must be an integer number in the range [0..64K).
If <tt>address</tt> If <tt>address</tt>
is '<tt>*</tt>', the system binds to all local interfaces is '<tt>*</tt>', the system binds to all local interfaces
using the <tt>INADDR_ANY</tt> constant or using the <tt>INADDR_ANY</tt> constant or
<tt>IN6ADDR_ANY_INIT</tt>, according to the family. <tt>IN6ADDR_ANY_INIT</tt>, according to the family.
If <tt>port</tt> is 0, the system automatically If <tt>port</tt> is 0, the system automatically
chooses an ephemeral port. chooses an ephemeral port.
</p> </p>
<p class=return> <p class=return>
@ -137,13 +170,13 @@ method returns <b><tt>nil</tt></b> followed by an error message.
</p> </p>
<p class=note> <p class=note>
Note: The function <a href=socket.html#bind><tt>socket.bind</tt></a> Note: The function <a href=socket.html#bind><tt>socket.bind</tt></a>
is available and is a shortcut for the creation of server sockets. is available and is a shortcut for the creation of server sockets.
</p> </p>
<!-- close ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- close ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="close"> <p class=name id="close">
master:<b>close()</b><br> master:<b>close()</b><br>
client:<b>close()</b><br> client:<b>close()</b><br>
server:<b>close()</b> server:<b>close()</b>
@ -154,14 +187,14 @@ Closes a TCP object. The internal socket used by the object is closed
and the local address to which the object was and the local address to which the object was
bound is made available to other applications. No further operations bound is made available to other applications. No further operations
(except for further calls to the <tt>close</tt> method) are allowed on (except for further calls to the <tt>close</tt> method) are allowed on
a closed socket. a closed socket.
</p> </p>
<p class=note> <p class=note>
Note: It is important to close all used sockets once they are not Note: It is important to close all used sockets once they are not
needed, since, in many systems, each socket uses a file descriptor, needed, since, in many systems, each socket uses a file descriptor,
which are limited system resources. Garbage-collected objects are which are limited system resources. Garbage-collected objects are
automatically closed before destruction, though. automatically closed before destruction, though.
</p> </p>
<!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -172,19 +205,19 @@ master:<b>connect(</b>address, port<b>)</b>
<p class=description> <p class=description>
Attempts to connect a master object to a remote host, transforming it into a Attempts to connect a master object to a remote host, transforming it into a
client object. client object.
Client objects support methods Client objects support methods
<a href=#send><tt>send</tt></a>, <a href=#send><tt>send</tt></a>,
<a href=#receive><tt>receive</tt></a>, <a href=#receive><tt>receive</tt></a>,
<a href=#getsockname><tt>getsockname</tt></a>, <a href=#getsockname><tt>getsockname</tt></a>,
<a href=#getpeername><tt>getpeername</tt></a>, <a href=#getpeername><tt>getpeername</tt></a>,
<a href=#settimeout><tt>settimeout</tt></a>, <a href=#settimeout><tt>settimeout</tt></a>,
and <a href=#close><tt>close</tt></a>. and <a href=#close><tt>close</tt></a>.
</p> </p>
<p class=parameters> <p class=parameters>
<tt>Address</tt> can be an IP address or a host name. <tt>Address</tt> can be an IP address or a host name.
<tt>Port</tt> must be an integer number in the range [1..64K). <tt>Port</tt> must be an integer number in the range [1..64K).
</p> </p>
<p class=return> <p class=return>
@ -193,14 +226,14 @@ describing the error. In case of success, the method returns 1.
</p> </p>
<p class=note> <p class=note>
Note: The function <a href=socket.html#connect><tt>socket.connect</tt></a> Note: The function <a href=socket.html#connect><tt>socket.connect</tt></a>
is available and is a shortcut for the creation of client sockets. is available and is a shortcut for the creation of client sockets.
</p> </p>
<p class=note> <p class=note>
Note: Starting with LuaSocket 2.0, Note: Starting with LuaSocket 2.0,
the <a href=#settimeout><tt>settimeout</tt></a> the <a href=#settimeout><tt>settimeout</tt></a>
method affects the behavior of <tt>connect</tt>, causing it to return method affects the behavior of <tt>connect</tt>, causing it to return
with an error in case of a timeout. If that happens, you can still call <a with an error in case of a timeout. If that happens, you can still call <a
href=socket.html#select><tt>socket.select</tt></a> with the socket in the href=socket.html#select><tt>socket.select</tt></a> with the socket in the
<tt>sendt</tt> table. The socket will be writable when the connection is <tt>sendt</tt> table. The socket will be writable when the connection is
@ -227,10 +260,10 @@ Returns information about the remote side of a connected client object.
</p> </p>
<p class=return> <p class=return>
Returns a string with the IP address of the peer, the Returns a string with the IP address of the peer, the
port number that peer is using for the connection, port number that peer is using for the connection,
and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>").
In case of error, the method returns <b><tt>nil</tt></b>. In case of error, the method returns <b><tt>nil</tt></b>.
</p> </p>
<p class=note> <p class=note>
@ -246,13 +279,13 @@ server:<b>getsockname()</b>
</p> </p>
<p class=description> <p class=description>
Returns the local address information associated to the object. Returns the local address information associated to the object.
</p> </p>
<p class=return> <p class=return>
The method returns a string with local IP address, a number with The method returns a string with local IP address, a number with
the local port, the local port,
and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>").
In case of error, the method returns <b><tt>nil</tt></b>. In case of error, the method returns <b><tt>nil</tt></b>.
</p> </p>
@ -266,32 +299,32 @@ server:<b>getstats()</b><br>
<p class=description> <p class=description>
Returns accounting information on the socket, useful for throttling Returns accounting information on the socket, useful for throttling
of bandwidth. of bandwidth.
</p> </p>
<p class=return> <p class=return>
The method returns the number of bytes received, the number of bytes sent, The method returns the number of bytes received, the number of bytes sent,
and the age of the socket object in seconds. and the age of the socket object in seconds.
</p> </p>
<!-- listen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- listen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="listen"> <p class=name id="listen">
master:<b>listen(</b>backlog<b>)</b> master:<b>listen(</b>backlog<b>)</b>
</p> </p>
<p class=description> <p class=description>
Specifies the socket is willing to receive connections, transforming the Specifies the socket is willing to receive connections, transforming the
object into a server object. Server objects support the object into a server object. Server objects support the
<a href=#accept><tt>accept</tt></a>, <a href=#accept><tt>accept</tt></a>,
<a href=#getsockname><tt>getsockname</tt></a>, <a href=#getsockname><tt>getsockname</tt></a>,
<a href=#setoption><tt>setoption</tt></a>, <a href=#setoption><tt>setoption</tt></a>,
<a href=#settimeout><tt>settimeout</tt></a>, <a href=#settimeout><tt>settimeout</tt></a>,
and <a href=#close><tt>close</tt></a> methods. and <a href=#close><tt>close</tt></a> methods.
</p> </p>
<p class=parameters> <p class=parameters>
The parameter <tt>backlog</tt> specifies the number of client The parameter <tt>backlog</tt> specifies the number of client
connections that can connections that can
be queued waiting for service. If the queue is full and another client be queued waiting for service. If the queue is full and another client
attempts connection, the connection is refused. attempts connection, the connection is refused.
@ -310,11 +343,11 @@ client:<b>receive(</b>[pattern [, prefix]]<b>)</b>
<p class=description> <p class=description>
Reads data from a client object, according to the specified <em>read Reads data from a client object, according to the specified <em>read
pattern</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. pattern</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible.
</p> </p>
<p class=parameters> <p class=parameters>
<tt>Pattern</tt> can be any of the following: <tt>Pattern</tt> can be any of the following:
</p> </p>
<ul> <ul>
@ -325,7 +358,7 @@ terminated by a LF character (ASCII&nbsp;10), optionally preceded by a
CR character (ASCII&nbsp;13). The CR and LF characters are not included in CR character (ASCII&nbsp;13). The CR and LF characters are not included in
the returned line. In fact, <em>all</em> CR characters are the returned line. In fact, <em>all</em> CR characters are
ignored by the pattern. This is the default pattern; ignored by the pattern. This is the default pattern;
<li> <tt>number</tt>: causes the method to read a specified <tt>number</tt> <li> <tt>number</tt>: causes the method to read a specified <tt>number</tt>
of bytes from the socket. of bytes from the socket.
</ul> </ul>
@ -347,10 +380,10 @@ closed before the transmission was completed or the string
<p class=note> <p class=note>
<b>Important note</b>: This function was changed <em>severely</em>. It used <b>Important note</b>: This function was changed <em>severely</em>. It used
to support multiple patterns (but I have never seen this feature used) and to support multiple patterns (but I have never seen this feature used) and
now it doesn't anymore. Partial results used to be returned in the same now it doesn't anymore. Partial results used to be returned in the same
way as successful results. This last feature violated the idea that all way as successful results. This last feature violated the idea that all
functions should return <tt><b>nil</b></tt> on error. Thus it was changed functions should return <tt><b>nil</b></tt> on error. Thus it was changed
too. too.
</p> </p>
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -366,7 +399,7 @@ Sends <tt>data</tt> through client object.
<p class=parameters> <p class=parameters>
<tt>Data</tt> is the string to be sent. The optional arguments <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>i</tt> and <tt>j</tt> work exactly like the standard
<tt>string.sub</tt> Lua function to allow the selection of a <tt>string.sub</tt> Lua function to allow the selection of a
substring to be sent. substring to be sent.
</p> </p>
@ -385,10 +418,10 @@ there was a timeout during the operation.
</p> </p>
<p class=note> <p class=note>
Note: Output is <em>not</em> buffered. For small strings, Note: Output is <em>not</em> buffered. For small strings,
it is always better to concatenate them in Lua it is always better to concatenate them in Lua
(with the '<tt>..</tt>' operator) and send the result in one call (with the '<tt>..</tt>' operator) and send the result in one call
instead of calling the method several times. instead of calling the method several times.
</p> </p>
<!-- setoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- setoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -400,12 +433,12 @@ server:<b>setoption(</b>option [, value]<b>)</b>
<p class=description> <p class=description>
Sets options for the TCP object. Options are only needed by low-level or Sets options for the TCP object. Options are only needed by low-level or
time-critical applications. You should only modify an option if you time-critical applications. You should only modify an option if you
are sure you need it. are sure you need it.
</p> </p>
<p class=parameters> <p class=parameters>
<tt>Option</tt> is a string with the option name, and <tt>value</tt> <tt>Option</tt> is a string with the option name, and <tt>value</tt>
depends on the option being set: depends on the option being set:
<ul> <ul>
@ -413,7 +446,7 @@ depends on the option being set:
<li> '<tt>keepalive</tt>': Setting this option to <tt>true</tt> enables <li> '<tt>keepalive</tt>': Setting this option to <tt>true</tt> enables
the periodic transmission of messages on a connected socket. Should the the periodic transmission of messages on a connected socket. Should the
connected party fail to respond to these messages, the connection is connected party fail to respond to these messages, the connection is
considered broken and processes using the socket are notified; considered broken and processes using the socket are notified;
<li> '<tt>linger</tt>': Controls the action taken when unsent data are <li> '<tt>linger</tt>': Controls the action taken when unsent data are
queued on a socket and a close is performed. The value is a table with a queued on a socket and a close is performed. The value is a table with a
@ -424,13 +457,13 @@ it is able to transmit the data or until '<tt>timeout</tt>' has passed. If
'<tt>on</tt>' is <tt>false</tt> and a close is issued, the system will '<tt>on</tt>' is <tt>false</tt> and a close is issued, the system will
process the close in a manner that allows the process to continue as process the close in a manner that allows the process to continue as
quickly as possible. I do not advise you to set this to anything other than quickly as possible. I do not advise you to set this to anything other than
zero; zero;
<li> '<tt>reuseaddr</tt>': Setting this option indicates that the rules <li> '<tt>reuseaddr</tt>': Setting this option indicates that the rules
used in validating addresses supplied in a call to used in validating addresses supplied in a call to
<a href=#bind><tt>bind</tt></a> should allow reuse of local addresses; <a href=#bind><tt>bind</tt></a> should allow reuse of local addresses;
<li> '<tt>tcp-nodelay</tt>': Setting this option to <tt>true</tt> <li> '<tt>tcp-nodelay</tt>': Setting this option to <tt>true</tt>
disables the Nagle's algorithm for the connection; disables the Nagle's algorithm for the connection;
<li> '<tt>ipv6-v6only</tt>': <li> '<tt>ipv6-v6only</tt>':
@ -485,7 +518,7 @@ server:<b>setstats(</b>received, sent, age<b>)</b><br>
<p class=description> <p class=description>
Resets accounting information on the socket, useful for throttling Resets accounting information on the socket, useful for throttling
of bandwidth. of bandwidth.
</p> </p>
<p class=parameters> <p class=parameters>
@ -495,7 +528,7 @@ of bandwidth.
</p> </p>
<p class=return> <p class=return>
The method returns 1 in case of success and <tt><b>nil</b></tt> otherwise. The method returns 1 in case of success and <tt><b>nil</b></tt> otherwise.
</p> </p>
<!-- settimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- settimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -509,8 +542,8 @@ server:<b>settimeout(</b>value [, mode]<b>)</b>
<p class=description> <p class=description>
Changes the timeout values for the object. By default, Changes the timeout values for the object. By default,
all I/O operations are blocking. That is, any call to the methods all I/O operations are blocking. That is, any call to the methods
<a href=#send><tt>send</tt></a>, <a href=#send><tt>send</tt></a>,
<a href=#receive><tt>receive</tt></a>, and <a href=#receive><tt>receive</tt></a>, and
<a href=#accept><tt>accept</tt></a> <a href=#accept><tt>accept</tt></a>
will block indefinitely, until the operation completes. The will block indefinitely, until the operation completes. The
<tt>settimeout</tt> method defines a limit on the amount of time the <tt>settimeout</tt> method defines a limit on the amount of time the
@ -521,7 +554,7 @@ time has elapsed, the affected methods give up and fail with an error code.
<p class=parameters> <p class=parameters>
The amount of time to wait is specified as the The amount of time to wait is specified as the
<tt>value</tt> parameter, in seconds. There are two timeout modes and <tt>value</tt> parameter, in seconds. There are two timeout modes and
both can be used together for fine tuning: both can be used together for fine tuning:
</p> </p>
<ul> <ul>
@ -532,7 +565,7 @@ default mode;</li>
<li> '<tt>t</tt>': <em>total</em> timeout. Specifies the upper limit on <li> '<tt>t</tt>': <em>total</em> timeout. Specifies the upper limit on
the amount of time LuaSocket can block a Lua script before returning from the amount of time LuaSocket can block a Lua script before returning from
a call.</li> a call.</li>
</ul> </ul>
<p class=parameters> <p class=parameters>
@ -562,7 +595,7 @@ client:<b>shutdown(</b>mode<b>)</b><br>
</p> </p>
<p class=description> <p class=description>
Shuts down part of a full-duplex connection. Shuts down part of a full-duplex connection.
</p> </p>
<p class=parameters> <p class=parameters>
@ -608,7 +641,7 @@ server:<b>getfd()</b>
</p> </p>
<p class=description> <p class=description>
Returns the underling socket descriptor or handle associated to the object. Returns the underling socket descriptor or handle associated to the object.
</p> </p>
<p class=return> <p class=return>

View File

@ -4,7 +4,7 @@
<head> <head>
<meta name="description" content="LuaSocket: The UDP support"> <meta name="description" content="LuaSocket: The UDP support">
<meta name="keywords" content="Lua, LuaSocket, Socket, UDP, Library, Network, Support"> <meta name="keywords" content="Lua, LuaSocket, Socket, UDP, Library, Network, Support">
<title>LuaSocket: UDP support</title> <title>LuaSocket: UDP support</title>
<link rel="stylesheet" href="reference.css" type="text/css"> <link rel="stylesheet" href="reference.css" type="text/css">
</head> </head>
@ -28,7 +28,7 @@
<a href="index.html#download">download</a> &middot; <a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot; <a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot; <a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a> <a href="reference.html">reference</a>
</p> </p>
</center> </center>
<hr> <hr>
@ -37,7 +37,7 @@
<!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id="udp">UDP</h2> <h2 id="udp">UDP</h2>
<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
@ -46,19 +46,62 @@ socket.<b>udp()</b>
</p> </p>
<p class="description"> <p class="description">
Creates and returns an unconnected IPv4 UDP object. Creates and returns an unconnected UDP object.
Unconnected objects support the Unconnected objects support the
<a href="#sendto"><tt>sendto</tt></a>, <a href="#sendto"><tt>sendto</tt></a>,
<a href="#receive"><tt>receive</tt></a>, <a href="#receive"><tt>receive</tt></a>,
<a href="#receivefrom"><tt>receivefrom</tt></a>, <a href="#receivefrom"><tt>receivefrom</tt></a>,
<a href="#getoption"><tt>getoption</tt></a>, <a href="#getoption"><tt>getoption</tt></a>,
<a href="#getsockname"><tt>getsockname</tt></a>, <a href="#getsockname"><tt>getsockname</tt></a>,
<a href="#setoption"><tt>setoption</tt></a>, <a href="#setoption"><tt>setoption</tt></a>,
<a href="#settimeout"><tt>settimeout</tt></a>, <a href="#settimeout"><tt>settimeout</tt></a>,
<a href="#setpeername"><tt>setpeername</tt></a>, <a href="#setpeername"><tt>setpeername</tt></a>,
<a href="#setsockname"><tt>setsockname</tt></a>, and <a href="#setsockname"><tt>setsockname</tt></a>, and
<a href="#close"><tt>close</tt></a>. <a href="#close"><tt>close</tt></a>.
The <a href="#setpeername"><tt>setpeername</tt></a> The <a href="#setpeername"><tt>setpeername</tt></a>
is used to connect the object.
</p>
<p class="return">
In case of success, a new unconnected UDP object
returned. In case of error, <b><tt>nil</tt></b> is returned, followed by
an error message.
</p>
<p class=note>
Note: The choice between IPv4 and IPv6 happens during a call to
<a href=#sendto><tt>sendto</tt></a>, <a
href=#setpeername><tt>setpeername</tt></a>, or <a
href=#setsockname><tt>sockname</tt></a>, depending on the address
family obtained from the resolver.
</p>
<p class=note>
Note: Before the choice between IPv4 and IPv6 happens,
the internal socket object is invalid and therefore <a
href=#setoption><tt>setoption</tt></a> will fail.
</p>
<!-- socket.udp4 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="socket.udp">
socket.<b>udp4()</b>
</p>
<p class="description">
Creates and returns an unconnected IPv4 UDP object.
Unconnected objects support the
<a href="#sendto"><tt>sendto</tt></a>,
<a href="#receive"><tt>receive</tt></a>,
<a href="#receivefrom"><tt>receivefrom</tt></a>,
<a href="#getoption"><tt>getoption</tt></a>,
<a href="#getsockname"><tt>getsockname</tt></a>,
<a href="#setoption"><tt>setoption</tt></a>,
<a href="#settimeout"><tt>settimeout</tt></a>,
<a href="#setpeername"><tt>setpeername</tt></a>,
<a href="#setsockname"><tt>setsockname</tt></a>, and
<a href="#close"><tt>close</tt></a>.
The <a href="#setpeername"><tt>setpeername</tt></a>
is used to connect the object. is used to connect the object.
</p> </p>
@ -75,19 +118,19 @@ socket.<b>udp6()</b>
</p> </p>
<p class="description"> <p class="description">
Creates and returns an unconnected IPv6 UDP object. Creates and returns an unconnected IPv6 UDP object.
Unconnected objects support the Unconnected objects support the
<a href="#sendto"><tt>sendto</tt></a>, <a href="#sendto"><tt>sendto</tt></a>,
<a href="#receive"><tt>receive</tt></a>, <a href="#receive"><tt>receive</tt></a>,
<a href="#receivefrom"><tt>receivefrom</tt></a>, <a href="#receivefrom"><tt>receivefrom</tt></a>,
<a href="#getoption"><tt>getoption</tt></a>, <a href="#getoption"><tt>getoption</tt></a>,
<a href="#getsockname"><tt>getsockname</tt></a>, <a href="#getsockname"><tt>getsockname</tt></a>,
<a href="#setoption"><tt>setoption</tt></a>, <a href="#setoption"><tt>setoption</tt></a>,
<a href="#settimeout"><tt>settimeout</tt></a>, <a href="#settimeout"><tt>settimeout</tt></a>,
<a href="#setpeername"><tt>setpeername</tt></a>, <a href="#setpeername"><tt>setpeername</tt></a>,
<a href="#setsockname"><tt>setsockname</tt></a>, and <a href="#setsockname"><tt>setsockname</tt></a>, and
<a href="#close"><tt>close</tt></a>. <a href="#close"><tt>close</tt></a>.
The <a href="#setpeername"><tt>setpeername</tt></a> The <a href="#setpeername"><tt>setpeername</tt></a>
is used to connect the object. is used to connect the object.
</p> </p>
@ -102,10 +145,6 @@ Note: The TCP object returned will have the option
"<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>. "<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>.
</p> </p>
<!-- close +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<!-- close +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- close +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="close"> <p class="name" id="close">
@ -142,10 +181,10 @@ associated with a connected UDP object.
<p class=return> <p class=return>
Returns a string with the IP address of the peer, the Returns a string with the IP address of the peer, the
port number that peer is using for the connection, port number that peer is using for the connection,
and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>").
In case of error, the method returns <b><tt>nil</tt></b>. In case of error, the method returns <b><tt>nil</tt></b>.
</p> </p>
<p class="note"> <p class="note">
@ -165,9 +204,9 @@ Returns the local address information associated to the object.
<p class=return> <p class=return>
The method returns a string with local IP address, a number with The method returns a string with local IP address, a number with
the local port, the local port,
and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>").
In case of error, the method returns <b><tt>nil</tt></b>. In case of error, the method returns <b><tt>nil</tt></b>.
</p> </p>
@ -217,7 +256,7 @@ unconnected:<b>receivefrom(</b>[size]<b>)</b>
</p> </p>
<p class="description"> <p class="description">
Works exactly as the <a href="#receive"><tt>receive</tt></a> Works exactly as the <a href="#receive"><tt>receive</tt></a>
method, except it returns the IP method, except it returns the IP
address and port as extra return values (and is therefore slightly less address and port as extra return values (and is therefore slightly less
efficient). efficient).
@ -236,7 +275,7 @@ See <a href=#setoption><tt>setoption</tt></a> for
description of the option names and values. description of the option names and values.
</p> </p>
<p class="parameters"><tt>Option</tt> is a string with the option name. <p class="parameters"><tt>Option</tt> is a string with the option name.
<ul> <ul>
<li> '<tt>dontroute</tt>' <li> '<tt>dontroute</tt>'
<li> '<tt>broadcast</tt>' <li> '<tt>broadcast</tt>'
@ -246,9 +285,9 @@ description of the option names and values.
<li> '<tt>ipv6-v6only</tt>' <li> '<tt>ipv6-v6only</tt>'
<li> '<tt>ip-multicast-if</tt>' <li> '<tt>ip-multicast-if</tt>'
<li> '<tt>ip-multicast-ttl</tt>' <li> '<tt>ip-multicast-ttl</tt>'
<li> '<tt>ip-add-membership</tt>' <li> '<tt>ip-add-membership</tt>'
<li> '<tt>ip-drop-membership</tt>' <li> '<tt>ip-drop-membership</tt>'
</ul> </ul>
</p> </p>
<p class=return> <p class=return>
@ -268,7 +307,7 @@ Sends a datagram to the UDP peer of a connected object.
</p> </p>
<p class="parameters"> <p class="parameters">
<tt>Datagram</tt> is a string with the datagram contents. <tt>Datagram</tt> is a string with the datagram contents.
The maximum datagram size for UDP is 64K minus IP layer overhead. The maximum datagram size for UDP is 64K minus IP layer overhead.
However datagrams larger than the link layer packet size will be However datagrams larger than the link layer packet size will be
fragmented, which may deteriorate performance and/or reliability. fragmented, which may deteriorate performance and/or reliability.
@ -298,11 +337,11 @@ Sends a datagram to the specified IP address and port number.
<p class="parameters"> <p class="parameters">
<tt>Datagram</tt> is a string with the <tt>Datagram</tt> is a string with the
datagram contents. datagram contents.
The maximum datagram size for UDP is 64K minus IP layer overhead. The maximum datagram size for UDP is 64K minus IP layer overhead.
However datagrams larger than the link layer packet size will be However datagrams larger than the link layer packet size will be
fragmented, which may deteriorate performance and/or reliability. fragmented, which may deteriorate performance and/or reliability.
<tt>Ip</tt> is the IP address of the recipient. <tt>Ip</tt> is the IP address of the recipient.
Host names are <em>not</em> allowed for performance reasons. Host names are <em>not</em> allowed for performance reasons.
<tt>Port</tt> is the port number at the recipient. <tt>Port</tt> is the port number at the recipient.
@ -337,9 +376,9 @@ object or vice versa.
For connected objects, outgoing datagrams For connected objects, outgoing datagrams
will be sent to the specified peer, and datagrams received from will be sent to the specified peer, and datagrams received from
other peers will be discarded by the OS. Connected UDP objects must other peers will be discarded by the OS. Connected UDP objects must
use the <a href="#send"><tt>send</tt></a> and use the <a href="#send"><tt>send</tt></a> and
<a href="#receive"><tt>receive</tt></a> methods instead of <a href="#receive"><tt>receive</tt></a> methods instead of
<a href="#sendto"><tt>sendto</tt></a> and <a href="#sendto"><tt>sendto</tt></a> and
<a href="#receivefrom"><tt>receivefrom</tt></a>. <a href="#receivefrom"><tt>receivefrom</tt></a>.
</p> </p>
@ -421,16 +460,16 @@ only modify an option if you are sure you need it.</p>
name, and <tt>value</tt> depends on the option being set: name, and <tt>value</tt> depends on the option being set:
</p> </p>
<ul> <ul>
<li> '<tt>dontroute</tt>': Indicates that outgoing <li> '<tt>dontroute</tt>': Indicates that outgoing
messages should bypass the standard routing facilities. messages should bypass the standard routing facilities.
Receives a boolean value; Receives a boolean value;
<li> '<tt>broadcast</tt>': Requests permission to send <li> '<tt>broadcast</tt>': Requests permission to send
broadcast datagrams on the socket. broadcast datagrams on the socket.
Receives a boolean value; Receives a boolean value;
<li> '<tt>reuseaddr</tt>': Indicates that the rules used in <li> '<tt>reuseaddr</tt>': Indicates that the rules used in
validating addresses supplied in a <tt>bind()</tt> call validating addresses supplied in a <tt>bind()</tt> call
should allow reuse of local addresses. should allow reuse of local addresses.
Receives a boolean value; Receives a boolean value;
<li> '<tt>reuseport</tt>': Allows completely duplicate <li> '<tt>reuseport</tt>': Allows completely duplicate
bindings by multiple processes if they all set bindings by multiple processes if they all set
@ -442,7 +481,7 @@ datagram is delivered to the sending host as long as it is a
member of the multicast group. member of the multicast group.
Receives a boolean value; Receives a boolean value;
<li> '<tt>ipv6-v6only</tt>': <li> '<tt>ipv6-v6only</tt>':
Specifies whether to restrict <tt>inet6</tt> sockets to Specifies whether to restrict <tt>inet6</tt> sockets to
sending and receiving only IPv6 packets. sending and receiving only IPv6 packets.
Receive a boolean value; Receive a boolean value;
<li> '<tt>ip-multicast-if</tt>': <li> '<tt>ip-multicast-if</tt>':
@ -451,9 +490,9 @@ are sent.
Receives an IP address; Receives an IP address;
<li> '<tt>ip-multicast-ttl</tt>': <li> '<tt>ip-multicast-ttl</tt>':
Sets the Time To Live in the IP header for outgoing Sets the Time To Live in the IP header for outgoing
multicast datagrams. multicast datagrams.
Receives a number; Receives a number;
<li> '<tt>ip-add-membership</tt>': <li> '<tt>ip-add-membership</tt>':
Joins the multicast group specified. Joins the multicast group specified.
Receives a table with fields Receives a table with fields
<tt>multiaddr</tt> and <tt>interface</tt>, each containing an <tt>multiaddr</tt> and <tt>interface</tt>, each containing an
@ -463,7 +502,7 @@ group specified.
Receives a table with fields Receives a table with fields
<tt>multiaddr</tt> and <tt>interface</tt>, each containing an <tt>multiaddr</tt> and <tt>interface</tt>, each containing an
IP address. IP address.
</ul> </ul>
<p class="return"> <p class="return">
The method returns 1 in case of success, or The method returns 1 in case of success, or
@ -482,14 +521,14 @@ unconnected:<b>settimeout(</b>value<b>)</b>
</p> </p>
<p class="description"> <p class="description">
Changes the timeout values for the object. By default, the Changes the timeout values for the object. By default, the
<a href="#receive"><tt>receive</tt></a> and <a href="#receive"><tt>receive</tt></a> and
<a href="#receivefrom"><tt>receivefrom</tt></a> <a href="#receivefrom"><tt>receivefrom</tt></a>
operations are blocking. That is, any call to the methods will block operations are blocking. That is, any call to the methods will block
indefinitely, until data arrives. The <tt>settimeout</tt> function defines indefinitely, until data arrives. The <tt>settimeout</tt> function defines
a limit on the amount of time the functions can block. When a timeout is a limit on the amount of time the functions can block. When a timeout is
set and the specified amount of time has elapsed, the affected methods set and the specified amount of time has elapsed, the affected methods
give up and fail with an error code. give up and fail with an error code.
</p> </p>
<p class="parameters"> <p class="parameters">
@ -524,7 +563,7 @@ imperative nature obvious.
<a href="index.html#download">download</a> &middot; <a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot; <a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot; <a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a> <a href="reference.html">reference</a>
</p> </p>
<p> <p>
<small> <small>

View File

@ -38,7 +38,7 @@ int buffer_open(lua_State *L) {
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Initializes C structure * Initializes C structure
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
void buffer_init(p_buffer buf, p_io io, p_timeout tm) { void buffer_init(p_buffer buf, p_io io, p_timeout tm) {
buf->first = buf->last = 0; buf->first = buf->last = 0;
@ -62,8 +62,8 @@ int buffer_meth_getstats(lua_State *L, p_buffer buf) {
* object:setstats() interface * object:setstats() interface
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
int buffer_meth_setstats(lua_State *L, p_buffer buf) { int buffer_meth_setstats(lua_State *L, p_buffer buf) {
buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received);
buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent);
if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4); if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4);
lua_pushnumber(L, 1); lua_pushnumber(L, 1);
return 1; return 1;
@ -88,7 +88,7 @@ int buffer_meth_send(lua_State *L, p_buffer buf) {
/* 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, (lua_Number) (sent+start-1)); lua_pushnumber(L, (lua_Number) (sent+start-1));
} else { } else {
lua_pushnumber(L, (lua_Number) (sent+start-1)); lua_pushnumber(L, (lua_Number) (sent+start-1));
@ -111,7 +111,7 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) {
size_t size; size_t size;
const char *part = luaL_optlstring(L, 3, "", &size); const char *part = luaL_optlstring(L, 3, "", &size);
timeout_markstart(buf->tm); timeout_markstart(buf->tm);
/* initialize buffer with optional extra prefix /* initialize buffer with optional extra prefix
* (useful for concatenating previous partial results) */ * (useful for concatenating previous partial results) */
luaL_buffinit(L, &b); luaL_buffinit(L, &b);
luaL_addlstring(&b, part, size); luaL_addlstring(&b, part, size);
@ -119,12 +119,12 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) {
if (!lua_isnumber(L, 2)) { if (!lua_isnumber(L, 2)) {
const char *p= luaL_optstring(L, 2, "*l"); const char *p= luaL_optstring(L, 2, "*l");
if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);
else luaL_argcheck(L, 0, 2, "invalid receive pattern"); else luaL_argcheck(L, 0, 2, "invalid receive pattern");
/* get a fixed number of bytes (minus what was already partially /* get a fixed number of bytes (minus what was already partially
* received) */ * received) */
} else { } else {
double n = lua_tonumber(L, 2); double n = lua_tonumber(L, 2);
size_t wanted = (size_t) n; size_t wanted = (size_t) n;
luaL_argcheck(L, n >= 0, 2, "invalid receive pattern"); luaL_argcheck(L, n >= 0, 2, "invalid receive pattern");
if (size == 0 || wanted > size) if (size == 0 || wanted > size)
@ -135,8 +135,8 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) {
/* we can't push anyting in the stack before pushing the /* we can't push anyting in the stack before pushing the
* contents of the buffer. this is the reason for the complication */ * contents of the buffer. this is the reason for the complication */
luaL_pushresult(&b); luaL_pushresult(&b);
lua_pushstring(L, buf->io->error(buf->io->ctx, err)); lua_pushstring(L, buf->io->error(buf->io->ctx, err));
lua_pushvalue(L, -2); lua_pushvalue(L, -2);
lua_pushnil(L); lua_pushnil(L);
lua_replace(L, -4); lua_replace(L, -4);
} else { } else {
@ -219,7 +219,7 @@ static int recvall(p_buffer buf, luaL_Buffer *b) {
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF * Reads a line terminated by a CR LF pair or just by a LF. The CR and LF
* are not returned by the function and are discarded from the buffer * are not returned by the function and are discarded from the buffer
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static int recvline(p_buffer buf, luaL_Buffer *b) { static int recvline(p_buffer buf, luaL_Buffer *b) {
@ -249,7 +249,7 @@ static int recvline(p_buffer buf, luaL_Buffer *b) {
static void buffer_skip(p_buffer buf, size_t count) { static void buffer_skip(p_buffer buf, size_t count) {
buf->received += count; buf->received += count;
buf->first += count; buf->first += count;
if (buffer_isempty(buf)) if (buffer_isempty(buf))
buf->first = buf->last = 0; buf->first = buf->last = 0;
} }

View File

@ -94,7 +94,7 @@ static int inet_global_getnameinfo(lua_State *L) {
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC; hints.ai_family = AF_UNSPEC;
ret = getaddrinfo(host, serv, &hints, &resolved); ret = getaddrinfo(host, serv, &hints, &resolved);
if (ret != 0) { if (ret != 0) {
@ -105,8 +105,8 @@ static int inet_global_getnameinfo(lua_State *L) {
lua_newtable(L); lua_newtable(L);
for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) { for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) {
getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen, getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen,
hbuf, host? (socklen_t) sizeof(hbuf): 0, hbuf, host? (socklen_t) sizeof(hbuf): 0,
sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0); sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0);
if (host) { if (host) {
lua_pushnumber(L, i); lua_pushnumber(L, i);
@ -146,7 +146,7 @@ static int inet_global_toip(lua_State *L)
int inet_optfamily(lua_State* L, int narg, const char* def) int inet_optfamily(lua_State* L, int narg, const char* def)
{ {
static const char* optname[] = { "unspec", "inet", "inet6", NULL }; static const char* optname[] = { "unspec", "inet", "inet6", NULL };
static int optvalue[] = { PF_UNSPEC, PF_INET, PF_INET6, 0 }; static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 };
return optvalue[luaL_checkoption(L, narg, def, optname)]; return optvalue[luaL_checkoption(L, narg, def, optname)];
} }
@ -167,7 +167,7 @@ static int inet_global_getaddrinfo(lua_State *L)
int i = 1, ret = 0; int i = 1, ret = 0;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC; hints.ai_family = AF_UNSPEC;
ret = getaddrinfo(hostname, NULL, &hints, &resolved); ret = getaddrinfo(hostname, NULL, &hints, &resolved);
if (ret != 0) { if (ret != 0) {
lua_pushnil(L); lua_pushnil(L);
@ -177,7 +177,7 @@ static int inet_global_getaddrinfo(lua_State *L)
lua_newtable(L); lua_newtable(L);
for (iterator = resolved; iterator; iterator = iterator->ai_next) { for (iterator = resolved; iterator; iterator = iterator->ai_next) {
char hbuf[NI_MAXHOST]; char hbuf[NI_MAXHOST];
ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen, ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen,
hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST); hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
if (ret){ if (ret){
freeaddrinfo(resolved); freeaddrinfo(resolved);
@ -198,6 +198,16 @@ static int inet_global_getaddrinfo(lua_State *L)
lua_pushliteral(L, "inet6"); lua_pushliteral(L, "inet6");
lua_settable(L, -3); lua_settable(L, -3);
break; break;
case AF_UNSPEC:
lua_pushliteral(L, "family");
lua_pushliteral(L, "unspec");
lua_settable(L, -3);
break;
default:
lua_pushliteral(L, "family");
lua_pushliteral(L, "unknown");
lua_settable(L, -3);
break;
} }
lua_pushliteral(L, "addr"); lua_pushliteral(L, "addr");
lua_pushstring(L, hbuf); lua_pushstring(L, hbuf);
@ -254,12 +264,11 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family)
} }
lua_pushstring(L, name); lua_pushstring(L, name);
lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10)); lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10));
if (family == PF_INET) { switch (family) {
lua_pushliteral(L, "inet"); case AF_INET: lua_pushliteral(L, "inet"); break;
} else if (family == PF_INET6) { case AF_INET6: lua_pushliteral(L, "inet6"); break;
lua_pushliteral(L, "inet6"); case AF_UNSPEC: lua_pushliteral(L, "unspec"); break;
} else { default: lua_pushliteral(L, "unknown"); break;
lua_pushliteral(L, "uknown family");
} }
return 3; return 3;
} }
@ -279,7 +288,7 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family)
lua_pushstring(L, socket_strerror(errno)); lua_pushstring(L, socket_strerror(errno));
return 2; return 2;
} }
err=getnameinfo((struct sockaddr *)&peer, peer_len, err=getnameinfo((struct sockaddr *)&peer, peer_len,
name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV);
if (err) { if (err) {
lua_pushnil(L); lua_pushnil(L);
@ -288,12 +297,11 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family)
} }
lua_pushstring(L, name); lua_pushstring(L, name);
lua_pushstring(L, port); lua_pushstring(L, port);
if (family == PF_INET) { switch (family) {
lua_pushliteral(L, "inet"); case AF_INET: lua_pushliteral(L, "inet"); break;
} else if (family == PF_INET6) { case AF_INET6: lua_pushliteral(L, "inet6"); break;
lua_pushliteral(L, "inet6"); case AF_UNSPEC: lua_pushliteral(L, "unspec"); break;
} else { default: lua_pushliteral(L, "unknown"); break;
lua_pushliteral(L, "uknown family");
} }
return 3; return 3;
} }
@ -344,8 +352,13 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp)
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Tries to create a new inet socket * Tries to create a new inet socket
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *inet_trycreate(p_socket ps, int family, int type) { const char *inet_trycreate(p_socket ps, int family, int type, int protocol) {
return socket_strerror(socket_create(ps, family, type, 0)); const char *err = socket_strerror(socket_create(ps, family, type, protocol));
if (err == NULL && family == AF_INET6) {
int yes = 1;
setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes));
}
return err;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
@ -354,21 +367,21 @@ const char *inet_trycreate(p_socket ps, int family, int type) {
const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm)
{ {
switch (family) { switch (family) {
case PF_INET: { case AF_INET: {
struct sockaddr_in sin; struct sockaddr_in sin;
memset((char *) &sin, 0, sizeof(sin)); memset((char *) &sin, 0, sizeof(sin));
sin.sin_family = AF_UNSPEC; sin.sin_family = AF_UNSPEC;
sin.sin_addr.s_addr = INADDR_ANY; sin.sin_addr.s_addr = INADDR_ANY;
return socket_strerror(socket_connect(ps, (SA *) &sin, return socket_strerror(socket_connect(ps, (SA *) &sin,
sizeof(sin), tm)); sizeof(sin), tm));
} }
case PF_INET6: { case AF_INET6: {
struct sockaddr_in6 sin6; struct sockaddr_in6 sin6;
struct in6_addr addrany = IN6ADDR_ANY_INIT; struct in6_addr addrany = IN6ADDR_ANY_INIT;
memset((char *) &sin6, 0, sizeof(sin6)); memset((char *) &sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_UNSPEC; sin6.sin6_family = AF_UNSPEC;
sin6.sin6_addr = addrany; sin6.sin6_addr = addrany;
return socket_strerror(socket_connect(ps, (SA *) &sin6, return socket_strerror(socket_connect(ps, (SA *) &sin6,
sizeof(sin6), tm)); sizeof(sin6), tm));
} }
} }
@ -383,6 +396,7 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address,
{ {
struct addrinfo *iterator = NULL, *resolved = NULL; struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL; const char *err = NULL;
int current_family = *family;
/* try resolving */ /* try resolving */
err = socket_gaistrerror(getaddrinfo(address, serv, err = socket_gaistrerror(getaddrinfo(address, serv,
connecthints, &resolved)); connecthints, &resolved));
@ -397,23 +411,23 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address,
* that shows up while iterating. if there was a * that shows up while iterating. if there was a
* bind, all families will be the same and we will * bind, all families will be the same and we will
* not enter this branch. */ * not enter this branch. */
if (*family != iterator->ai_family) { if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) {
socket_destroy(ps); socket_destroy(ps);
err = socket_strerror(socket_create(ps, iterator->ai_family, err = inet_trycreate(ps, iterator->ai_family,
iterator->ai_socktype, iterator->ai_protocol)); iterator->ai_socktype, iterator->ai_protocol);
if (err != NULL) { if (err) continue;
freeaddrinfo(resolved); current_family = iterator->ai_family;
return err; /* set non-blocking before connect */
}
*family = iterator->ai_family;
/* all sockets initially non-blocking */
socket_setnonblocking(ps); socket_setnonblocking(ps);
} }
/* try connecting to remote address */ /* try connecting to remote address */
err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr,
(socklen_t) iterator->ai_addrlen, tm)); (socklen_t) iterator->ai_addrlen, tm));
/* if success, break out of loop */ /* if success, break out of loop */
if (err == NULL) break; if (err == NULL) {
*family = current_family;
break;
}
} }
freeaddrinfo(resolved); freeaddrinfo(resolved);
/* here, if err is set, we failed */ /* here, if err is set, we failed */
@ -423,29 +437,27 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address,
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Tries to accept a socket * Tries to accept a socket
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *inet_tryaccept(p_socket server, int family, p_socket client, const char *inet_tryaccept(p_socket server, int family, p_socket client,
p_timeout tm) p_timeout tm) {
{
socklen_t len; socklen_t len;
t_sockaddr_storage addr; t_sockaddr_storage addr;
if (family == PF_INET6) { switch (family) {
len = sizeof(struct sockaddr_in6); case AF_INET6: len = sizeof(struct sockaddr_in6); break;
} else { case AF_INET: len = sizeof(struct sockaddr_in); break;
len = sizeof(struct sockaddr_in); default: len = sizeof(addr); break;
} }
return socket_strerror(socket_accept(server, client, (SA *) &addr, return socket_strerror(socket_accept(server, client, (SA *) &addr,
&len, tm)); &len, tm));
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Tries to bind socket to (address, port) * Tries to bind socket to (address, port)
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
const char *inet_trybind(p_socket ps, const char *address, const char *serv, const char *inet_trybind(p_socket ps, int *family, const char *address,
struct addrinfo *bindhints) const char *serv, struct addrinfo *bindhints) {
{
struct addrinfo *iterator = NULL, *resolved = NULL; struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL; const char *err = NULL;
t_socket sock = *ps; int current_family = *family;
/* translate luasocket special values to C */ /* translate luasocket special values to C */
if (strcmp(address, "*") == 0) address = NULL; if (strcmp(address, "*") == 0) address = NULL;
if (!serv) serv = "0"; if (!serv) serv = "0";
@ -457,35 +469,32 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv,
} }
/* iterate over resolved addresses until one is good */ /* iterate over resolved addresses until one is good */
for (iterator = resolved; iterator; iterator = iterator->ai_next) { for (iterator = resolved; iterator; iterator = iterator->ai_next) {
if(sock == SOCKET_INVALID) { if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) {
err = socket_strerror(socket_create(&sock, iterator->ai_family, socket_destroy(ps);
iterator->ai_socktype, iterator->ai_protocol)); err = inet_trycreate(ps, iterator->ai_family,
if(err) iterator->ai_socktype, iterator->ai_protocol);
continue; if (err) continue;
current_family = iterator->ai_family;
} }
/* try binding to local address */ /* try binding to local address */
err = socket_strerror(socket_bind(&sock, err = socket_strerror(socket_bind(ps, (SA *) iterator->ai_addr,
(SA *) iterator->ai_addr,
(socklen_t) iterator->ai_addrlen)); (socklen_t) iterator->ai_addrlen));
/* keep trying unless bind succeeded */ /* keep trying unless bind succeeded */
if (err) { if (err == NULL) {
if(sock != *ps) *family = current_family;
socket_destroy(&sock); /* set to non-blocking after bind */
} else { socket_setnonblocking(ps);
/* remember what we connected to, particularly the family */
*bindhints = *iterator;
break; break;
} }
} }
/* cleanup and return error */ /* cleanup and return error */
freeaddrinfo(resolved); freeaddrinfo(resolved);
*ps = sock; /* here, if err is set, we failed */
return err; return err;
} }
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Some systems do not provide these so that we provide our own. * Some systems do not provide these so that we provide our own.
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
#ifdef LUASOCKET_INET_ATON #ifdef LUASOCKET_INET_ATON
int inet_aton(const char *cp, struct in_addr *inp) int inet_aton(const char *cp, struct in_addr *inp)
@ -510,7 +519,7 @@ int inet_aton(const char *cp, struct in_addr *inp)
#endif #endif
#ifdef LUASOCKET_INET_PTON #ifdef LUASOCKET_INET_PTON
int inet_pton(int af, const char *src, void *dst) int inet_pton(int af, const char *src, void *dst)
{ {
struct addrinfo hints, *res; struct addrinfo hints, *res;
int ret = 1; int ret = 1;
@ -527,7 +536,7 @@ int inet_pton(int af, const char *src, void *dst)
} else { } else {
ret = -1; ret = -1;
} }
freeaddrinfo(res); freeaddrinfo(res);
return ret; return ret;
} }

View File

@ -1,12 +1,12 @@
#ifndef INET_H #ifndef INET_H
#define INET_H #define INET_H
/*=========================================================================*\ /*=========================================================================*\
* Internet domain functions * Internet domain functions
* LuaSocket toolkit * LuaSocket toolkit
* *
* This module implements the creation and connection of internet domain * This module implements the creation and connection of internet domain
* sockets, on top of the socket.h interface, and the interface of with the * sockets, on top of the socket.h interface, and the interface of with the
* resolver. * resolver.
* *
* The function inet_aton is provided for the platforms where it is not * The function inet_aton is provided for the platforms where it is not
* available. The module also implements the interface of the internet * available. The module also implements the interface of the internet
@ -24,11 +24,11 @@
int inet_open(lua_State *L); int inet_open(lua_State *L);
const char *inet_trycreate(p_socket ps, int family, int type); const char *inet_trycreate(p_socket ps, int family, int type, int protocol);
const char *inet_tryconnect(p_socket ps, int *family, const char *address, const char *inet_tryconnect(p_socket ps, int *family, const char *address,
const char *serv, p_timeout tm, struct addrinfo *connecthints); const char *serv, p_timeout tm, struct addrinfo *connecthints);
const char *inet_trybind(p_socket ps, const char *address, const char *serv, const char *inet_trybind(p_socket ps, int *family, const char *address,
struct addrinfo *bindhints); const char *serv, struct addrinfo *bindhints);
const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm); const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm);
const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm); const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm);

View File

@ -22,7 +22,7 @@ enum {
IO_DONE = 0, /* operation completed successfully */ IO_DONE = 0, /* operation completed successfully */
IO_TIMEOUT = -1, /* operation timed out */ IO_TIMEOUT = -1, /* operation timed out */
IO_CLOSED = -2, /* the connection has been closed */ IO_CLOSED = -2, /* the connection has been closed */
IO_UNKNOWN = -3 IO_UNKNOWN = -3
}; };
/* interface to error message function */ /* interface to error message function */

View File

@ -32,23 +32,23 @@ function _M.bind(host, port, backlog)
err = "no info on address" err = "no info on address"
for i, alt in base.ipairs(addrinfo) do for i, alt in base.ipairs(addrinfo) do
if alt.family == "inet" then if alt.family == "inet" then
sock, err = socket.tcp() sock, err = socket.tcp4()
else else
sock, err = socket.tcp6() sock, err = socket.tcp6()
end end
if not sock then return nil, err end if not sock then return nil, err end
sock:setoption("reuseaddr", true) sock:setoption("reuseaddr", true)
res, err = sock:bind(alt.addr, port) res, err = sock:bind(alt.addr, port)
if not res then if not res then
sock:close() sock:close()
else else
res, err = sock:listen(backlog) res, err = sock:listen(backlog)
if not res then if not res then
sock:close() sock:close()
else else
return sock return sock
end end
end end
end end
return nil, err return nil, err
end end

123
src/tcp.c
View File

@ -18,6 +18,7 @@
* Internal function prototypes * Internal function prototypes
\*=========================================================================*/ \*=========================================================================*/
static int global_create(lua_State *L); static int global_create(lua_State *L);
static int global_create4(lua_State *L);
static int global_create6(lua_State *L); static int global_create6(lua_State *L);
static int global_connect(lua_State *L); static int global_connect(lua_State *L);
static int meth_connect(lua_State *L); static int meth_connect(lua_State *L);
@ -90,6 +91,7 @@ static t_opt optset[] = {
/* functions in library namespace */ /* functions in library namespace */
static luaL_Reg func[] = { static luaL_Reg func[] = {
{"tcp", global_create}, {"tcp", global_create},
{"tcp4", global_create4},
{"tcp6", global_create6}, {"tcp6", global_create6},
{"connect", global_connect}, {"connect", global_connect},
{NULL, NULL} {NULL, NULL}
@ -213,8 +215,7 @@ static int meth_accept(lua_State *L)
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Binds an object to an address * Binds an object to an address
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static int meth_bind(lua_State *L) static int meth_bind(lua_State *L) {
{
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1);
const char *address = luaL_checkstring(L, 2); const char *address = luaL_checkstring(L, 2);
const char *port = luaL_checkstring(L, 3); const char *port = luaL_checkstring(L, 3);
@ -224,7 +225,7 @@ static int meth_bind(lua_State *L)
bindhints.ai_socktype = SOCK_STREAM; bindhints.ai_socktype = SOCK_STREAM;
bindhints.ai_family = tcp->family; bindhints.ai_family = tcp->family;
bindhints.ai_flags = AI_PASSIVE; bindhints.ai_flags = AI_PASSIVE;
err = inet_trybind(&tcp->sock, address, port, &bindhints); err = inet_trybind(&tcp->sock, &tcp->family, address, port, &bindhints);
if (err) { if (err) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, err); lua_pushstring(L, err);
@ -237,8 +238,7 @@ static int meth_bind(lua_State *L)
/*-------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------*\
* Turns a master tcp object into a client object. * Turns a master tcp object into a client object.
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static int meth_connect(lua_State *L) static int meth_connect(lua_State *L) {
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
const char *address = luaL_checkstring(L, 2); const char *address = luaL_checkstring(L, 2);
const char *port = luaL_checkstring(L, 3); const char *port = luaL_checkstring(L, 3);
@ -249,7 +249,7 @@ static int meth_connect(lua_State *L)
/* make sure we try to connect only to the same family */ /* make sure we try to connect only to the same family */
connecthints.ai_family = tcp->family; connecthints.ai_family = tcp->family;
timeout_markstart(&tcp->tm); timeout_markstart(&tcp->tm);
err = inet_tryconnect(&tcp->sock, &tcp->family, address, port, err = inet_tryconnect(&tcp->sock, &tcp->family, address, port,
&tcp->tm, &connecthints); &tcp->tm, &connecthints);
/* have to set the class even if it failed due to non-blocking connects */ /* have to set the class even if it failed due to non-blocking connects */
auxiliar_setclass(L, "tcp{client}", 1); auxiliar_setclass(L, "tcp{client}", 1);
@ -279,9 +279,12 @@ static int meth_close(lua_State *L)
static int meth_getfamily(lua_State *L) static int meth_getfamily(lua_State *L)
{ {
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
if (tcp->family == PF_INET6) { if (tcp->family == AF_INET6) {
lua_pushliteral(L, "inet6"); lua_pushliteral(L, "inet6");
return 1; return 1;
} else if (tcp->family == AF_INET) {
lua_pushliteral(L, "inet4");
return 1;
} else { } else {
lua_pushliteral(L, "inet4"); lua_pushliteral(L, "inet4");
return 1; return 1;
@ -352,37 +355,36 @@ static int meth_settimeout(lua_State *L)
* Creates a master tcp object * Creates a master tcp object
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static int tcp_create(lua_State *L, int family) { static int tcp_create(lua_State *L, int family) {
t_socket sock; p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
const char *err = inet_trycreate(&sock, family, SOCK_STREAM); memset(tcp, 0, sizeof(t_tcp));
/* try to allocate a system socket */ /* set its type as master object */
if (!err) { auxiliar_setclass(L, "tcp{master}", -1);
/* allocate tcp object */ /* if family is AF_UNSPEC, we leave the socket invalid and
p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); * store AF_UNSPEC into family. This will allow it to later be
memset(tcp, 0, sizeof(t_tcp)); * replaced with an AF_INET6 or AF_INET socket upon first use. */
/* set its type as master object */ tcp->sock = SOCKET_INVALID;
auxiliar_setclass(L, "tcp{master}", -1); tcp->family = family;
/* initialize remaining structure fields */ io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv,
socket_setnonblocking(&sock); (p_error) socket_ioerror, &tcp->sock);
if (family == PF_INET6) { timeout_init(&tcp->tm, -1, -1);
int yes = 1; buffer_init(&tcp->buf, &tcp->io, &tcp->tm);
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, if (family != AF_UNSPEC) {
(void *)&yes, sizeof(yes)); const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0);
if (err != NULL) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
} }
tcp->sock = sock; socket_setnonblocking(&tcp->sock);
io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv,
(p_error) socket_ioerror, &tcp->sock);
timeout_init(&tcp->tm, -1, -1);
buffer_init(&tcp->buf, &tcp->io, &tcp->tm);
tcp->family = family;
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
} }
return 1;
} }
static int global_create(lua_State *L) { static int global_create(lua_State *L) {
return tcp_create(L, AF_UNSPEC);
}
static int global_create4(lua_State *L) {
return tcp_create(L, AF_INET); return tcp_create(L, AF_INET);
} }
@ -390,53 +392,6 @@ static int global_create6(lua_State *L) {
return tcp_create(L, AF_INET6); return tcp_create(L, AF_INET6);
} }
#if 0
static const char *tryconnect6(const char *remoteaddr, const char *remoteserv,
struct addrinfo *connecthints, p_tcp tcp) {
struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL;
/* try resolving */
err = socket_gaistrerror(getaddrinfo(remoteaddr, remoteserv,
connecthints, &resolved));
if (err != NULL) {
if (resolved) freeaddrinfo(resolved);
return err;
}
/* iterate over all returned addresses trying to connect */
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
p_timeout tm = timeout_markstart(&tcp->tm);
/* create new socket if necessary. if there was no
* bind, we need to create one for every new family
* that shows up while iterating. if there was a
* bind, all families will be the same and we will
* not enter this branch. */
if (tcp->family != iterator->ai_family) {
socket_destroy(&tcp->sock);
err = socket_strerror(socket_create(&tcp->sock,
iterator->ai_family, iterator->ai_socktype,
iterator->ai_protocol));
if (err != NULL) {
freeaddrinfo(resolved);
return err;
}
tcp->family = iterator->ai_family;
/* all sockets initially non-blocking */
socket_setnonblocking(&tcp->sock);
}
/* finally try connecting to remote address */
err = socket_strerror(socket_connect(&tcp->sock,
(SA *) iterator->ai_addr,
(socklen_t) iterator->ai_addrlen, tm));
/* if success, break out of loop */
if (err == NULL) break;
}
freeaddrinfo(resolved);
/* here, if err is set, we failed */
return err;
}
#endif
static int global_connect(lua_State *L) { static int global_connect(lua_State *L) {
const char *remoteaddr = luaL_checkstring(L, 1); const char *remoteaddr = luaL_checkstring(L, 1);
const char *remoteserv = luaL_checkstring(L, 2); const char *remoteserv = luaL_checkstring(L, 2);
@ -453,26 +408,26 @@ static int global_connect(lua_State *L) {
timeout_init(&tcp->tm, -1, -1); timeout_init(&tcp->tm, -1, -1);
buffer_init(&tcp->buf, &tcp->io, &tcp->tm); buffer_init(&tcp->buf, &tcp->io, &tcp->tm);
tcp->sock = SOCKET_INVALID; tcp->sock = SOCKET_INVALID;
tcp->family = PF_UNSPEC; tcp->family = AF_UNSPEC;
/* allow user to pick local address and port */ /* allow user to pick local address and port */
memset(&bindhints, 0, sizeof(bindhints)); memset(&bindhints, 0, sizeof(bindhints));
bindhints.ai_socktype = SOCK_STREAM; bindhints.ai_socktype = SOCK_STREAM;
bindhints.ai_family = family; bindhints.ai_family = family;
bindhints.ai_flags = AI_PASSIVE; bindhints.ai_flags = AI_PASSIVE;
if (localaddr) { if (localaddr) {
err = inet_trybind(&tcp->sock, localaddr, localserv, &bindhints); err = inet_trybind(&tcp->sock, &tcp->family, localaddr,
localserv, &bindhints);
if (err) { if (err) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, err); lua_pushstring(L, err);
return 2; return 2;
} }
tcp->family = bindhints.ai_family;
} }
/* try to connect to remote address and port */ /* try to connect to remote address and port */
memset(&connecthints, 0, sizeof(connecthints)); memset(&connecthints, 0, sizeof(connecthints));
connecthints.ai_socktype = SOCK_STREAM; connecthints.ai_socktype = SOCK_STREAM;
/* make sure we try to connect only to the same family */ /* make sure we try to connect only to the same family */
connecthints.ai_family = bindhints.ai_family; connecthints.ai_family = tcp->family;
err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv, err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv,
&tcp->tm, &connecthints); &tcp->tm, &connecthints);
if (err) { if (err) {

View File

@ -27,6 +27,7 @@
* Internal function prototypes * Internal function prototypes
\*=========================================================================*/ \*=========================================================================*/
static int global_create(lua_State *L); static int global_create(lua_State *L);
static int global_create4(lua_State *L);
static int global_create6(lua_State *L); static int global_create6(lua_State *L);
static int meth_send(lua_State *L); static int meth_send(lua_State *L);
static int meth_sendto(lua_State *L); static int meth_sendto(lua_State *L);
@ -107,6 +108,7 @@ static t_opt optget[] = {
/* functions in library namespace */ /* functions in library namespace */
static luaL_Reg func[] = { static luaL_Reg func[] = {
{"udp", global_create}, {"udp", global_create},
{"udp4", global_create4},
{"udp6", global_create6}, {"udp6", global_create6},
{NULL, NULL} {NULL, NULL}
}; };
@ -183,7 +185,7 @@ static int meth_sendto(lua_State *L) {
return 2; return 2;
} }
timeout_markstart(tm); timeout_markstart(tm);
err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr,
(socklen_t) ai->ai_addrlen, tm); (socklen_t) ai->ai_addrlen, tm);
freeaddrinfo(ai); freeaddrinfo(ai);
if (err != IO_DONE) { if (err != IO_DONE) {
@ -235,7 +237,7 @@ static int meth_receivefrom(lua_State *L)
char portstr[6]; char portstr[6];
timeout_markstart(tm); timeout_markstart(tm);
count = MIN(count, sizeof(buffer)); count = MIN(count, sizeof(buffer));
err = socket_recvfrom(&udp->sock, buffer, count, &got, (SA *) &addr, err = socket_recvfrom(&udp->sock, buffer, count, &got, (SA *) &addr,
&addr_len, tm); &addr_len, tm);
/* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */
if (err == IO_CLOSED) if (err == IO_CLOSED)
@ -245,7 +247,7 @@ static int meth_receivefrom(lua_State *L)
lua_pushstring(L, udp_strerror(err)); lua_pushstring(L, udp_strerror(err));
return 2; return 2;
} }
err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr,
INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV);
if (err) { if (err) {
lua_pushnil(L); lua_pushnil(L);
@ -264,7 +266,7 @@ static int meth_receivefrom(lua_State *L)
static int meth_getfamily(lua_State *L) static int meth_getfamily(lua_State *L)
{ {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
if (udp->family == PF_INET6) { if (udp->family == AF_INET6) {
lua_pushliteral(L, "inet6"); lua_pushliteral(L, "inet6");
return 1; return 1;
} else { } else {
@ -349,7 +351,7 @@ static int meth_setpeername(lua_State *L) {
/* make sure we try to connect only to the same family */ /* make sure we try to connect only to the same family */
connecthints.ai_family = udp->family; connecthints.ai_family = udp->family;
if (connecting) { if (connecting) {
err = inet_tryconnect(&udp->sock, &udp->family, address, err = inet_tryconnect(&udp->sock, &udp->family, address,
port, tm, &connecthints); port, tm, &connecthints);
if (err) { if (err) {
lua_pushnil(L); lua_pushnil(L);
@ -363,7 +365,6 @@ static int meth_setpeername(lua_State *L) {
inet_trydisconnect(&udp->sock, udp->family, tm); inet_trydisconnect(&udp->sock, udp->family, tm);
auxiliar_setclass(L, "udp{unconnected}", 1); auxiliar_setclass(L, "udp{unconnected}", 1);
} }
/* change class to connected or unconnected depending on address */
lua_pushnumber(L, 1); lua_pushnumber(L, 1);
return 1; return 1;
} }
@ -391,7 +392,7 @@ static int meth_setsockname(lua_State *L) {
bindhints.ai_socktype = SOCK_DGRAM; bindhints.ai_socktype = SOCK_DGRAM;
bindhints.ai_family = udp->family; bindhints.ai_family = udp->family;
bindhints.ai_flags = AI_PASSIVE; bindhints.ai_flags = AI_PASSIVE;
err = inet_trybind(&udp->sock, address, port, &bindhints); err = inet_trybind(&udp->sock, &udp->family, address, port, &bindhints);
if (err) { if (err) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, err); lua_pushstring(L, err);
@ -408,32 +409,32 @@ static int meth_setsockname(lua_State *L) {
* Creates a master udp object * Creates a master udp object
\*-------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------*/
static int udp_create(lua_State *L, int family) { static int udp_create(lua_State *L, int family) {
t_socket sock; /* allocate udp object */
const char *err = inet_trycreate(&sock, family, SOCK_DGRAM); p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp));
/* try to allocate a system socket */ auxiliar_setclass(L, "udp{unconnected}", -1);
if (!err) { /* if family is AF_UNSPEC, we leave the socket invalid and
/* allocate udp object */ * store AF_UNSPEC into family. This will allow it to later be
p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); * replaced with an AF_INET6 or AF_INET socket upon first use. */
auxiliar_setclass(L, "udp{unconnected}", -1); udp->sock = SOCKET_INVALID;
/* initialize remaining structure fields */ timeout_init(&udp->tm, -1, -1);
socket_setnonblocking(&sock); udp->family = family;
if (family == PF_INET6) { if (family != AF_UNSPEC) {
int yes = 1; const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0);
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, if (err != NULL) {
(void *)&yes, sizeof(yes)); lua_pushnil(L);
lua_pushstring(L, err);
return 2;
} }
udp->sock = sock; socket_setnonblocking(&udp->sock);
timeout_init(&udp->tm, -1, -1);
udp->family = family;
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
} }
return 1;
} }
static int global_create(lua_State *L) { static int global_create(lua_State *L) {
return udp_create(L, AF_UNSPEC);
}
static int global_create4(lua_State *L) {
return udp_create(L, AF_INET); return udp_create(L, AF_INET);
} }

View File

@ -211,6 +211,8 @@ int socket_send(p_socket ps, const char *data, size_t count,
err = errno; err = errno;
/* EPIPE means the connection was closed */ /* EPIPE means the connection was closed */
if (err == EPIPE) return IO_CLOSED; if (err == EPIPE) return IO_CLOSED;
/* EPROTOTYPE means the connection is being closed (on Yosemite!)*/
if (err == EPROTOTYPE) continue;
/* we call was interrupted, just try again */ /* we call was interrupted, just try again */
if (err == EINTR) continue; if (err == EINTR) continue;
/* if failed fatal reason, report error */ /* if failed fatal reason, report error */
@ -239,6 +241,7 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
} }
err = errno; err = errno;
if (err == EPIPE) return IO_CLOSED; if (err == EPIPE) return IO_CLOSED;
if (err == EPROTOTYPE) continue;
if (err == EINTR) continue; if (err == EINTR) continue;
if (err != EAGAIN) return err; if (err != EAGAIN) return err;
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
@ -317,6 +320,8 @@ int socket_write(p_socket ps, const char *data, size_t count,
err = errno; err = errno;
/* EPIPE means the connection was closed */ /* EPIPE means the connection was closed */
if (err == EPIPE) return IO_CLOSED; if (err == EPIPE) return IO_CLOSED;
/* EPROTOTYPE means the connection is being closed (on Yosemite!)*/
if (err == EPROTOTYPE) continue;
/* we call was interrupted, just try again */ /* we call was interrupted, just try again */
if (err == EINTR) continue; if (err == EINTR) continue;
/* if failed fatal reason, report error */ /* if failed fatal reason, report error */
@ -410,7 +415,9 @@ const char *socket_strerror(int err) {
case ECONNABORTED: return PIE_CONNABORTED; case ECONNABORTED: return PIE_CONNABORTED;
case ECONNRESET: return PIE_CONNRESET; case ECONNRESET: return PIE_CONNRESET;
case ETIMEDOUT: return PIE_TIMEDOUT; case ETIMEDOUT: return PIE_TIMEDOUT;
default: return strerror(err); default: {
return strerror(err);
}
} }
} }

View File

@ -8,7 +8,7 @@ function printf(...)
end end
function pass(...) function pass(...)
printf(...) printf(...)
io.stderr:write("\n") io.stderr:write("\n")
end end
@ -45,30 +45,30 @@ function check_timeout(tm, sl, elapsed, err, opp, mode, alldone)
if not err then warn("must be buffered") if not err then warn("must be buffered")
elseif err == "timeout" then pass("proper timeout") elseif err == "timeout" then pass("proper timeout")
else fail("unexpected error '%s'", err) end else fail("unexpected error '%s'", err) end
else else
if err ~= "timeout" then fail("should have timed out") if err ~= "timeout" then fail("should have timed out")
else pass("proper timeout") end else pass("proper timeout") end
end end
else else
if mode == "total" then if mode == "total" then
if elapsed > tm then if elapsed > tm then
if err ~= "timeout" then fail("should have timed out") if err ~= "timeout" then fail("should have timed out")
else pass("proper timeout") end else pass("proper timeout") end
elseif elapsed < tm then elseif elapsed < tm then
if err then fail(err) if err then fail(err)
else pass("ok") end else pass("ok") end
else else
if alldone then if alldone then
if err then fail("unexpected error '%s'", err) if err then fail("unexpected error '%s'", err)
else pass("ok") end else pass("ok") end
else else
if err ~= "timeout" then fail(err) if err ~= "timeout" then fail(err)
else pass("proper timeoutk") end else pass("proper timeoutk") end
end end
end end
else else
if err then fail(err) if err then fail(err)
else pass("ok") end else pass("ok") end
end end
end end
end end
@ -104,8 +104,8 @@ control:setoption("tcp-nodelay", true)
------------------------------------------------------------------------ ------------------------------------------------------------------------
function test_methods(sock, methods) function test_methods(sock, methods)
for _, v in pairs(methods) do for _, v in pairs(methods) do
if type(sock[v]) ~= "function" then if type(sock[v]) ~= "function" then
fail(sock.class .. " method '" .. v .. "' not registered") fail(sock.class .. " method '" .. v .. "' not registered")
end end
end end
pass(sock.class .. " methods are ok") pass(sock.class .. " methods are ok")
@ -121,7 +121,7 @@ function test_mixed(len)
local p3 = "raw " .. string.rep("z", inter) .. "bytes" local p3 = "raw " .. string.rep("z", inter) .. "bytes"
local p4 = "end" .. string.rep("w", inter) .. "bytes" local p4 = "end" .. string.rep("w", inter) .. "bytes"
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
@ -166,7 +166,7 @@ function test_rawline(len)
io.stderr:write("length " .. len .. ": ") io.stderr:write("length " .. len .. ": ")
local str, str10, back, err local str, str10, back, err
str = string.rep(string.char(47), math.mod(len, 10)) str = string.rep(string.char(47), math.mod(len, 10))
str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100),
math.floor(len/10)) math.floor(len/10))
str = str .. str10 str = str .. str10
remote "str = data:receive()" remote "str = data:receive()"
@ -216,7 +216,7 @@ function test_totaltimeoutreceive(len, tm, sl)
data:settimeout(tm, "total") data:settimeout(tm, "total")
local t = socket.gettime() local t = socket.gettime()
str, err, partial, elapsed = data:receive(2*len) str, err, partial, elapsed = data:receive(2*len)
check_timeout(tm, sl, elapsed, err, "receive", "total", check_timeout(tm, sl, elapsed, err, "receive", "total",
string.len(str or partial) == 2*len) string.len(str or partial) == 2*len)
end end
@ -236,7 +236,7 @@ function test_totaltimeoutsend(len, tm, sl)
data:settimeout(tm, "total") data:settimeout(tm, "total")
str = string.rep("a", 2*len) str = string.rep("a", 2*len)
total, err, partial, elapsed = data:send(str) total, err, partial, elapsed = data:send(str)
check_timeout(tm, sl, elapsed, err, "send", "total", check_timeout(tm, sl, elapsed, err, "send", "total",
total == 2*len) total == 2*len)
end end
@ -256,7 +256,7 @@ function test_blockingtimeoutreceive(len, tm, sl)
]], 2*tm, len, sl, sl)) ]], 2*tm, len, sl, sl))
data:settimeout(tm) data:settimeout(tm)
str, err, partial, elapsed = data:receive(2*len) str, err, partial, elapsed = data:receive(2*len)
check_timeout(tm, sl, elapsed, err, "receive", "blocking", check_timeout(tm, sl, elapsed, err, "receive", "blocking",
string.len(str or partial) == 2*len) string.len(str or partial) == 2*len)
end end
@ -290,10 +290,10 @@ function empty_connect()
data = server:accept() data = server:accept()
]] ]]
data, err = socket.connect("", port) data, err = socket.connect("", port)
if not data then if not data then
pass("ok") pass("ok")
data = socket.connect(host, port) data = socket.connect(host, port)
else else
pass("gethostbyname returns localhost on empty string...") pass("gethostbyname returns localhost on empty string...")
end end
end end
@ -304,15 +304,20 @@ function isclosed(c)
end end
function active_close() function active_close()
reconnect() local tcp = socket.tcp4()
if isclosed(data) then fail("should not be closed") end if isclosed(tcp) then fail("should not be closed") end
data:close() tcp:close()
if not isclosed(data) then fail("should be closed") end if not isclosed(tcp) then fail("should be closed") end
data = nil tcp = socket.tcp()
local udp = socket.udp() if not isclosed(tcp) then fail("should be closed") end
tcp = nil
local udp = socket.udp4()
if isclosed(udp) then fail("should not be closed") end if isclosed(udp) then fail("should not be closed") end
udp:close() udp:close()
if not isclosed(udp) then fail("should be closed") end if not isclosed(udp) then fail("should be closed") end
udp = socket.udp()
if not isclosed(udp) then fail("should be closed") end
udp = nil
pass("ok") pass("ok")
end end
@ -327,7 +332,7 @@ function test_closed()
data:close() data:close()
data = nil data = nil
]], str)) ]], str))
-- try to get a line -- try to get a line
back, err, partial = data:receive() back, err, partial = data:receive()
if not err then fail("should have gotten 'closed'.") if not err then fail("should have gotten 'closed'.")
elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.")
@ -340,25 +345,25 @@ function test_closed()
data = nil data = nil
]] ]]
total, err, partial = data:send(string.rep("ugauga", 100000)) total, err, partial = data:send(string.rep("ugauga", 100000))
if not err then if not err then
pass("failed: output buffer is at least %d bytes long!", total) pass("failed: output buffer is at least %d bytes long!", total)
elseif err ~= "closed" then elseif err ~= "closed" then
fail("got '"..err.."' instead of 'closed'.") fail("got '"..err.."' instead of 'closed'.")
else else
pass("graceful 'closed' received after %d bytes were sent", partial) pass("graceful 'closed' received after %d bytes were sent", partial)
end end
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function test_selectbugs() function test_selectbugs()
local r, s, e = socket.select(nil, nil, 0.1) local r, s, e = socket.select(nil, nil, 0.1)
assert(type(r) == "table" and type(s) == "table" and assert(type(r) == "table" and type(s) == "table" and
(e == "timeout" or e == "error")) (e == "timeout" or e == "error"))
pass("both nil: ok") pass("both nil: ok")
local udp = socket.udp() local udp = socket.udp()
udp:close() udp:close()
r, s, e = socket.select({ udp }, { udp }, 0.1) r, s, e = socket.select({ udp }, { udp }, 0.1)
assert(type(r) == "table" and type(s) == "table" and assert(type(r) == "table" and type(s) == "table" and
(e == "timeout" or e == "error")) (e == "timeout" or e == "error"))
pass("closed sockets: ok") pass("closed sockets: ok")
e = pcall(socket.select, "wrong", 1, 0.1) e = pcall(socket.select, "wrong", 1, 0.1)
@ -368,7 +373,7 @@ function test_selectbugs()
pass("invalid input: ok") pass("invalid input: ok")
local toomany = {} local toomany = {}
for i = 1, socket._SETSIZE+1 do for i = 1, socket._SETSIZE+1 do
toomany[#toomany+1] = socket.udp() toomany[#toomany+1] = socket.udp4()
end end
if #toomany > socket._SETSIZE then if #toomany > socket._SETSIZE then
local e = pcall(socket.select, toomany, nil, 0.1) local e = pcall(socket.select, toomany, nil, 0.1)
@ -389,7 +394,7 @@ function accept_timeout()
local t = socket.gettime() local t = socket.gettime()
s:settimeout(1) s:settimeout(1)
local c, e = s:accept() local c, e = s:accept()
assert(not c, "should not accept") assert(not c, "should not accept")
assert(e == "timeout", string.format("wrong error message (%s)", e)) assert(e == "timeout", string.format("wrong error message (%s)", e))
t = socket.gettime() - t t = socket.gettime() - t
assert(t < 2, string.format("took to long to give up (%gs)", t)) assert(t < 2, string.format("took to long to give up (%gs)", t))
@ -407,9 +412,9 @@ function connect_timeout()
local t = socket.gettime() local t = socket.gettime()
local r, e = c:connect("10.0.0.1", 81) local r, e = c:connect("10.0.0.1", 81)
assert(not r, "should not connect") assert(not r, "should not connect")
assert(socket.gettime() - t < 2, "took too long to give up.") assert(socket.gettime() - t < 2, "took too long to give up.")
c:close() c:close()
pass("ok") pass("ok")
end end
------------------------------------------------------------------------ ------------------------------------------------------------------------
@ -447,16 +452,14 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function rebind_test() function rebind_test()
--local c ,c1 = socket.bind("localhost", 0)
local c ,c1 = socket.bind("127.0.0.1", 0) local c ,c1 = socket.bind("127.0.0.1", 0)
if not c then pass ("failed to bind! " .. tostring(c) .. ' ' .. tostring(c1)) return end if not c then pass ("failed to bind! " .. tostring(c) .. ' ' .. tostring(c1)) return end
assert(c,c1) assert(c,c1)
local i, p = c:getsockname() local i, p = c:getsockname()
local s, e = socket.tcp() local s, e = socket.tcp()
assert(s, e) assert(s, e)
s:setoption("reuseaddr", false) s:setoption("reuseaddr", false)
r, e = s:bind("localhost", p) r, e = s:bind(i, p)
assert(not r, "managed to rebind!") assert(not r, "managed to rebind!")
assert(e) assert(e)
pass("ok") pass("ok")
@ -476,9 +479,9 @@ function getstats_test()
data:receive(c) data:receive(c)
t = t + c t = t + c
local r, s, a = data:getstats() local r, s, a = data:getstats()
assert(r == t, "received count failed" .. tostring(r) assert(r == t, "received count failed" .. tostring(r)
.. "/" .. tostring(t)) .. "/" .. tostring(t))
assert(s == t, "sent count failed" .. tostring(s) assert(s == t, "sent count failed" .. tostring(s)
.. "/" .. tostring(t)) .. "/" .. tostring(t))
end end
pass("ok") pass("ok")
@ -486,7 +489,7 @@ end
------------------------------------------------------------------------ ------------------------------------------------------------------------
function test_nonblocking(size) function test_nonblocking(size)
reconnect() reconnect()
printf("testing " .. 2*size .. " bytes: ") printf("testing " .. 2*size .. " bytes: ")
remote(string.format([[ remote(string.format([[
@ -545,7 +548,7 @@ function test_readafterclose()
data:close() data:close()
data = nil data = nil
]])) ]]))
data:close() data:close()
back, err, partial = data:receive("*a") back, err, partial = data:receive("*a")
assert(back == nil and err == "closed", "should have returned 'closed'") assert(back == nil and err == "closed", "should have returned 'closed'")
pass("ok") pass("ok")
@ -555,7 +558,7 @@ function test_readafterclose()
data:close() data:close()
data = nil data = nil
]])) ]]))
data:close() data:close()
back, err, partial = data:receive() back, err, partial = data:receive()
assert(back == nil and err == "closed", "should have returned 'closed'") assert(back == nil and err == "closed", "should have returned 'closed'")
pass("ok") pass("ok")
@ -565,7 +568,7 @@ function test_readafterclose()
data:close() data:close()
data = nil data = nil
]])) ]]))
data:close() data:close()
back, err, partial = data:receive(1) back, err, partial = data:receive(1)
assert(back == nil and err == "closed", "should have returned 'closed'") assert(back == nil and err == "closed", "should have returned 'closed'")
pass("ok") pass("ok")
@ -575,7 +578,7 @@ function test_readafterclose()
data:close() data:close()
data = nil data = nil
]])) ]]))
data:close() data:close()
back, err, partial = data:receive(0) back, err, partial = data:receive(0)
assert(back == nil and err == "closed", "should have returned 'closed'") assert(back == nil and err == "closed", "should have returned 'closed'")
pass("ok") pass("ok")
@ -593,7 +596,7 @@ function test_writeafterclose()
while not err do while not err do
sent, err, errsent, time = data:send(str) sent, err, errsent, time = data:send(str)
end end
assert(err == "closed", "should have returned 'closed'") assert(err == "closed", "got " .. err .. " instead of 'closed'")
pass("ok") pass("ok")
end end
@ -648,18 +651,18 @@ else io.stderr:write("Warning! IPv6 does not support!\n") end
end end
local udp_methods = { local udp_methods = {
"close", "close",
"dirty", "dirty",
"getfamily", "getfamily",
"getfd", "getfd",
"getoption", "getoption",
"getpeername", "getpeername",
"getsockname", "getsockname",
"receive", "receive",
"receivefrom", "receivefrom",
"send", "send",
"sendto", "sendto",
"setfd", "setfd",
"setoption", "setoption",
"setpeername", "setpeername",
"setsockname", "setsockname",
@ -674,6 +677,9 @@ if sock then test_methods(socket.udp6(), udp_methods)
else io.stderr:write("Warning! IPv6 does not support!\n") end else io.stderr:write("Warning! IPv6 does not support!\n") end
end end
test("closed connection detection: ")
test_closed()
test("partial receive") test("partial receive")
test_partialrecv() test_partialrecv()
@ -697,9 +703,6 @@ rebind_test()
test("active close: ") test("active close: ")
active_close() active_close()
test("closed connection detection: ")
test_closed()
test("accept function: ") test("accept function: ")
accept_timeout() accept_timeout()
accept_errors() accept_errors()

View File

@ -6,7 +6,7 @@ ack = "\n";
while 1 do while 1 do
print("server: waiting for client connection..."); print("server: waiting for client connection...");
control = assert(server:accept()); control = assert(server:accept());
while 1 do while 1 do
command, emsg = control:receive(); command, emsg = control:receive();
if emsg == "closed" then if emsg == "closed" then
control:close() control:close()

View File

@ -1,7 +1,7 @@
local socket = require"socket" local socket = require"socket"
local udp = socket.udp local udp = socket.udp
local localhost = "127.0.0.1" local localhost = "127.0.0.1"
local port = arg[1] local port = assert(arg[1], "missing port argument")
se = udp(); se:setoption("reuseaddr", true) se = udp(); se:setoption("reuseaddr", true)
se:setsockname(localhost, 5062) se:setsockname(localhost, 5062)