6
|
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 |
|