# HG changeset patch # User Tero Marttila # Date 1250446416 -10800 # Node ID 314d47bdd4d99a211eb96cf45a7449a2d0dbc2a4 # Parent 7ae92c2b433f916b1d6d38f2d05e9d0e52a7991e full range of send/write operations diff -r 7ae92c2b433f -r 314d47bdd4d9 qmsk/net/libc.pxd --- a/qmsk/net/libc.pxd Sun Aug 16 20:22:06 2009 +0300 +++ b/qmsk/net/libc.pxd Sun Aug 16 21:13:36 2009 +0300 @@ -35,6 +35,10 @@ char* strerror (int errno) +cdef extern from "unistd.h" : + ssize_t read (int fd, void *buf, size_t count) + ssize_t write (int fd, void *buf, size_t count) + cdef extern from "alloca.h" : void* alloca (size_t size) @@ -42,6 +46,9 @@ struct iovec : void *iov_base size_t iov_len + + ssize_t readv (int fd, iovec *iov, int iovcnt) + ssize_t writev (int fd, iovec *iov, int iovcnt) ## general errno-based errors #cdef class Errno (py.OSError) : diff -r 7ae92c2b433f -r 314d47bdd4d9 qmsk/net/socket/__init__.py --- a/qmsk/net/socket/__init__.py Sun Aug 16 20:22:06 2009 +0300 +++ b/qmsk/net/socket/__init__.py Sun Aug 16 21:13:36 2009 +0300 @@ -1,6 +1,6 @@ # combine all the modules +#from qmsk.net.socket.constants import * +#from qmsk.net.socket.platform import * #from qmsk.net.socket.addr import * #from qmsk.net.socket.socket import * -#from qmsk.net.socket.platform import * -#from qmsk.net.socket.constants import * diff -r 7ae92c2b433f -r 314d47bdd4d9 qmsk/net/socket/socket.pyx --- 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, 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, 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( &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.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.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 +