* rubyio.h (rb_io_set_nonblock): declared.
* io.c (rb_io_set_nonblock): new function. (io_getpartial): nonblocking read support. (io_read_nonblock): new method: IO#read_nonblock. (io_write_nonblock): new method: IO#write_nonblock. * ext/socket/socket.c (s_accept): retry for EWOULDBLOCK. revert [ruby-talk:113807]. (sock_connect_nonblock): new method: Socket#connect_nonblock. (sock_accept_nonblock): new method: Socket#accept_nonblock. (sock_recvfrom_nonblock): new method: Socket#recvfrom_nonblock. [ruby-core:7917] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@10172 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
baedd2b4f7
commit
096ff284ca
@ -1410,8 +1410,6 @@ s_accept(klass, fd, sockaddr, len)
|
|||||||
rb_gc();
|
rb_gc();
|
||||||
retry = 1;
|
retry = 1;
|
||||||
goto retry;
|
goto retry;
|
||||||
case EWOULDBLOCK:
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
if (!rb_io_wait_readable(fd)) break;
|
if (!rb_io_wait_readable(fd)) break;
|
||||||
retry = 0;
|
retry = 0;
|
||||||
@ -2153,7 +2151,7 @@ unix_s_socketpair(argc, argv, klass)
|
|||||||
* socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
|
* socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
|
||||||
* sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' )
|
* sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' )
|
||||||
* socket.connect( sockaddr )
|
* socket.connect( sockaddr )
|
||||||
* socket.write( "GET / HTTP/1.0\n\n" )
|
* socket.write( "GET / HTTP/1.0\r\n\r\n" )
|
||||||
* results = socket.read
|
* results = socket.read
|
||||||
*
|
*
|
||||||
* === Unix-based Exceptions
|
* === Unix-based Exceptions
|
||||||
@ -2267,6 +2265,142 @@ sock_connect(sock, addr)
|
|||||||
return INT2FIX(n);
|
return INT2FIX(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* socket.connect_nonblock( sockaddr ) => 0
|
||||||
|
*
|
||||||
|
* Requests a connection to be made on the given +sockaddr+ after
|
||||||
|
* O_NONBLOCK is set for the underlying file descriptor.
|
||||||
|
* Returns 0 if successful, otherwise an exception is raised.
|
||||||
|
*
|
||||||
|
* === Parameter
|
||||||
|
* * +sockaddr+ - the +struct+ sockaddr contained in a string
|
||||||
|
*
|
||||||
|
* === Example:
|
||||||
|
* # Pull down Google's web page
|
||||||
|
* require 'socket'
|
||||||
|
* include Socket::Constants
|
||||||
|
* socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
|
||||||
|
* sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' )
|
||||||
|
* begin
|
||||||
|
* socket.connect_nonblock( sockaddr )
|
||||||
|
* rescue Errno::EINPROGRESS
|
||||||
|
* IO.select(nil, [socket])
|
||||||
|
* socket.connect_nonblock( sockaddr )
|
||||||
|
* end
|
||||||
|
* socket.write( "GET / HTTP/1.0\r\n\r\n" )
|
||||||
|
* results = socket.read
|
||||||
|
*
|
||||||
|
* === Unix-based Exceptions
|
||||||
|
* On unix-based systems the following system exceptions may be raised if
|
||||||
|
* the call to _connect_ fails:
|
||||||
|
* * Errno::EACCES - search permission is denied for a component of the prefix
|
||||||
|
* path or write access to the +socket+ is denided
|
||||||
|
* * Errno::EADDRINUSE - the _sockaddr_ is already in use
|
||||||
|
* * Errno::EADDRNOTAVAIL - the specified _sockaddr_ is not available from the
|
||||||
|
* local machine
|
||||||
|
* * Errno::EAFNOSUPPORT - the specified _sockaddr_ is not a valid address for
|
||||||
|
* the address family of the specified +socket+
|
||||||
|
* * Errno::EALREADY - a connection is already in progress for the specified
|
||||||
|
* socket
|
||||||
|
* * Errno::EBADF - the +socket+ is not a valid file descriptor
|
||||||
|
* * Errno::ECONNREFUSED - the target _sockaddr_ was not listening for connections
|
||||||
|
* refused the connection request
|
||||||
|
* * Errno::ECONNRESET - the remote host reset the connection request
|
||||||
|
* * Errno::EFAULT - the _sockaddr_ cannot be accessed
|
||||||
|
* * Errno::EHOSTUNREACH - the destination host cannot be reached (probably
|
||||||
|
* because the host is down or a remote router cannot reach it)
|
||||||
|
* * Errno::EINPROGRESS - the O_NONBLOCK is set for the +socket+ and the
|
||||||
|
* connection cnanot be immediately established; the connection will be
|
||||||
|
* established asynchronously
|
||||||
|
* * Errno::EINTR - the attempt to establish the connection was interrupted by
|
||||||
|
* delivery of a signal that was caught; the connection will be established
|
||||||
|
* asynchronously
|
||||||
|
* * Errno::EISCONN - the specified +socket+ is already connected
|
||||||
|
* * Errno::EINVAL - the address length used for the _sockaddr_ is not a valid
|
||||||
|
* length for the address family or there is an invalid family in _sockaddr_
|
||||||
|
* * Errno::ENAMETOOLONG - the pathname resolved had a length which exceeded
|
||||||
|
* PATH_MAX
|
||||||
|
* * Errno::ENETDOWN - the local interface used to reach the destination is down
|
||||||
|
* * Errno::ENETUNREACH - no route to the network is present
|
||||||
|
* * Errno::ENOBUFS - no buffer space is available
|
||||||
|
* * Errno::ENOSR - there were insufficient STREAMS resources available to
|
||||||
|
* complete the operation
|
||||||
|
* * Errno::ENOTSOCK - the +socket+ argument does not refer to a socket
|
||||||
|
* * Errno::EOPNOTSUPP - the calling +socket+ is listening and cannot be connected
|
||||||
|
* * Errno::EPROTOTYPE - the _sockaddr_ has a different type than the socket
|
||||||
|
* bound to the specified peer address
|
||||||
|
* * Errno::ETIMEDOUT - the attempt to connect time out before a connection
|
||||||
|
* was made.
|
||||||
|
*
|
||||||
|
* On unix-based systems if the address family of the calling +socket+ is
|
||||||
|
* AF_UNIX the follow exceptions may be raised if the call to _connect_
|
||||||
|
* fails:
|
||||||
|
* * Errno::EIO - an i/o error occured while reading from or writing to the
|
||||||
|
* file system
|
||||||
|
* * Errno::ELOOP - too many symbolic links were encountered in translating
|
||||||
|
* the pathname in _sockaddr_
|
||||||
|
* * Errno::ENAMETOOLLONG - a component of a pathname exceeded NAME_MAX
|
||||||
|
* characters, or an entired pathname exceeded PATH_MAX characters
|
||||||
|
* * Errno::ENOENT - a component of the pathname does not name an existing file
|
||||||
|
* or the pathname is an empty string
|
||||||
|
* * Errno::ENOTDIR - a component of the path prefix of the pathname in _sockaddr_
|
||||||
|
* is not a directory
|
||||||
|
*
|
||||||
|
* === Windows Exceptions
|
||||||
|
* On Windows systems the following system exceptions may be raised if
|
||||||
|
* the call to _connect_ fails:
|
||||||
|
* * Errno::ENETDOWN - the network is down
|
||||||
|
* * Errno::EADDRINUSE - the socket's local address is already in use
|
||||||
|
* * Errno::EINTR - the socket was cancelled
|
||||||
|
* * Errno::EINPROGRESS - a blocking socket is in progress or the service provider
|
||||||
|
* is still processing a callback function. Or a nonblocking connect call is
|
||||||
|
* in progress on the +socket+.
|
||||||
|
* * Errno::EALREADY - see Errno::EINVAL
|
||||||
|
* * Errno::EADDRNOTAVAIL - the remote address is not a valid address, such as
|
||||||
|
* ADDR_ANY TODO check ADDRANY TO INADDR_ANY
|
||||||
|
* * Errno::EAFNOSUPPORT - addresses in the specified family cannot be used with
|
||||||
|
* with this +socket+
|
||||||
|
* * Errno::ECONNREFUSED - the target _sockaddr_ was not listening for connections
|
||||||
|
* refused the connection request
|
||||||
|
* * Errno::EFAULT - the socket's internal address or address length parameter
|
||||||
|
* is too small or is not a valid part of the user space address
|
||||||
|
* * Errno::EINVAL - the +socket+ is a listening socket
|
||||||
|
* * Errno::EISCONN - the +socket+ is already connected
|
||||||
|
* * Errno::ENETUNREACH - the network cannot be reached from this host at this time
|
||||||
|
* * Errno::EHOSTUNREACH - no route to the network is present
|
||||||
|
* * Errno::ENOBUFS - no buffer space is available
|
||||||
|
* * Errno::ENOTSOCK - the +socket+ argument does not refer to a socket
|
||||||
|
* * Errno::ETIMEDOUT - the attempt to connect time out before a connection
|
||||||
|
* was made.
|
||||||
|
* * Errno::EWOULDBLOCK - the socket is marked as nonblocking and the
|
||||||
|
* connection cannot be completed immediately
|
||||||
|
* * Errno::EACCES - the attempt to connect the datagram socket to the
|
||||||
|
* broadcast address failed
|
||||||
|
*
|
||||||
|
* === See
|
||||||
|
* * connect manual pages on unix-based systems
|
||||||
|
* * connect function in Microsoft's Winsock functions reference
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
sock_connect_nonblock(sock, addr)
|
||||||
|
VALUE sock, addr;
|
||||||
|
{
|
||||||
|
OpenFile *fptr;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
StringValue(addr);
|
||||||
|
addr = rb_str_new4(addr);
|
||||||
|
GetOpenFile(sock, fptr);
|
||||||
|
rb_io_set_nonblock(fptr);
|
||||||
|
n = connect(fptr->fd, (struct sockaddr*)RSTRING(addr)->ptr, RSTRING(addr)->len);
|
||||||
|
if (n < 0) {
|
||||||
|
rb_sys_fail("connect(2)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return INT2FIX(n);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* socket.bind( sockaddr ) => 0
|
* socket.bind( sockaddr ) => 0
|
||||||
@ -2454,7 +2588,7 @@ sock_listen(sock, log)
|
|||||||
* Receives up to _len_ bytes from +socket+. _flags_ is zero or more
|
* Receives up to _len_ bytes from +socket+. _flags_ is zero or more
|
||||||
* of the +MSG_+ options. The first element of the results is the data
|
* of the +MSG_+ options. The first element of the results is the data
|
||||||
* received. The second element contains protocol-specific information
|
* received. The second element contains protocol-specific information
|
||||||
* on the snder
|
* on the sender.
|
||||||
*
|
*
|
||||||
* === Parameters
|
* === Parameters
|
||||||
* * +len+ - the number of bytes to receive from the socket
|
* * +len+ - the number of bytes to receive from the socket
|
||||||
@ -2560,6 +2694,163 @@ sock_recvfrom(argc, argv, sock)
|
|||||||
return s_recvfrom(sock, argc, argv, RECV_SOCKET);
|
return s_recvfrom(sock, argc, argv, RECV_SOCKET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* socket.recvfrom_nonblock( len ) => [ data, sender ]
|
||||||
|
* socket.recvfrom_nonblock( len, flags ) => [ data, sender ]
|
||||||
|
*
|
||||||
|
* Receives up to _len_ bytes from +socket+ using recvfrom(2) after
|
||||||
|
* O_NONBLOCK is set for the underlying file descriptor.
|
||||||
|
* _flags_ is zero or more of the +MSG_+ options.
|
||||||
|
* The first element of the results is the data received.
|
||||||
|
* The second element contains protocol-specific information
|
||||||
|
* on the sender.
|
||||||
|
*
|
||||||
|
* === Parameters
|
||||||
|
* * +len+ - the number of bytes to receive from the socket
|
||||||
|
* * +flags+ - zero or more of the +MSG_+ options
|
||||||
|
*
|
||||||
|
* === Example
|
||||||
|
* # In one file, start this first
|
||||||
|
* require 'socket'
|
||||||
|
* include Socket::Constants
|
||||||
|
* socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
|
||||||
|
* sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
|
||||||
|
* socket.bind( sockaddr )
|
||||||
|
* socket.listen( 5 )
|
||||||
|
* client, client_sockaddr = socket.accept
|
||||||
|
* begin
|
||||||
|
* pair = client.recvfrom_nonblock( 20 )
|
||||||
|
* rescue Errno::EAGAIN
|
||||||
|
* IO.select([client])
|
||||||
|
* retry
|
||||||
|
* end
|
||||||
|
* data = pair[0].chomp
|
||||||
|
* puts "I only received 20 bytes '#{data}'"
|
||||||
|
* sleep 1
|
||||||
|
* socket.close
|
||||||
|
*
|
||||||
|
* # In another file, start this second
|
||||||
|
* require 'socket'
|
||||||
|
* include Socket::Constants
|
||||||
|
* socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
|
||||||
|
* sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
|
||||||
|
* socket.connect( sockaddr )
|
||||||
|
* socket.puts "Watch this get cut short!"
|
||||||
|
* socket.close
|
||||||
|
*
|
||||||
|
* === Unix-based Exceptions
|
||||||
|
* On unix-based based systems the following system exceptions may be raised if the
|
||||||
|
* call to _recvfrom_ fails:
|
||||||
|
* * Errno::EAGAIN - the +socket+ file descriptor is marked as O_NONBLOCK and no
|
||||||
|
* data is waiting to be received; or MSG_OOB is set and no out-of-band data
|
||||||
|
* is available and either the +socket+ file descriptor is marked as
|
||||||
|
* O_NONBLOCK or the +socket+ does not support blocking to wait for
|
||||||
|
* out-of-band-data
|
||||||
|
* * Errno::EWOULDBLOCK - see Errno::EAGAIN
|
||||||
|
* * Errno::EBADF - the +socket+ is not a valid file descriptor
|
||||||
|
* * Errno::ECONNRESET - a connection was forcibly closed by a peer
|
||||||
|
* * Errno::EFAULT - the socket's internal buffer, address or address length
|
||||||
|
* cannot be accessed or written
|
||||||
|
* * Errno::EINTR - a signal interupted _recvfrom_ before any data was available
|
||||||
|
* * Errno::EINVAL - the MSG_OOB flag is set and no out-of-band data is available
|
||||||
|
* * Errno::EIO - an i/o error occurred while reading from or writing to the
|
||||||
|
* filesystem
|
||||||
|
* * Errno::ENOBUFS - insufficient resources were available in the system to
|
||||||
|
* perform the operation
|
||||||
|
* * Errno::ENOMEM - insufficient memory was available to fulfill the request
|
||||||
|
* * Errno::ENOSR - there were insufficient STREAMS resources available to
|
||||||
|
* complete the operation
|
||||||
|
* * Errno::ENOTCONN - a receive is attempted on a connection-mode socket that
|
||||||
|
* is not connected
|
||||||
|
* * Errno::ENOTSOCK - the +socket+ does not refer to a socket
|
||||||
|
* * Errno::EOPNOTSUPP - the specified flags are not supported for this socket type
|
||||||
|
* * Errno::ETIMEDOUT - the connection timed out during connection establishment
|
||||||
|
* or due to a transmission timeout on an active connection
|
||||||
|
*
|
||||||
|
* === Windows Exceptions
|
||||||
|
* On Windows systems the following system exceptions may be raised if
|
||||||
|
* the call to _recvfrom_ fails:
|
||||||
|
* * Errno::ENETDOWN - the network is down
|
||||||
|
* * Errno::EFAULT - the internal buffer and from parameters on +socket+ are not
|
||||||
|
* part of the user address space, or the internal fromlen parameter is
|
||||||
|
* too small to accomodate the peer address
|
||||||
|
* * Errno::EINTR - the (blocking) call was cancelled by an internal call to
|
||||||
|
* the WinSock function WSACancelBlockingCall
|
||||||
|
* * Errno::EINPROGRESS - a blocking Windows Sockets 1.1 call is in progress or
|
||||||
|
* the service provider is still processing a callback function
|
||||||
|
* * Errno::EINVAL - +socket+ has not been bound with a call to _bind_, or an
|
||||||
|
* unknown flag was specified, or MSG_OOB was specified for a socket with
|
||||||
|
* SO_OOBINLINE enabled, or (for byte stream-style sockets only) the internal
|
||||||
|
* len parameter on +socket+ was zero or negative
|
||||||
|
* * Errno::EISCONN - +socket+ is already connected. The call to _recvfrom_ is
|
||||||
|
* not permitted with a connected socket on a socket that is connetion
|
||||||
|
* oriented or connectionless.
|
||||||
|
* * Errno::ENETRESET - the connection has been broken due to the keep-alive
|
||||||
|
* activity detecting a failure while the operation was in progress.
|
||||||
|
* * Errno::EOPNOTSUPP - MSG_OOB was specified, but +socket+ is not stream-style
|
||||||
|
* such as type SOCK_STREAM. OOB data is not supported in the communication
|
||||||
|
* domain associated with +socket+, or +socket+ is unidirectional and
|
||||||
|
* supports only send operations
|
||||||
|
* * Errno::ESHUTDOWN - +socket+ has been shutdown. It is not possible to
|
||||||
|
* call _recvfrom_ on a socket after _shutdown_ has been invoked.
|
||||||
|
* * Errno::EWOULDBLOCK - +socket+ is marked as nonblocking and a call to
|
||||||
|
* _recvfrom_ would block.
|
||||||
|
* * Errno::EMSGSIZE - the message was too large to fit into the specified buffer
|
||||||
|
* and was truncated.
|
||||||
|
* * Errno::ETIMEDOUT - the connection has been dropped, because of a network
|
||||||
|
* failure or because the system on the other end went down without
|
||||||
|
* notice
|
||||||
|
* * Errno::ECONNRESET - the virtual circuit was reset by the remote side
|
||||||
|
* executing a hard or abortive close. The application should close the
|
||||||
|
* socket; it is no longer usable. On a UDP-datagram socket this error
|
||||||
|
* indicates a previous send operation resulted in an ICMP Port Unreachable
|
||||||
|
* message.
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
sock_recvfrom_nonblock(argc, argv, sock)
|
||||||
|
int argc;
|
||||||
|
VALUE *argv;
|
||||||
|
VALUE sock;
|
||||||
|
{
|
||||||
|
OpenFile *fptr;
|
||||||
|
VALUE str;
|
||||||
|
char buf[1024];
|
||||||
|
socklen_t alen = sizeof buf;
|
||||||
|
VALUE len, flg;
|
||||||
|
long buflen;
|
||||||
|
long slen;
|
||||||
|
int fd, flags;
|
||||||
|
|
||||||
|
rb_scan_args(argc, argv, "11", &len, &flg);
|
||||||
|
|
||||||
|
if (flg == Qnil) flags = 0;
|
||||||
|
else flags = NUM2INT(flg);
|
||||||
|
buflen = NUM2INT(len);
|
||||||
|
|
||||||
|
GetOpenFile(sock, fptr);
|
||||||
|
if (rb_io_read_pending(fptr)) {
|
||||||
|
rb_raise(rb_eIOError, "recv for buffered IO");
|
||||||
|
}
|
||||||
|
fd = fptr->fd;
|
||||||
|
|
||||||
|
str = rb_tainted_str_new(0, buflen);
|
||||||
|
|
||||||
|
rb_io_check_closed(fptr);
|
||||||
|
rb_io_set_nonblock(fptr);
|
||||||
|
slen = recvfrom(fd, RSTRING(str)->ptr, buflen, flags, (struct sockaddr*)buf, &alen);
|
||||||
|
|
||||||
|
if (slen < 0) {
|
||||||
|
rb_sys_fail("recvfrom(2)");
|
||||||
|
}
|
||||||
|
if (slen < RSTRING(str)->len) {
|
||||||
|
RSTRING(str)->len = slen;
|
||||||
|
RSTRING(str)->ptr[slen] = '\0';
|
||||||
|
}
|
||||||
|
rb_obj_taint(str);
|
||||||
|
return rb_assoc_new(str, rb_str_new(buf, alen));
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
sock_accept(sock)
|
sock_accept(sock)
|
||||||
VALUE sock;
|
VALUE sock;
|
||||||
@ -2575,6 +2866,69 @@ sock_accept(sock)
|
|||||||
return rb_assoc_new(sock2, rb_str_new(buf, len));
|
return rb_assoc_new(sock2, rb_str_new(buf, len));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* socket.accept_nonblock => [ socket, address ]
|
||||||
|
*
|
||||||
|
* Accepts an incoming connection using accept(2) after
|
||||||
|
* O_NONBLOCK is set for the underlying file descriptor.
|
||||||
|
* It returns an array containg the accpeted socket
|
||||||
|
* for the incoming connection and a string that contains the
|
||||||
|
* +struct+ sockaddr information about the caller.
|
||||||
|
*
|
||||||
|
* === Example
|
||||||
|
* # In one script, start this first
|
||||||
|
* require 'socket'
|
||||||
|
* include Socket::Constants
|
||||||
|
* socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
|
||||||
|
* sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
|
||||||
|
* socket.bind( sockaddr )
|
||||||
|
* socket.listen( 5 )
|
||||||
|
* IO.select([socket])
|
||||||
|
* client_fd, client_sockaddr = socket.accept_nonblock
|
||||||
|
* puts "The client said, '#{socket.readline.chomp}'"
|
||||||
|
* client_socket = Socket.for_fd( client_fd )
|
||||||
|
* client_socket.puts "Hello from script one!"
|
||||||
|
* socket.close
|
||||||
|
*
|
||||||
|
* # In another script, start this second
|
||||||
|
* require 'socket'
|
||||||
|
* include Socket::Constants
|
||||||
|
* socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
|
||||||
|
* sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' )
|
||||||
|
* socket.connect( sockaddr )
|
||||||
|
* socket.puts "Hello from script 2."
|
||||||
|
* puts "The server said, '#{socket.readline.chomp}'"
|
||||||
|
* socket.close
|
||||||
|
*
|
||||||
|
* Refer to Socket#accept for the exceptions that may be thrown if the call
|
||||||
|
* to _sysaccept_ fails.
|
||||||
|
*
|
||||||
|
* === See
|
||||||
|
* * Socket#accept
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
sock_accept_nonblock(sock)
|
||||||
|
VALUE sock;
|
||||||
|
{
|
||||||
|
OpenFile *fptr;
|
||||||
|
int fd2;
|
||||||
|
VALUE sock2;
|
||||||
|
char buf[1024];
|
||||||
|
socklen_t len = sizeof buf;
|
||||||
|
|
||||||
|
GetOpenFile(sock, fptr);
|
||||||
|
rb_io_set_nonblock(fptr);
|
||||||
|
fd2 = accept(fptr->fd, (struct sockaddr*)buf, &len);
|
||||||
|
|
||||||
|
if (fd2 < 0) {
|
||||||
|
rb_sys_fail(0);
|
||||||
|
}
|
||||||
|
sock2 = init_sock(rb_obj_alloc(rb_cSocket), fd2);
|
||||||
|
|
||||||
|
return rb_assoc_new(sock2, rb_str_new(buf, len));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* socket.sysaccept => [ socket_fd, address ]
|
* socket.sysaccept => [ socket_fd, address ]
|
||||||
@ -3225,12 +3579,15 @@ Init_socket()
|
|||||||
|
|
||||||
rb_define_method(rb_cSocket, "initialize", sock_initialize, 3);
|
rb_define_method(rb_cSocket, "initialize", sock_initialize, 3);
|
||||||
rb_define_method(rb_cSocket, "connect", sock_connect, 1);
|
rb_define_method(rb_cSocket, "connect", sock_connect, 1);
|
||||||
|
rb_define_method(rb_cSocket, "connect_nonblock", sock_connect_nonblock, 1);
|
||||||
rb_define_method(rb_cSocket, "bind", sock_bind, 1);
|
rb_define_method(rb_cSocket, "bind", sock_bind, 1);
|
||||||
rb_define_method(rb_cSocket, "listen", sock_listen, 1);
|
rb_define_method(rb_cSocket, "listen", sock_listen, 1);
|
||||||
rb_define_method(rb_cSocket, "accept", sock_accept, 0);
|
rb_define_method(rb_cSocket, "accept", sock_accept, 0);
|
||||||
|
rb_define_method(rb_cSocket, "accept_nonblock", sock_accept_nonblock, 0);
|
||||||
rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, 0);
|
rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, 0);
|
||||||
|
|
||||||
rb_define_method(rb_cSocket, "recvfrom", sock_recvfrom, -1);
|
rb_define_method(rb_cSocket, "recvfrom", sock_recvfrom, -1);
|
||||||
|
rb_define_method(rb_cSocket, "recvfrom_nonblock", sock_recvfrom_nonblock, -1);
|
||||||
|
|
||||||
rb_define_singleton_method(rb_cSocket, "socketpair", sock_s_socketpair, 3);
|
rb_define_singleton_method(rb_cSocket, "socketpair", sock_s_socketpair, 3);
|
||||||
rb_define_singleton_method(rb_cSocket, "pair", sock_s_socketpair, 3);
|
rb_define_singleton_method(rb_cSocket, "pair", sock_s_socketpair, 3);
|
||||||
|
110
io.c
110
io.c
@ -1236,8 +1236,25 @@ read_all(OpenFile *fptr, long siz, VALUE str)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rb_io_set_nonblock(OpenFile *fptr)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
#ifdef F_GETFL
|
||||||
|
flags = fcntl(fptr->fd, F_GETFL);
|
||||||
|
if (flags == -1) {
|
||||||
|
rb_sys_fail(fptr->path);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
flags = 0;
|
||||||
|
#endif
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
if (fcntl(fptr->fd, F_SETFL, flags) == -1) {
|
||||||
|
rb_sys_fail(fptr->path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
io_getpartial(int argc, VALUE *argv, VALUE io)
|
io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock)
|
||||||
{
|
{
|
||||||
OpenFile *fptr;
|
OpenFile *fptr;
|
||||||
VALUE length, str;
|
VALUE length, str;
|
||||||
@ -1265,6 +1282,7 @@ io_getpartial(int argc, VALUE *argv, VALUE io)
|
|||||||
if (len == 0)
|
if (len == 0)
|
||||||
return str;
|
return str;
|
||||||
|
|
||||||
|
if (!nonblock)
|
||||||
READ_CHECK(fptr);
|
READ_CHECK(fptr);
|
||||||
if (RSTRING(str)->len != len) {
|
if (RSTRING(str)->len != len) {
|
||||||
modified:
|
modified:
|
||||||
@ -1274,11 +1292,17 @@ io_getpartial(int argc, VALUE *argv, VALUE io)
|
|||||||
if (n <= 0) {
|
if (n <= 0) {
|
||||||
again:
|
again:
|
||||||
if (RSTRING(str)->len != len) goto modified;
|
if (RSTRING(str)->len != len) goto modified;
|
||||||
|
if (nonblock) {
|
||||||
|
rb_io_set_nonblock(fptr);
|
||||||
|
n = read(fptr->fd, RSTRING(str)->ptr, len);
|
||||||
|
}
|
||||||
|
else {
|
||||||
TRAP_BEG;
|
TRAP_BEG;
|
||||||
n = read(fptr->fd, RSTRING(str)->ptr, len);
|
n = read(fptr->fd, RSTRING(str)->ptr, len);
|
||||||
TRAP_END;
|
TRAP_END;
|
||||||
|
}
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
if (rb_io_wait_readable(fptr->fd))
|
if (!nonblock && rb_io_wait_readable(fptr->fd))
|
||||||
goto again;
|
goto again;
|
||||||
rb_sys_fail(fptr->path);
|
rb_sys_fail(fptr->path);
|
||||||
}
|
}
|
||||||
@ -1353,13 +1377,89 @@ io_readpartial(int argc, VALUE *argv, VALUE io)
|
|||||||
{
|
{
|
||||||
VALUE ret;
|
VALUE ret;
|
||||||
|
|
||||||
ret = io_getpartial(argc, argv, io);
|
ret = io_getpartial(argc, argv, io, 0);
|
||||||
if (NIL_P(ret))
|
if (NIL_P(ret))
|
||||||
rb_eof_error();
|
rb_eof_error();
|
||||||
else
|
else
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* ios.read_nonblock(maxlen) => string
|
||||||
|
* ios.read_nonblock(maxlen, outbuf) => outbuf
|
||||||
|
*
|
||||||
|
* Reads at most <i>maxlen</i> bytes from <em>ios</em> using
|
||||||
|
* read(2) system call after O_NONBLOCK is set for
|
||||||
|
* the underlying file descriptor.
|
||||||
|
*
|
||||||
|
* If the optional <i>outbuf</i> argument is present,
|
||||||
|
* it must reference a String, which will receive the data.
|
||||||
|
*
|
||||||
|
* read_nonblock just calls read(2).
|
||||||
|
* It causes all errors read(2) causes: EAGAIN, EINTR, etc.
|
||||||
|
* The caller should care such errors.
|
||||||
|
*
|
||||||
|
* read_nonblock causes EOFError on EOF.
|
||||||
|
*
|
||||||
|
* If the read buffer is not empty,
|
||||||
|
* read_nonblock reads from the buffer like readpartial.
|
||||||
|
* In this case, read(2) is not called.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
io_read_nonblock(int argc, VALUE *argv, VALUE io)
|
||||||
|
{
|
||||||
|
VALUE ret;
|
||||||
|
|
||||||
|
ret = io_getpartial(argc, argv, io, 1);
|
||||||
|
if (NIL_P(ret))
|
||||||
|
rb_eof_error();
|
||||||
|
else
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* ios.write_nonblock(string) => integer
|
||||||
|
*
|
||||||
|
* Writes the given string to <em>ios</em> using
|
||||||
|
* write(2) system call after O_NONBLOCK is set for
|
||||||
|
* the underlying file descriptor.
|
||||||
|
*
|
||||||
|
* write_nonblock just calls write(2).
|
||||||
|
* It causes all errors write(2) causes: EAGAIN, EINTR, etc.
|
||||||
|
* The result may also be smaller than string.length (partial write).
|
||||||
|
* The caller should care such errors and partial write.
|
||||||
|
*
|
||||||
|
* If the write buffer is not empty, it is flushed at first.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
rb_io_write_nonblock(VALUE io, VALUE str)
|
||||||
|
{
|
||||||
|
OpenFile *fptr;
|
||||||
|
long n;
|
||||||
|
|
||||||
|
rb_secure(4);
|
||||||
|
if (TYPE(str) != T_STRING)
|
||||||
|
str = rb_obj_as_string(str);
|
||||||
|
|
||||||
|
GetOpenFile(io, fptr);
|
||||||
|
rb_io_check_writable(fptr);
|
||||||
|
|
||||||
|
io_fflush(fptr);
|
||||||
|
|
||||||
|
rb_io_set_nonblock(fptr);
|
||||||
|
n = write(fptr->fd, RSTRING(str)->ptr, RSTRING(str)->len);
|
||||||
|
|
||||||
|
if (n == -1) rb_sys_fail(fptr->path);
|
||||||
|
|
||||||
|
return LONG2FIX(n);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* ios.read([length [, buffer]]) => string, buffer, or nil
|
* ios.read([length [, buffer]]) => string, buffer, or nil
|
||||||
@ -5194,7 +5294,7 @@ argf_readpartial(int argc, VALUE *argv)
|
|||||||
rb_eEOFError, (VALUE)0);
|
rb_eEOFError, (VALUE)0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
tmp = io_getpartial(argc, argv, current_file);
|
tmp = io_getpartial(argc, argv, current_file, 0);
|
||||||
}
|
}
|
||||||
if (NIL_P(tmp)) {
|
if (NIL_P(tmp)) {
|
||||||
if (next_p == -1) {
|
if (next_p == -1) {
|
||||||
@ -5537,6 +5637,8 @@ Init_IO(void)
|
|||||||
|
|
||||||
rb_define_method(rb_cIO, "readlines", rb_io_readlines, -1);
|
rb_define_method(rb_cIO, "readlines", rb_io_readlines, -1);
|
||||||
|
|
||||||
|
rb_define_method(rb_cIO, "read_nonblock", io_read_nonblock, -1);
|
||||||
|
rb_define_method(rb_cIO, "write_nonblock", rb_io_write_nonblock, 1);
|
||||||
rb_define_method(rb_cIO, "readpartial", io_readpartial, -1);
|
rb_define_method(rb_cIO, "readpartial", io_readpartial, -1);
|
||||||
rb_define_method(rb_cIO, "read", io_read, -1);
|
rb_define_method(rb_cIO, "read", io_read, -1);
|
||||||
rb_define_method(rb_cIO, "write", io_write, 1);
|
rb_define_method(rb_cIO, "write", io_write, 1);
|
||||||
|
1
rubyio.h
1
rubyio.h
@ -93,6 +93,7 @@ void rb_io_check_initialized(OpenFile*);
|
|||||||
void rb_io_check_closed(OpenFile*);
|
void rb_io_check_closed(OpenFile*);
|
||||||
int rb_io_wait_readable(int);
|
int rb_io_wait_readable(int);
|
||||||
int rb_io_wait_writable(int);
|
int rb_io_wait_writable(int);
|
||||||
|
void rb_io_set_nonblock(OpenFile *fptr);
|
||||||
|
|
||||||
VALUE rb_io_taint_check(VALUE);
|
VALUE rb_io_taint_check(VALUE);
|
||||||
NORETURN(void rb_eof_error(void));
|
NORETURN(void rb_eof_error(void));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user