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