terom@7: from qmsk.net.socket.socket cimport * terom@49: from qmsk.net.socket._address cimport sockaddr terom@49: from qmsk.net.socket.address cimport build_sockaddr terom@12: terom@10: cimport qmsk.net.socket.platform as platform terom@11: cimport qmsk.net.libc as libc, qmsk.net.py as py terom@6: terom@11: from qmsk.net.py cimport raise_errno terom@6: terom@12: cdef parse_sockaddr (platform.sockaddr **sa_ptr, platform.socklen_t *sa_len, sockaddr addr, int optional = 0) : terom@12: if addr is not None : terom@12: addr._get_sockaddr(sa_ptr, sa_len) terom@12: terom@12: elif optional : terom@12: sa_ptr[0] = NULL terom@12: sa_len[0] = 0 terom@12: terom@12: else : terom@12: raise ValueError(addr) terom@12: terom@12: cdef parse_buf (void **buf_ptr, size_t *buf_len, object buf, int optional = 0) : terom@12: cdef libc.ssize_t tmp_len terom@12: terom@12: if buf is not None : terom@12: # XXX: test that except works right terom@14: # XXX: this complains about const... terom@12: py.PyObject_AsCharBuffer(buf, buf_ptr, &tmp_len) terom@12: terom@12: # XXX: ensure that this is >= 0 terom@12: buf_len[0] = tmp_len terom@12: terom@12: elif optional : terom@12: buf_ptr[0] = NULL terom@12: buf_len[0] = 0 terom@12: terom@12: else : terom@12: raise ValueError(buf) terom@12: terom@16: ## helper for socket.recv* terom@16: # XXX: make sure these don't leak the PyString in case of errors... terom@16: cdef struct sockbuf : terom@16: py.PyObject *str terom@16: terom@16: cdef char* sockbuf_init (sockbuf *buf, size_t len) : terom@16: """ terom@16: Initialize the sockbuf to contain a PyString that can hold `len` bytes, and return a pointer into its terom@16: contents as a char*. terom@16: terom@16: Note that this requires use of a try-finally with sockbuf_deinit... terom@16: """ terom@16: terom@16: buf.str = py.PyString_FromStringAndSize(NULL, len) terom@16: terom@16: return py.PyString_AS_STRING(buf.str) terom@16: terom@16: cdef object sockbuf_truncate (sockbuf *buf, size_t len) : terom@16: """ terom@16: Truncate the given sockbuf's PyString to the given length, and return the PyObject* terom@16: """ terom@16: terom@16: # optimize for the no-need-to-resize case terom@16: # this also fixes behaviour for zero-length strings (heh), since they're interned and can't be resized terom@16: if len != py.PyString_GET_SIZE(buf.str) : terom@16: py._PyString_Resize(&buf.str, len) terom@16: terom@16: return buf.str terom@16: terom@16: cdef void sockbuf_deinit (sockbuf *buf) : terom@16: """ terom@16: Release the PyObject. terom@16: terom@16: This is safe if the sockbuf was initialized to zero. terom@16: """ terom@16: terom@16: py.Py_XDECREF(buf.str) terom@16: terom@6: # XXX: do some GIL-releasin' terom@8: cdef class socket : terom@6: terom@13: def __cinit__ (self) : terom@6: """ terom@13: Initialize the socket to set fd to -1, so that we dont't try and close stdin too often :) terom@6: """ terom@6: terom@13: self.fd = -1 terom@6: terom@13: def __init__ (self, int family = platform.AF_INET, int socktype = platform.SOCK_STREAM, int protocol = 0, int fd = -1) : terom@6: """ terom@13: Create a new socket endpoint with the given family/domain, socktype and optionally, specific protocol, terom@13: unless the fd argument is given as >= 0, in which case it used directly. terom@6: terom@6: family - one of AF_* terom@6: socktype - one of SOCK_* terom@6: protocol - one of IPPROTO_* or zero to select default terom@6: """ terom@6: terom@13: if fd >= 0 : terom@13: # given fd terom@13: self.fd = fd terom@13: terom@13: else : terom@13: # socket() terom@13: self.fd = platform.socket(family, socktype, protocol) terom@6: terom@6: # trap terom@6: if self.fd < 0 : terom@6: raise_errno('socket') terom@6: terom@14: def fileno (self) : terom@14: """ terom@14: Returns the OS-level file desriptor for this socket as an integer terom@14: """ terom@14: terom@14: return self.fd terom@19: terom@24: # # XXX: does this *really* belong here? terom@24: # def setblocking (self, bint blocking = True) : terom@24: # """ terom@24: # Control the OS-level nonblocking-IO mode flag for this socket. terom@24: # terom@24: # blocking - True for normal blocking operation, False to use non-blocking operation terom@24: # """ terom@24: # terom@24: # # fcntl magic terom@24: # libc.fcntl_set_flag(self.fd, libc.O_NONBLOCK, not blocking) terom@14: terom@6: def bind (self, sockaddr addr) : terom@6: """ terom@6: Bind this socket to the given local socket address. The given sockaddr should be of the same or a terom@6: compatible address family. terom@6: terom@6: addr - the local address to bind to. The port may be zero to let the system choose an unused terom@6: ephemeral port. terom@6: """ terom@6: terom@12: cdef platform.sockaddr *sa terom@10: cdef platform.socklen_t sa_len terom@12: terom@12: # XXX: require non-NULL addr? terom@12: parse_sockaddr(&sa, &sa_len, addr, 1) terom@6: terom@6: # bind() terom@12: if platform.bind(self.fd, sa, sa_len) : terom@6: raise_errno('bind') terom@6: terom@6: def listen (self, int backlog) : terom@6: """ terom@6: Listen for connections, marking this socket as a passive socket, which can accept incoming connection terom@6: requests using sock.accept(). terom@6: terom@6: It is customary to call .bind() before .listen(). terom@6: terom@6: backlog - maximum number of pending connections (those not yet .accept()'d). terom@6: """ terom@6: terom@6: # listen() terom@10: if platform.listen(self.fd, backlog) : terom@6: raise_errno('listen') terom@6: terom@6: def connect (self, sockaddr addr) : terom@6: """ terom@6: Initiate a connection, connecting this socket to the remote endpoint specified by `addr`. The given sockaddr terom@6: should be of the same or a compatible address family. terom@6: terom@6: If the socket is in non-blocking mode, this will presumeably return errno.EINPROGRESS. terom@6: terom@6: If the socket has not yet been bound (using .bind()), the system will pick an appropriate local address and terom@6: ephemeral port. terom@6: terom@6: addr - the remote address to connect to. terom@6: """ terom@6: terom@12: cdef platform.sockaddr *sa terom@10: cdef platform.socklen_t sa_len terom@6: terom@12: # XXX: require non-NULL addr? terom@12: parse_sockaddr(&sa, &sa_len, addr, 1) terom@6: terom@6: # connect() terom@12: if platform.connect(self.fd, sa, sa_len) : terom@6: raise_errno('connect') terom@6: terom@6: def accept (self) : terom@6: """ terom@6: Accept a connection, dequeueing the first pending connection and returning a new sock object for it. This terom@6: socket must be a connection-based socket (SOCK_STREAM/SOCK_SEQPACKET) and in the passive listening mode terom@6: (.listen()). terom@6: terom@14: This returns a (sock, src_addr) tuple: terom@6: sock - the newly created sock, corresponding to the incoming connection terom@14: src_addr - the remote address of the incoming connection terom@6: """ terom@6: terom@6: # prep the sockaddr that we will return terom@10: cdef platform.sockaddr_storage ss terom@10: cdef platform.socklen_t ss_len = sizeof(ss) terom@6: terom@6: # accept() terom@14: cdef socket_t sock_fd = platform.accept(self.fd, &ss, &ss_len) terom@6: terom@6: if sock_fd < 0 : terom@6: raise_errno('accept') terom@13: terom@13: try : terom@13: # prep the new socket terom@15: sock_obj = socket(fd=sock_fd) terom@6: terom@13: except : terom@13: # XXX: don't leak the socket fd? How does socket.__init__ handle this? terom@13: platform.close(sock_fd) terom@13: terom@13: raise terom@6: terom@6: # prep the new addr terom@14: cdef sockaddr src_addr = build_sockaddr( &ss, ss_len) terom@6: terom@14: return sock_obj, src_addr terom@18: terom@18: def getsockname (self) : terom@18: """ terom@18: Get the local address this socket is currently bound to. This can be set using bind(), or automatically. terom@18: terom@18: Returns a sockaddr object. terom@18: """ terom@18: terom@18: # prep the sockaddr that we will return terom@18: cdef platform.sockaddr_storage ss terom@18: cdef platform.socklen_t ss_len = sizeof(ss) terom@18: terom@18: # getsockname() terom@18: if platform.getsockname(self.fd, &ss, &ss_len) : terom@18: raise_errno('getsockname') terom@18: terom@18: # build the new sockaddr terom@18: return build_sockaddr( &ss, ss_len) terom@18: terom@18: def getpeername (self) : terom@18: """ terom@18: Get the remote address this socket is currently connected to. terom@18: terom@18: Returns a sockaddr object. terom@18: """ terom@18: terom@18: # prep the sockaddr that we will return terom@18: cdef platform.sockaddr_storage ss terom@18: cdef platform.socklen_t ss_len = sizeof(ss) terom@18: terom@18: # getpeername() terom@18: if platform.getpeername(self.fd, &ss, &ss_len) : terom@18: raise_errno('getpeername') terom@18: terom@18: # build the new sockaddr terom@18: return build_sockaddr( &ss, ss_len) terom@53: terom@53: def getsockopt_int (self, int level, int opt) : terom@53: """ terom@53: Get and return the integer value of the given socket option. terom@53: """ terom@53: terom@53: cdef int val terom@53: cdef platform.socklen_t val_len = sizeof(val) terom@53: terom@53: # run terom@53: if platform.getsockopt(self.fd, level, opt, &val, &val_len) : terom@53: raise_errno('getsockopt') terom@53: terom@53: # check value length terom@53: if val_len != sizeof(val) : terom@53: raise ValueError("value length: %d, should be: %d" % (val_len, sizeof(val))) terom@53: terom@53: return val terom@6: terom@6: def send (self, object buf, int flags = 0) : terom@6: """ terom@6: Transmit a message to the connected remote endpoint. terom@6: terom@12: buf - the data to send terom@12: flags - (optional) MSG_* flags to send with terom@6: terom@6: Returns the number of bytes sent, which may be less than the length of buf. terom@6: """ terom@6: terom@12: cdef void *buf_ptr terom@12: cdef size_t buf_len terom@12: cdef libc.ssize_t ret terom@6: terom@12: parse_buf(&buf_ptr, &buf_len, buf, 0) terom@12: terom@6: # send() terom@12: ret = platform.send(self.fd, buf_ptr, buf_len, flags) terom@6: terom@6: if ret < 0 : terom@6: raise_errno('send') terom@6: terom@6: else : terom@6: return ret terom@6: terom@12: def sendto (self, object buf, int flags = 0, sockaddr addr = None) : terom@12: """ terom@12: Transmit a message to the given remote endpoint. If this socket is connected, the addr must not be terom@12: specified, and this acts like send() terom@12: terom@12: buf - the data to send terom@12: flags - (optional) MSG_* flags to send with terom@12: addr - (optional) target address terom@12: terom@12: Returns the number of bytes sent, which may be less than the length of buf. terom@12: """ terom@12: terom@12: cdef void *buf_ptr terom@12: cdef size_t buf_len terom@12: cdef libc.ssize_t ret terom@12: terom@12: cdef platform.sockaddr *sa terom@12: cdef platform.socklen_t sa_len terom@12: terom@12: parse_sockaddr(&sa, &sa_len, addr, 1) terom@12: parse_buf(&buf_ptr, &buf_len, buf, 0) terom@12: terom@12: # send() terom@12: ret = platform.sendto(self.fd, buf_ptr, buf_len, flags, sa, sa_len) terom@12: terom@12: if ret < 0 : terom@12: raise_errno('sendto') terom@12: terom@12: else : terom@12: return ret terom@12: terom@12: def sendmsg (self, sockaddr addr = None, iov = None, control = None, int flags = 0) : terom@12: """ terom@12: Transmit an extended message to the given remote endpoint (or default for connected sockets) with the given terom@12: extra parameters. terom@12: terom@12: addr - (optional) destination address (struct msghdr::msg_name) terom@12: iov - (optional) sequence of read-buffers to transmit terom@12: control - (optional) control message to transmit terom@12: flags - (optional) MSG_* flags to send with terom@12: terom@12: Returns the number of bytes sent, which may be less than the total length of iov. terom@12: """ terom@12: terom@12: cdef libc.ssize_t ret terom@12: cdef libc.iovec *iovec terom@12: cdef platform.msghdr msg terom@12: terom@12: libc.memset(&msg, 0, sizeof(msg)) terom@12: terom@12: parse_sockaddr( &msg.msg_name, &msg.msg_namelen, addr, 1) terom@12: parse_buf(&msg.msg_control, &msg.msg_controllen, control, 1) terom@12: terom@12: # iov terom@12: if iov : terom@12: iov = tuple(iov) terom@12: terom@12: # numerb of bufs = number of iovecs terom@12: msg.msg_iovlen = len(iov) terom@12: terom@12: # alloca the required number of iovec's terom@12: msg.msg_iov = libc.alloca(msg.msg_iovlen * sizeof(libc.iovec)) terom@12: terom@12: # fill in the iovecs terom@12: for i, buf in enumerate(iov) : terom@12: iovec = &msg.msg_iov[i] terom@12: terom@12: parse_buf(&iovec.iov_base, &iovec.iov_len, buf, 1) terom@12: terom@12: # sendmsg() terom@12: ret = platform.sendmsg(self.fd, &msg, flags) terom@12: terom@12: if ret < 0 : terom@12: raise_errno('sendmsg') terom@12: terom@12: else : terom@12: return ret terom@12: terom@12: def write (self, object buf) : terom@12: """ terom@12: Write data to socket, mostly equivalent to send() with flags=0. terom@12: terom@12: buf - the data to send terom@12: terom@12: Returns the number of bytes sent, which may be less than the length of buf. terom@12: """ terom@12: terom@12: cdef void *buf_ptr terom@12: cdef size_t buf_len terom@12: cdef libc.ssize_t ret terom@12: terom@12: parse_buf(&buf_ptr, &buf_len, buf, 0) terom@12: terom@12: # send() terom@12: ret = libc.write(self.fd, buf_ptr, buf_len) terom@12: terom@12: if ret < 0 : terom@12: raise_errno('write') terom@12: terom@12: else : terom@12: return ret terom@12: terom@12: def writev (self, iov) : terom@12: """ terom@12: Write data to a socket from multiple read-buffers. terom@12: terom@12: iov - sequence of read-buffers to transmit terom@12: terom@12: Returns the number of bytes sent, which may be less than the total length of iov. terom@12: """ terom@12: terom@12: # iov terom@12: cdef libc.iovec *iov_list = NULL terom@12: cdef size_t iov_count = 0 terom@12: cdef libc.iovec *iovec terom@12: terom@12: iov = tuple(iov) terom@12: terom@12: # numerb of bufs = number of iovecs terom@12: iov_count = len(iov) terom@12: terom@12: # alloca the required number of iovec's terom@12: iov_list = libc.alloca(iov_count * sizeof(libc.iovec)) terom@12: terom@12: # fill in the iovecs terom@12: for i, buf in enumerate(iov) : terom@12: iovec = &iov_list[i] terom@12: terom@12: parse_buf(&iovec.iov_base, &iovec.iov_len, buf, 1) terom@12: terom@12: # sendmsg() terom@12: ret = libc.writev(self.fd, iov_list, iov_count) terom@12: terom@12: if ret < 0 : terom@12: raise_errno('writev') terom@12: terom@12: else : terom@12: return ret terom@14: terom@14: def recv (self, size_t len, int flags = 0) : terom@14: """ terom@14: Recieve a message, reading and returning at most `len` bytes. terom@13: terom@14: len - size of buffer to use for recv terom@14: flags - (optional) MSG_* flags to use for recv() terom@14: terom@14: Returns the recieved data as a newly allocated string of the correct length. terom@14: """ terom@14: terom@16: cdef sockbuf sb terom@16: cdef libc.ssize_t ret terom@15: terom@16: # alloc the recv buffer terom@16: cdef char *buf = sockbuf_init(&sb, len) terom@16: terom@16: try : terom@16: # recv() terom@16: ret = platform.recv(self.fd, buf, len, flags) terom@15: terom@16: if ret < 0 : terom@16: raise_errno('recv') terom@16: terom@16: # truncate to correct length terom@16: return sockbuf_truncate(&sb, ret) terom@16: terom@16: finally : terom@16: sockbuf_deinit(&sb) terom@14: terom@14: def recvfrom (self, size_t len, int flags = 0) : terom@14: """ terom@14: Recieve a message, reading at most `len` bytes, also returning the source address. terom@14: terom@14: len - size of buffer to use for recv terom@14: flags - (optional) MSG_* flags to use for recvfrom() terom@14: terom@14: Returns the recieved data and the source address as a (buf, src_addr) tuple : terom@14: buf - a newly allocated string containing the recieved data, of the correct length terom@14: src_addr - the source address the message was recieved from terom@14: """ terom@14: terom@16: cdef sockbuf sb terom@16: cdef libc.ssize_t ret terom@16: cdef object str terom@16: cdef sockaddr src_addr terom@16: terom@14: # prep the sockaddr that we will return terom@14: cdef platform.sockaddr_storage ss terom@14: cdef platform.socklen_t ss_len = sizeof(ss) terom@14: terom@16: # alloc recv buf terom@16: cdef char *buf = sockbuf_init(&sb, len) terom@16: terom@16: try : terom@16: # recvfrom() terom@16: ret = platform.recvfrom(self.fd, buf, len, flags, &ss, &ss_len) terom@16: terom@16: if ret < 0 : terom@16: raise_errno('recv') terom@16: terom@16: # truncate terom@16: str = sockbuf_truncate(&sb, ret) terom@16: terom@16: # prep the new addr terom@16: src_addr = build_sockaddr( &ss, ss_len) terom@16: terom@16: return str, src_addr terom@16: terom@16: finally : terom@16: sockbuf_deinit(&sb) terom@14: terom@14: def recvmsg (self, bint recv_addr = True, object iov_lens = None, size_t control_len = 0, int flags = 0) : terom@14: """ terom@14: Recieve a message along with some extra data. terom@14: terom@14: recv_addr - ask for and return a sockaddr for the source address of the message? terom@17: iov_lens - (optional) sequence of buffer sizes to use for the message data iov terom@14: control_len - (optional) amount of auxiliary data to recieve terom@14: flags - (optional) flags to pass to recvmsg() terom@14: terom@16: Returns a (name, iovs, control, flags) tuple : terom@14: name - the source address of the message, or None terom@27: iovs - sequence of buffers containing the recieved data corresponding to the iov_lens. terom@27: This may contain fewer buffers than iov_lens if there was insufficient data to fill all buffers. terom@14: control - string containing recieved control message, if any terom@14: flags - recieved flags terom@14: """ terom@14: terom@14: cdef platform.msghdr msg terom@16: cdef sockbuf *sb_list, *sb, cmsg_sb terom@16: cdef libc.iovec *iovec terom@16: cdef size_t iov_len, i, msg_len terom@16: cdef libc.ssize_t ret terom@16: cdef sockaddr name = None terom@16: cdef object iovs = None terom@16: cdef object control = None terom@14: terom@14: libc.memset(&msg, 0, sizeof(msg)) terom@16: libc.memset(&cmsg_sb, 0, sizeof(cmsg_sb)) terom@14: terom@14: # prep the sockaddr that we will return terom@14: cdef platform.sockaddr_storage ss terom@14: terom@14: # ask for a name? terom@14: if recv_addr : terom@14: msg.msg_name = &ss terom@14: msg.msg_namelen = sizeof(ss) terom@14: terom@16: try : terom@16: # build iov? terom@16: if iov_lens : terom@16: # stabilize terom@16: iov_lens = tuple(iov_lens) terom@16: terom@16: msg.msg_iovlen = len(iov_lens) terom@16: terom@16: # alloc each iov plus a sockbuf for storing the PyString terom@16: msg.msg_iov = libc.alloca(msg.msg_iovlen * sizeof(libc.iovec)) terom@16: sb_list = libc.alloca(msg.msg_iovlen * sizeof(sockbuf)) terom@16: terom@16: # zero out so we can cleanup terom@16: libc.memset(sb_list, 0, msg.msg_iovlen * sizeof(sockbuf)) terom@16: terom@16: # build each terom@16: for i, iov_len in enumerate(iov_lens) : terom@16: # the associated iovec/sockbuf terom@16: iovec = &msg.msg_iov[i] terom@16: sb = &sb_list[i] terom@16: terom@16: # set up the sockbuf and iovec terom@16: iovec.iov_base = sockbuf_init(&sb_list[i], iov_len) terom@16: iovec.iov_len = iov_len terom@14: terom@16: # build control buffer? terom@16: if control_len : terom@16: msg.msg_control = sockbuf_init(&cmsg_sb, control_len) terom@16: msg.msg_controllen = control_len terom@14: terom@16: # recvmsg() terom@16: ret = platform.recvmsg(self.fd, &msg, flags) terom@14: terom@16: if ret < 0 : terom@16: raise_errno('recvmsg') terom@14: terom@16: # name? terom@16: if msg.msg_name and msg.msg_namelen : terom@16: # build a sockaddr for the name terom@16: name = build_sockaddr( msg.msg_name, msg.msg_namelen) terom@14: terom@16: # iov? terom@16: if ret : terom@16: assert msg.msg_iov and msg.msg_iovlen terom@16: terom@16: iovs = [] terom@16: msg_len = ret terom@16: i = 0 terom@16: terom@16: # consume iov's until we have all the data we need terom@16: while msg_len : terom@16: # sanity-check terom@16: assert i < msg.msg_iovlen terom@14: terom@16: # get the associated iovec/sockbuf terom@16: iovec = &msg.msg_iov[i] terom@16: sb = &sb_list[i] terom@16: terom@16: # calc the size of this iov terom@16: # XXX: cdef terom@16: iov_len = min(msg_len, iovec.iov_len) terom@14: terom@16: # add it as a string terom@16: iovs.append(sockbuf_truncate(sb, iov_len)) terom@16: terom@16: # advance terom@16: msg_len -= iov_len terom@16: i += 1 terom@16: terom@16: # control? terom@16: if msg.msg_control and msg.msg_controllen : terom@16: # build the PyString for the control message terom@16: control = sockbuf_truncate(&cmsg_sb, msg.msg_controllen) terom@16: terom@16: return name, iovs, control, msg.msg_flags terom@16: terom@16: finally : terom@16: # cleanup terom@16: sockbuf_deinit(&cmsg_sb) terom@16: terom@16: for i in range(msg.msg_iovlen) : terom@16: sockbuf_deinit(&sb_list[i]) terom@13: terom@17: def read (self, size_t len) : terom@17: """ terom@17: Read data from a socket, mostly equivalent to a recv() with flags=0. terom@17: terom@17: len - size of buffer to use for recv terom@17: terom@17: Returns the recieved data as a newly allocated string of the correct length. terom@17: """ terom@17: terom@17: cdef sockbuf sb terom@17: cdef libc.ssize_t ret terom@17: terom@17: # alloc the recv buffer terom@17: cdef char *buf = sockbuf_init(&sb, len) terom@17: terom@17: try : terom@17: # recv() terom@17: ret = libc.read(self.fd, buf, len) terom@17: terom@17: if ret < 0 : terom@17: raise_errno('read') terom@17: terom@17: # truncate to correct length terom@17: return sockbuf_truncate(&sb, ret) terom@17: terom@17: finally : terom@17: sockbuf_deinit(&sb) terom@17: terom@17: def readv (self, object iov_lens) : terom@17: """ terom@17: Read data from a socket into multiple buffers. terom@17: terom@17: iov_lens - sequence of buffer sizes to use as iovs terom@17: terom@17: Returns a sequence of strings containing the recieved data corresponding to the iov_lens. terom@17: terom@17: XXX: implement using real readv instead of faking it with recvmsg... terom@17: """ terom@17: terom@17: # fake using recvmsg terom@17: _, iovs, _, _ = self.recvmsg(recv_addr=False, iov_lens=iov_lens) terom@17: terom@17: return iovs terom@17: terom@13: def shutdown (self, how) : terom@13: """ terom@13: Shutdown part of a full-duplex connection. terom@13: terom@13: how - one of SHUT_* terom@13: terom@13: This does not affect this socket's fd. terom@13: """ terom@13: terom@13: # shutdown() terom@13: if platform.shutdown(self.fd, how) : terom@13: raise_errno('shutdown') terom@13: terom@13: def close (self) : terom@13: """ terom@13: Close the socket fd if we have one, invalidating it if succesful. terom@13: terom@13: Note that this will raise an error and keep the fd if the system close() returns an error. terom@13: terom@13: Calling this again after a succesfull close() does nothing. terom@13: terom@13: XXX: SO_LINGER/blocking? terom@13: terom@13: >>> s = socket() terom@13: >>> s.fd >= 0 terom@13: True terom@13: >>> s.close() terom@13: >>> s.fd >= 0 terom@13: False terom@13: >>> s.close() terom@13: """ terom@13: terom@13: # ignore if already closed terom@13: if self.fd < 0 : terom@13: return terom@13: terom@13: # close() terom@13: if libc.close(self.fd) : terom@13: raise_errno('close') terom@13: terom@13: # invalidate terom@13: self.fd = -1 terom@13: terom@13: def __dealloc__ (self) : terom@13: """ terom@13: Close the socket fd if one is set, ignoring any errors from close terom@13: """ terom@13: terom@13: if self.fd >= 0 : terom@13: if libc.close(self.fd) : terom@13: # XXX: at least warn... ? terom@13: pass terom@13: