qmsk/net/socket/socket.pyx
changeset 12 314d47bdd4d9
parent 11 7ae92c2b433f
child 13 a1091632a8a7
equal deleted inserted replaced
11:7ae92c2b433f 12:314d47bdd4d9
     1 from qmsk.net.socket.socket cimport *
     1 from qmsk.net.socket.socket cimport *
     2 from qmsk.net.socket.addr cimport sockaddr, build_sockaddr
     2 from qmsk.net.socket.addr cimport sockaddr, build_sockaddr
       
     3 
     3 cimport qmsk.net.socket.platform as platform
     4 cimport qmsk.net.socket.platform as platform
     4 
       
     5 cimport qmsk.net.libc as libc, qmsk.net.py as py
     5 cimport qmsk.net.libc as libc, qmsk.net.py as py
     6 
     6 
     7 from qmsk.net.py cimport raise_errno
     7 from qmsk.net.py cimport raise_errno
       
     8 
       
     9 cdef parse_sockaddr (platform.sockaddr **sa_ptr, platform.socklen_t *sa_len, sockaddr addr, int optional = 0) :
       
    10     if addr is not None :
       
    11         addr._get_sockaddr(sa_ptr, sa_len)
       
    12 
       
    13     elif optional :
       
    14         sa_ptr[0] = NULL
       
    15         sa_len[0] = 0
       
    16 
       
    17     else :
       
    18         raise ValueError(addr)
       
    19 
       
    20 cdef parse_buf (void **buf_ptr, size_t *buf_len, object buf, int optional = 0) :
       
    21     cdef libc.ssize_t tmp_len
       
    22 
       
    23     if buf is not None :
       
    24         # XXX: test that except works right
       
    25         py.PyObject_AsCharBuffer(buf, <char **> buf_ptr, &tmp_len)
       
    26         
       
    27         # XXX: ensure that this is >= 0
       
    28         buf_len[0] = tmp_len
       
    29     
       
    30     elif optional :
       
    31         buf_ptr[0] = NULL
       
    32         buf_len[0] = 0
       
    33 
       
    34     else :
       
    35         raise ValueError(buf)
     8 
    36 
     9 # XXX: do some GIL-releasin'
    37 # XXX: do some GIL-releasin'
    10 cdef class socket :
    38 cdef class socket :
    11 
    39 
    12     def __init__ (self, int fd = -1) :
    40     def __init__ (self, int fd = -1) :
    42 
    70 
    43                 addr        - the local address to bind to. The port may be zero to let the system choose an unused
    71                 addr        - the local address to bind to. The port may be zero to let the system choose an unused
    44                               ephemeral port.
    72                               ephemeral port.
    45         """
    73         """
    46 
    74 
    47         cdef platform.sockaddr *sa_ptr
    75         cdef platform.sockaddr *sa
    48         cdef platform.socklen_t sa_len
    76         cdef platform.socklen_t sa_len
    49 
    77         
    50         # get the address
    78         # XXX: require non-NULL addr?
    51         addr._get_sockaddr(&sa_ptr, &sa_len)
    79         parse_sockaddr(&sa, &sa_len, addr, 1)
    52 
    80 
    53         # bind()
    81         # bind()
    54         if platform.bind(self.fd, sa_ptr, sa_len) :
    82         if platform.bind(self.fd, sa, sa_len) :
    55             raise_errno('bind')
    83             raise_errno('bind')
    56 
    84 
    57     def listen (self, int backlog) :
    85     def listen (self, int backlog) :
    58         """
    86         """
    59             Listen for connections, marking this socket as a passive socket, which can accept incoming connection
    87             Listen for connections, marking this socket as a passive socket, which can accept incoming connection
    79             ephemeral port.
   107             ephemeral port.
    80 
   108 
    81                 addr        - the remote address to connect to.
   109                 addr        - the remote address to connect to.
    82         """
   110         """
    83 
   111 
    84         cdef platform.sockaddr *sa_ptr
   112         cdef platform.sockaddr *sa
    85         cdef platform.socklen_t sa_len
   113         cdef platform.socklen_t sa_len
    86 
   114 
    87         # get the address
   115         # XXX: require non-NULL addr?
    88         addr._get_sockaddr(&sa_ptr, &sa_len)
   116         parse_sockaddr(&sa, &sa_len, addr, 1)
    89         
   117         
    90         # connect()
   118         # connect()
    91         if platform.connect(self.fd, sa_ptr, sa_len) :
   119         if platform.connect(self.fd, sa, sa_len) :
    92             raise_errno('connect')
   120             raise_errno('connect')
    93 
   121 
    94     def accept (self) :
   122     def accept (self) :
    95         """
   123         """
    96             Accept a connection, dequeueing the first pending connection and returning a new sock object for it. This
   124             Accept a connection, dequeueing the first pending connection and returning a new sock object for it. This
   124 
   152 
   125     def send (self, object buf, int flags = 0) :
   153     def send (self, object buf, int flags = 0) :
   126         """
   154         """
   127             Transmit a message to the connected remote endpoint.
   155             Transmit a message to the connected remote endpoint.
   128 
   156 
   129                 buf     - the data to send
   157                 buf         - the data to send
   130                 flags   - MSG_* flags to send with
   158                 flags       - (optional) MSG_* flags to send with
   131 
   159 
   132             Returns the number of bytes sent, which may be less than the length of buf.
   160             Returns the number of bytes sent, which may be less than the length of buf.
   133         """
   161         """
   134 
   162 
   135         cdef char *buf_ptr
   163         cdef void *buf_ptr
   136         cdef libc.ssize_t buf_len, ret
   164         cdef size_t buf_len
   137 
   165         cdef libc.ssize_t ret
   138         # get buffer
   166 
   139         # XXX: test that except works right
   167         parse_buf(&buf_ptr, &buf_len, buf, 0)
   140         py.PyObject_AsCharBuffer(buf, &buf_ptr, &buf_len)
   168 
   141         
       
   142         # send()
   169         # send()
   143         ret = platform.send(self.fd, <void *> buf_ptr, buf_len, flags)
   170         ret = platform.send(self.fd, buf_ptr, buf_len, flags)
   144         
   171         
   145         if ret < 0 :
   172         if ret < 0 :
   146             raise_errno('send')
   173             raise_errno('send')
   147 
   174 
   148         else :
   175         else :
   149             return ret
   176             return ret
   150 
   177 
       
   178     def sendto (self, object buf, int flags = 0, sockaddr addr = None) :
       
   179         """
       
   180             Transmit a message to the given remote endpoint. If this socket is connected, the addr must not be
       
   181             specified, and this acts like send()
       
   182 
       
   183                 buf         - the data to send
       
   184                 flags       - (optional) MSG_* flags to send with
       
   185                 addr        - (optional) target address
       
   186 
       
   187             Returns the number of bytes sent, which may be less than the length of buf.
       
   188         """
       
   189 
       
   190         cdef void *buf_ptr
       
   191         cdef size_t buf_len
       
   192         cdef libc.ssize_t ret
       
   193         
       
   194         cdef platform.sockaddr *sa
       
   195         cdef platform.socklen_t sa_len
       
   196        
       
   197         parse_sockaddr(&sa, &sa_len, addr, 1)
       
   198         parse_buf(&buf_ptr, &buf_len, buf, 0)
       
   199         
       
   200         # send()
       
   201         ret = platform.sendto(self.fd, buf_ptr, buf_len, flags, sa, sa_len)
       
   202         
       
   203         if ret < 0 :
       
   204             raise_errno('sendto')
       
   205 
       
   206         else :
       
   207             return ret
       
   208     
       
   209     def sendmsg (self, sockaddr addr = None, iov = None, control = None, int flags = 0) :
       
   210         """
       
   211             Transmit an extended message to the given remote endpoint (or default for connected sockets) with the given
       
   212             extra parameters.
       
   213 
       
   214                 addr        - (optional) destination address (struct msghdr::msg_name)
       
   215                 iov         - (optional) sequence of read-buffers to transmit
       
   216                 control     - (optional) control message to transmit
       
   217                 flags       - (optional) MSG_* flags to send with
       
   218 
       
   219             Returns the number of bytes sent, which may be less than the total length of iov.
       
   220         """
       
   221         
       
   222         cdef libc.ssize_t ret
       
   223         cdef libc.iovec *iovec
       
   224         cdef platform.msghdr msg
       
   225         
       
   226         libc.memset(&msg, 0, sizeof(msg))
       
   227         
       
   228         parse_sockaddr(<platform.sockaddr **> &msg.msg_name, &msg.msg_namelen, addr, 1)
       
   229         parse_buf(&msg.msg_control, &msg.msg_controllen, control, 1)
       
   230         
       
   231         # iov
       
   232         if iov :
       
   233             iov = tuple(iov)
       
   234 
       
   235             # numerb of bufs = number of iovecs
       
   236             msg.msg_iovlen = len(iov)
       
   237                 
       
   238             # alloca the required number of iovec's
       
   239             msg.msg_iov = <libc.iovec *> libc.alloca(msg.msg_iovlen * sizeof(libc.iovec))
       
   240             
       
   241             # fill in the iovecs
       
   242             for i, buf in enumerate(iov) :
       
   243                 iovec = &msg.msg_iov[i]
       
   244 
       
   245                 parse_buf(&iovec.iov_base, &iovec.iov_len, buf, 1)
       
   246         
       
   247         # sendmsg()
       
   248         ret = platform.sendmsg(self.fd, &msg, flags)
       
   249 
       
   250         if ret < 0 :
       
   251             raise_errno('sendmsg')
       
   252 
       
   253         else :
       
   254             return ret
       
   255        
       
   256     def write (self, object buf) :
       
   257         """
       
   258             Write data to socket, mostly equivalent to send() with flags=0.
       
   259 
       
   260                 buf         - the data to send
       
   261 
       
   262             Returns the number of bytes sent, which may be less than the length of buf.
       
   263         """
       
   264         
       
   265         cdef void *buf_ptr
       
   266         cdef size_t buf_len
       
   267         cdef libc.ssize_t ret
       
   268 
       
   269         parse_buf(&buf_ptr, &buf_len, buf, 0)
       
   270 
       
   271         # send()
       
   272         ret = libc.write(self.fd, buf_ptr, buf_len)
       
   273         
       
   274         if ret < 0 :
       
   275             raise_errno('write')
       
   276 
       
   277         else :
       
   278             return ret
       
   279     
       
   280     def writev (self, iov) :
       
   281         """
       
   282             Write data to a socket from multiple read-buffers.
       
   283 
       
   284                 iov         - sequence of read-buffers to transmit
       
   285             
       
   286             Returns the number of bytes sent, which may be less than the total length of iov.
       
   287         """
       
   288 
       
   289         # iov
       
   290         cdef libc.iovec *iov_list = NULL
       
   291         cdef size_t iov_count = 0
       
   292         cdef libc.iovec *iovec
       
   293         
       
   294         iov = tuple(iov)
       
   295 
       
   296         # numerb of bufs = number of iovecs
       
   297         iov_count = len(iov)
       
   298             
       
   299         # alloca the required number of iovec's
       
   300         iov_list = <libc.iovec *> libc.alloca(iov_count * sizeof(libc.iovec))
       
   301         
       
   302         # fill in the iovecs
       
   303         for i, buf in enumerate(iov) :
       
   304             iovec = &iov_list[i]
       
   305                 
       
   306                 
       
   307             parse_buf(&iovec.iov_base, &iovec.iov_len, buf, 1)
       
   308             
       
   309         # sendmsg()
       
   310         ret = libc.writev(self.fd, iov_list, iov_count)
       
   311 
       
   312         if ret < 0 :
       
   313             raise_errno('writev')
       
   314 
       
   315         else :
       
   316             return ret
       
   317