--- a/qmsk/net/socket/socket.pyx Sun Aug 16 20:22:06 2009 +0300
+++ b/qmsk/net/socket/socket.pyx Sun Aug 16 21:13:36 2009 +0300
@@ -1,11 +1,39 @@
from qmsk.net.socket.socket cimport *
from qmsk.net.socket.addr cimport sockaddr, build_sockaddr
+
cimport qmsk.net.socket.platform as platform
-
cimport qmsk.net.libc as libc, qmsk.net.py as py
from qmsk.net.py cimport raise_errno
+cdef parse_sockaddr (platform.sockaddr **sa_ptr, platform.socklen_t *sa_len, sockaddr addr, int optional = 0) :
+ if addr is not None :
+ addr._get_sockaddr(sa_ptr, sa_len)
+
+ elif optional :
+ sa_ptr[0] = NULL
+ sa_len[0] = 0
+
+ else :
+ raise ValueError(addr)
+
+cdef parse_buf (void **buf_ptr, size_t *buf_len, object buf, int optional = 0) :
+ cdef libc.ssize_t tmp_len
+
+ if buf is not None :
+ # XXX: test that except works right
+ py.PyObject_AsCharBuffer(buf, <char **> buf_ptr, &tmp_len)
+
+ # XXX: ensure that this is >= 0
+ buf_len[0] = tmp_len
+
+ elif optional :
+ buf_ptr[0] = NULL
+ buf_len[0] = 0
+
+ else :
+ raise ValueError(buf)
+
# XXX: do some GIL-releasin'
cdef class socket :
@@ -44,14 +72,14 @@
ephemeral port.
"""
- cdef platform.sockaddr *sa_ptr
+ cdef platform.sockaddr *sa
cdef platform.socklen_t sa_len
-
- # get the address
- addr._get_sockaddr(&sa_ptr, &sa_len)
+
+ # XXX: require non-NULL addr?
+ parse_sockaddr(&sa, &sa_len, addr, 1)
# bind()
- if platform.bind(self.fd, sa_ptr, sa_len) :
+ if platform.bind(self.fd, sa, sa_len) :
raise_errno('bind')
def listen (self, int backlog) :
@@ -81,14 +109,14 @@
addr - the remote address to connect to.
"""
- cdef platform.sockaddr *sa_ptr
+ cdef platform.sockaddr *sa
cdef platform.socklen_t sa_len
- # get the address
- addr._get_sockaddr(&sa_ptr, &sa_len)
+ # XXX: require non-NULL addr?
+ parse_sockaddr(&sa, &sa_len, addr, 1)
# connect()
- if platform.connect(self.fd, sa_ptr, sa_len) :
+ if platform.connect(self.fd, sa, sa_len) :
raise_errno('connect')
def accept (self) :
@@ -126,21 +154,20 @@
"""
Transmit a message to the connected remote endpoint.
- buf - the data to send
- flags - MSG_* flags to send with
+ buf - the data to send
+ flags - (optional) MSG_* flags to send with
Returns the number of bytes sent, which may be less than the length of buf.
"""
- cdef char *buf_ptr
- cdef libc.ssize_t buf_len, ret
+ cdef void *buf_ptr
+ cdef size_t buf_len
+ cdef libc.ssize_t ret
- # get buffer
- # XXX: test that except works right
- py.PyObject_AsCharBuffer(buf, &buf_ptr, &buf_len)
-
+ parse_buf(&buf_ptr, &buf_len, buf, 0)
+
# send()
- ret = platform.send(self.fd, <void *> buf_ptr, buf_len, flags)
+ ret = platform.send(self.fd, buf_ptr, buf_len, flags)
if ret < 0 :
raise_errno('send')
@@ -148,3 +175,143 @@
else :
return ret
+ def sendto (self, object buf, int flags = 0, sockaddr addr = None) :
+ """
+ Transmit a message to the given remote endpoint. If this socket is connected, the addr must not be
+ specified, and this acts like send()
+
+ buf - the data to send
+ flags - (optional) MSG_* flags to send with
+ addr - (optional) target address
+
+ Returns the number of bytes sent, which may be less than the length of buf.
+ """
+
+ cdef void *buf_ptr
+ cdef size_t buf_len
+ cdef libc.ssize_t ret
+
+ cdef platform.sockaddr *sa
+ cdef platform.socklen_t sa_len
+
+ parse_sockaddr(&sa, &sa_len, addr, 1)
+ parse_buf(&buf_ptr, &buf_len, buf, 0)
+
+ # send()
+ ret = platform.sendto(self.fd, buf_ptr, buf_len, flags, sa, sa_len)
+
+ if ret < 0 :
+ raise_errno('sendto')
+
+ else :
+ return ret
+
+ def sendmsg (self, sockaddr addr = None, iov = None, control = None, int flags = 0) :
+ """
+ Transmit an extended message to the given remote endpoint (or default for connected sockets) with the given
+ extra parameters.
+
+ addr - (optional) destination address (struct msghdr::msg_name)
+ iov - (optional) sequence of read-buffers to transmit
+ control - (optional) control message to transmit
+ flags - (optional) MSG_* flags to send with
+
+ Returns the number of bytes sent, which may be less than the total length of iov.
+ """
+
+ cdef libc.ssize_t ret
+ cdef libc.iovec *iovec
+ cdef platform.msghdr msg
+
+ libc.memset(&msg, 0, sizeof(msg))
+
+ parse_sockaddr(<platform.sockaddr **> &msg.msg_name, &msg.msg_namelen, addr, 1)
+ parse_buf(&msg.msg_control, &msg.msg_controllen, control, 1)
+
+ # iov
+ if iov :
+ iov = tuple(iov)
+
+ # numerb of bufs = number of iovecs
+ msg.msg_iovlen = len(iov)
+
+ # alloca the required number of iovec's
+ msg.msg_iov = <libc.iovec *> libc.alloca(msg.msg_iovlen * sizeof(libc.iovec))
+
+ # fill in the iovecs
+ for i, buf in enumerate(iov) :
+ iovec = &msg.msg_iov[i]
+
+ parse_buf(&iovec.iov_base, &iovec.iov_len, buf, 1)
+
+ # sendmsg()
+ ret = platform.sendmsg(self.fd, &msg, flags)
+
+ if ret < 0 :
+ raise_errno('sendmsg')
+
+ else :
+ return ret
+
+ def write (self, object buf) :
+ """
+ Write data to socket, mostly equivalent to send() with flags=0.
+
+ buf - the data to send
+
+ Returns the number of bytes sent, which may be less than the length of buf.
+ """
+
+ cdef void *buf_ptr
+ cdef size_t buf_len
+ cdef libc.ssize_t ret
+
+ parse_buf(&buf_ptr, &buf_len, buf, 0)
+
+ # send()
+ ret = libc.write(self.fd, buf_ptr, buf_len)
+
+ if ret < 0 :
+ raise_errno('write')
+
+ else :
+ return ret
+
+ def writev (self, iov) :
+ """
+ Write data to a socket from multiple read-buffers.
+
+ iov - sequence of read-buffers to transmit
+
+ Returns the number of bytes sent, which may be less than the total length of iov.
+ """
+
+ # iov
+ cdef libc.iovec *iov_list = NULL
+ cdef size_t iov_count = 0
+ cdef libc.iovec *iovec
+
+ iov = tuple(iov)
+
+ # numerb of bufs = number of iovecs
+ iov_count = len(iov)
+
+ # alloca the required number of iovec's
+ iov_list = <libc.iovec *> libc.alloca(iov_count * sizeof(libc.iovec))
+
+ # fill in the iovecs
+ for i, buf in enumerate(iov) :
+ iovec = &iov_list[i]
+
+
+ parse_buf(&iovec.iov_base, &iovec.iov_len, buf, 1)
+
+ # sendmsg()
+ ret = libc.writev(self.fd, iov_list, iov_count)
+
+ if ret < 0 :
+ raise_errno('writev')
+
+ else :
+ return ret
+