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