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