Skip to content

Commit d2f4d99

Browse files
committed
Added support for native UDP packets
1 parent 6b258e3 commit d2f4d99

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed

bridge/bridge.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ def process(self, data):
7979
import sockets
8080
sockets.init(cp)
8181

82+
import sockets_udp
83+
sockets_udp.init(cp)
84+
8285
pr = packet.PacketReader(cp)
8386
start_time = time.time()
8487
with cbreak():

bridge/sockets_udp.py

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
## This file is part of YunBridge.
2+
##
3+
## Copyright 2015 Arduino LLC (http://www.arduino.cc/)
4+
##
5+
## YunBridge is free software; you can redistribute it and/or modify
6+
## it under the terms of the GNU General Public License as published by
7+
## the Free Software Foundation; either version 2 of the License, or
8+
## (at your option) any later version.
9+
##
10+
## This program is distributed in the hope that it will be useful,
11+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
## GNU General Public License for more details.
14+
##
15+
## You should have received a copy of the GNU General Public License
16+
## along with this program; if not, write to the Free Software
17+
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
##
19+
## As a special exception, you may use this file as part of a free software
20+
## library without restriction. Specifically, if other files instantiate
21+
## templates or use macros or inline functions from this file, or you compile
22+
## this file and link it with other files to produce an executable, this
23+
## file does not by itself cause the resulting executable to be covered by
24+
## the GNU General Public License. This exception does not however
25+
## invalidate any other reasons why the executable file might be covered by
26+
## the GNU General Public License.
27+
28+
from socket import AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_REUSEADDR# SO_ERROR
29+
from socket import gethostname
30+
from select import select
31+
import utils, socket
32+
33+
34+
class UDPSocket:
35+
def __init__(self, address, port):
36+
self.txbuff = [ ]
37+
self.txmeta = [ ]
38+
self.rxbuff = [ ]
39+
self.rxmeta = [ ]
40+
self.curr_rxbuff = None
41+
self.curr_rxmeta = None
42+
self.curr_txbuff = None
43+
self.curr_txmeta = None
44+
self.sock = socket.socket(AF_INET, SOCK_DGRAM)
45+
self.sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
46+
self.sock.setblocking(0)
47+
self.opened = False
48+
try:
49+
self.sock.bind((address, port))
50+
self.opened = True
51+
except socket.error, e:
52+
pass
53+
54+
def run(self):
55+
rd, wr, err = select([self.sock], [self.sock], [self.sock], 0)
56+
57+
if len(err) > 0:
58+
self.close()
59+
return
60+
61+
# receive data from socket
62+
if len(rd) > 0:
63+
try:
64+
data, client = self.sock.recvfrom(1024)
65+
except:
66+
self.close()
67+
return
68+
self.rxbuff.append(data)
69+
self.rxmeta.append(client)
70+
71+
# send data to socket
72+
if len(wr) > 0:
73+
if len(self.txbuff) > 0:
74+
try:
75+
self.sock.sendto(self.txbuff.pop(0), self.txmeta.pop(0))
76+
except socket.error, e:
77+
pass
78+
79+
def recv_next(self):
80+
if len(self.rxbuff) == 0:
81+
return None
82+
self.curr_rxbuff = self.rxbuff.pop(0)
83+
self.curr_rxmeta = self.rxmeta.pop(0)
84+
return len(self.curr_rxbuff)
85+
86+
def recv_address(self):
87+
if self.curr_rxbuff is None:
88+
return [None, None]
89+
if len(self.curr_rxbuff) == 0:
90+
return [None, None]
91+
return self.curr_rxmeta
92+
93+
def recv(self, maxlen):
94+
if self.curr_rxbuff is None:
95+
return None
96+
if len(self.curr_rxbuff) > maxlen:
97+
res = self.curr_rxbuff[:maxlen]
98+
self.curr_rxbuff = self.curr_rxbuff[maxlen:]
99+
else:
100+
res = self.curr_rxbuff
101+
self.curr_rxbuff = ''
102+
return res
103+
104+
def available(self):
105+
if self.curr_rxbuff is None:
106+
return None
107+
return len(self.curr_rxbuff)
108+
109+
def send_start(self, address, port):
110+
self.curr_txbuff = ''
111+
self.curr_txmeta = (address, port)
112+
return True
113+
114+
def send(self, data):
115+
if self.curr_txbuff is None:
116+
return None
117+
self.curr_txbuff += data
118+
return True
119+
120+
def send_end(self):
121+
if self.curr_txbuff is None:
122+
return None
123+
self.txbuff.append(self.curr_txbuff)
124+
self.txmeta.append(self.curr_txmeta)
125+
self.curr_txbuff = None
126+
self.curr_txmeta = None
127+
return True
128+
129+
def close(self):
130+
self.sock.close()
131+
self.opened = False
132+
return True
133+
134+
def is_opened(self):
135+
return self.opened
136+
137+
class UDPSocketsManager:
138+
def __init__(self):
139+
self.sockets = { }
140+
self.next_id = 0
141+
142+
def run(self):
143+
for id in self.sockets:
144+
self.sockets[id].run()
145+
146+
def create(self, address, port):
147+
# Determine the next id to assign to socket
148+
socket = UDPSocket(address, port)
149+
while self.next_id in self.sockets:
150+
self.next_id = (self.next_id + 1) % 256
151+
self.sockets[self.next_id] = socket
152+
return self.next_id
153+
154+
def get(self, id):
155+
if not id in self.sockets:
156+
return None
157+
return self.sockets[id]
158+
159+
def close(self, id):
160+
if not id in self.sockets:
161+
return None
162+
self.sockets[id].close()
163+
del self.sockets[id]
164+
return True
165+
166+
udp_sockets = UDPSocketsManager()
167+
168+
class CREATE_Command:
169+
def run(self, data):
170+
port = (ord(data[0]) << 8) + ord(data[1])
171+
id = udp_sockets.create(data[2:], port)
172+
if id is None:
173+
return chr(1) + chr(0)
174+
else:
175+
return chr(0) + chr(id)
176+
177+
class CLOSE_Command:
178+
def run(self, data):
179+
id = ord(data[0])
180+
udp_sockets.close(id)
181+
return ''
182+
183+
class WRITE_BEGIN_Command:
184+
def run(self, data):
185+
id = ord(data[0])
186+
sock = udp_sockets.get(id)
187+
if sock is None:
188+
return chr(0)
189+
port = (ord(data[1]) << 8) + ord(data[2])
190+
if sock.send_start(data[3:], port) is None:
191+
return chr(0)
192+
else:
193+
return chr(1)
194+
195+
class WRITE_Command:
196+
def run(self, data):
197+
id = ord(data[0])
198+
sock = udp_sockets.get(id)
199+
if sock is None:
200+
return chr(0)
201+
if sock.send(data[1:]) is None:
202+
return chr(0)
203+
else:
204+
return chr(1)
205+
206+
class WRITE_END_Command:
207+
def run(self, data):
208+
id = ord(data[0])
209+
sock = udp_sockets.get(id)
210+
if sock is None:
211+
return chr(0)
212+
if sock.send_end() is None:
213+
return chr(0)
214+
else:
215+
return chr(1)
216+
217+
class CLOSE_Command:
218+
def run(self, data):
219+
id = ord(data[0])
220+
server.close(id)
221+
return ''
222+
223+
class RECV_BEGIN_Command:
224+
def run(self, data):
225+
id = ord(data[0])
226+
sock = udp_sockets.get(id)
227+
if sock is None:
228+
return chr(0) + chr(0) + chr(0)
229+
l = sock.recv_next()
230+
if l is None:
231+
return chr(0) + chr(0) + chr(0)
232+
else:
233+
return chr(1) + chr((l >> 8) & 0xFF) + chr(l & 0xFF)
234+
235+
class RECV_Command:
236+
def run(self, data):
237+
id = ord(data[0])
238+
sock = udp_sockets.get(id)
239+
if sock is None:
240+
return chr(0)
241+
maxlen = ord(data[1])
242+
res = sock.recv(maxlen)
243+
if res is None:
244+
return ''
245+
else:
246+
return res
247+
248+
class AVAILABLE_Command:
249+
def run(self, data):
250+
id = ord(data[0])
251+
sock = udp_sockets.get(id)
252+
if sock is None:
253+
return chr(0)
254+
l = sock.available()
255+
if l is None:
256+
return chr(0) + chr(0) + chr(0)
257+
else:
258+
return chr(1) + chr((l >> 8) & 0xFF) + chr(l & 0xFF)
259+
260+
class REMOTE_IP_Command:
261+
def run(self, data):
262+
id = ord(data[0])
263+
sock = udp_sockets.get(id)
264+
if sock is None:
265+
return ''
266+
addr, port = sock.recv_address()
267+
if addr is None:
268+
return chr(0)
269+
addr = map(chr, map(int, addr.split('.')))
270+
return chr(1) + addr[0] + addr[1] + addr[2] + addr[3] + chr((port >> 8) & 0xff) + chr(port & 0xFF)
271+
272+
def init(command_processor):
273+
command_processor.register('e', CREATE_Command())
274+
command_processor.register('E', WRITE_BEGIN_Command())
275+
command_processor.register('h', WRITE_Command())
276+
command_processor.register('H', WRITE_END_Command())
277+
command_processor.register('q', CLOSE_Command())
278+
command_processor.register('Q', RECV_BEGIN_Command())
279+
command_processor.register('u', RECV_Command())
280+
command_processor.register('U', AVAILABLE_Command())
281+
command_processor.register('T', REMOTE_IP_Command())
282+
command_processor.register_runner(udp_sockets)
283+
284+
def test():
285+
from time import sleep
286+
import struct
287+
udp = UDPSocket('', 5555)
288+
while True:
289+
udp.run()
290+
sleep(0.1)
291+
r = udp.recv_next()
292+
if r != None:
293+
addr, port = udp.recv_address()
294+
print "RECEIVED " + str(r) + " " + addr + ":" + str(port)
295+
data = udp.recv(4)
296+
data += udp.recv(1024)
297+
udp.send_start(addr, port)
298+
udp.send(data)
299+
udp.send_end()
300+
301+
if __name__ == '__main__':
302+
test()
303+

0 commit comments

Comments
 (0)