qmsk/net/socket/socket.pyx
changeset 7 74fde84264b1
parent 6 10bd48c9b6ce
child 8 b3880dafbab1
equal deleted inserted replaced
6:10bd48c9b6ce 7:74fde84264b1
       
     1 from qmsk.net.socket.socket cimport *
       
     2 from qmsk.net.socket.addr cimport sockaddr, build_sockaddr
       
     3 
       
     4 cimport libc, py
       
     5 
       
     6 from py cimport raise_errno
       
     7 
       
     8 
       
     9 # XXX: do some GIL-releasin'
       
    10 cdef class sock :
       
    11 
       
    12     def __init__ (self, int fd = -1) :
       
    13         """
       
    14             Construct this socket with the given fd, or -1 to mark it as fd-less
       
    15         """
       
    16 
       
    17         self.fd = fd
       
    18 
       
    19     def socket (self, int family = libc.AF_INET, int socktype = libc.SOCK_STREAM, int protocol = 0) :
       
    20         """
       
    21             Create a new socket endpoint with the given family/domain, socktype and optionally, specific protocol.
       
    22 
       
    23                 family      - one of AF_*
       
    24                 socktype    - one of SOCK_*
       
    25                 protocol    - one of IPPROTO_* or zero to select default
       
    26         """
       
    27 
       
    28         if self.fd >= 0 :
       
    29             raise Exception("Socket fd already exists")
       
    30         
       
    31         # socket()
       
    32         self.fd = libc.socket(family, socktype, protocol)
       
    33         
       
    34         # trap
       
    35         if self.fd < 0 :
       
    36             raise_errno('socket')
       
    37 
       
    38     def bind (self, sockaddr addr) :
       
    39         """
       
    40             Bind this socket to the given local socket address. The given sockaddr should be of the same or a
       
    41             compatible address family.
       
    42 
       
    43                 addr        - the local address to bind to. The port may be zero to let the system choose an unused
       
    44                               ephemeral port.
       
    45         """
       
    46 
       
    47         cdef libc.sockaddr *sa_ptr
       
    48         cdef libc.socklen_t sa_len
       
    49 
       
    50         # get the address
       
    51         addr._get_sockaddr(&sa_ptr, &sa_len)
       
    52 
       
    53         # bind()
       
    54         if libc.bind(self.fd, sa_ptr, sa_len) :
       
    55             raise_errno('bind')
       
    56 
       
    57     def listen (self, int backlog) :
       
    58         """
       
    59             Listen for connections, marking this socket as a passive socket, which can accept incoming connection
       
    60             requests using sock.accept().
       
    61 
       
    62             It is customary to call .bind() before .listen().
       
    63 
       
    64                 backlog     - maximum number of pending connections (those not yet .accept()'d).
       
    65         """
       
    66         
       
    67         # listen()
       
    68         if libc.listen(self.fd, backlog) :
       
    69             raise_errno('listen')
       
    70 
       
    71     def connect (self, sockaddr addr) :
       
    72         """
       
    73             Initiate a connection, connecting this socket to the remote endpoint specified by `addr`. The given sockaddr
       
    74             should be of the same or a compatible address family.
       
    75 
       
    76             If the socket is in non-blocking mode, this will presumeably return errno.EINPROGRESS.
       
    77 
       
    78             If the socket has not yet been bound (using .bind()), the system will pick an appropriate local address and
       
    79             ephemeral port.
       
    80 
       
    81                 addr        - the remote address to connect to.
       
    82         """
       
    83 
       
    84         cdef libc.sockaddr *sa_ptr
       
    85         cdef libc.socklen_t sa_len
       
    86 
       
    87         # get the address
       
    88         addr._get_sockaddr(&sa_ptr, &sa_len)
       
    89         
       
    90         # connect()
       
    91         if libc.connect(self.fd, sa_ptr, sa_len) :
       
    92             raise_errno('connect')
       
    93 
       
    94     def accept (self) :
       
    95         """
       
    96             Accept a connection, dequeueing the first pending connection and returning a new sock object for it. This
       
    97             socket must be a connection-based socket (SOCK_STREAM/SOCK_SEQPACKET) and in the passive listening mode
       
    98             (.listen()).
       
    99 
       
   100             This returns a (sock, sockaddr) tuple:
       
   101                 sock        - the newly created sock, corresponding to the incoming connection
       
   102                 sockaddr    - the remote address of the incoming connection
       
   103         """
       
   104         
       
   105         # prep the sockaddr that we will return
       
   106         cdef libc.sockaddr_storage ss
       
   107         cdef libc.socklen_t ss_len = sizeof(ss)
       
   108 
       
   109         cdef socket_t sock_fd
       
   110 
       
   111         # accept()
       
   112         sock_fd = libc.accept(self.fd, <libc.sockaddr *> &ss, &ss_len)
       
   113 
       
   114         if sock_fd < 0 :
       
   115             raise_errno('accept')
       
   116 
       
   117         # prep the new socket
       
   118         sock_obj = sock(sock_fd)
       
   119 
       
   120         # prep the new addr
       
   121         sock_addr = build_sockaddr(<libc.sockaddr *> &ss, ss_len)
       
   122 
       
   123         return sock_obj, sock_addr
       
   124 
       
   125     def send (self, object buf, int flags = 0) :
       
   126         """
       
   127             Transmit a message to the connected remote endpoint.
       
   128 
       
   129                 buf     - the data to send
       
   130                 flags   - MSG_* flags to send with
       
   131 
       
   132             Returns the number of bytes sent, which may be less than the length of buf.
       
   133         """
       
   134 
       
   135         cdef char *buf_ptr
       
   136         cdef libc.ssize_t buf_len, ret
       
   137 
       
   138         # get buffer
       
   139         # XXX: test that except works right
       
   140         py.PyObject_AsCharBuffer(buf, &buf_ptr, &buf_len)
       
   141         
       
   142         # send()
       
   143         ret = libc.send(self.fd, <void *> buf_ptr, buf_len, flags)
       
   144         
       
   145         if ret < 0 :
       
   146             raise_errno('send')
       
   147 
       
   148         else :
       
   149             return ret
       
   150