OSCNet.Mod 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. (* Copyright 2005-2006, Markus Heule, ETH Zurich *)
  2. MODULE OSCNet; (** AUTHOR "heulemar"; PURPOSE "OpenSoundControl networkplugins (TCP and UDP)"; *)
  3. (*
  4. This modue contains objecttypes for the TCP and UDP implementation of the OSC protocol. For each version, a server
  5. and a client version is supplied.
  6. The server version uses an OSCService as servicebackend. Upon creation of a serverobject, it will
  7. start listening for connections or packets from the network. When it receives a packet, it parses it and upon successful
  8. parsing, it will hand it over to the corresponding OSCService. They can also return packets to the sender of another
  9. packet with the 'Return' function. The services can be stopped with the 'Stop' function.
  10. Note: The TCP version uses the TCPServices framework to support multiple concurrent TCP connections.
  11. Example of usage:
  12. VAR
  13. net: OSCNet.OSCUDPServer or OSCNet.OSCTCPServer;
  14. service: OSCService.OSCService;
  15. BEGIN
  16. ...
  17. NEW(net, service, 57110, res); (* server listens now on port 57110 and delivers packets to service *)
  18. ...
  19. net.Stop; (* stopps the networkplugin *)
  20. ...
  21. The client versions can send OSCPackets to a remote OSCServer. They can also receive replies from the remote OSC server.
  22. Example:
  23. VAR
  24. client: OSCTCPClient (or OSCUDPClient);
  25. p, newp: OSCPacket;
  26. res: WORD;
  27. BEGIN
  28. NEW(client, fip, fport, TCP.NilPort, res);
  29. ...
  30. res := client.Send(p);
  31. ...
  32. res := client.Receive(newp);
  33. *)
  34. IMPORT
  35. OSC, OSCService, IP, UDP, TCP, Network, TCPServices,
  36. Kernel, KernelLog (* Testing *), Strings;
  37. CONST
  38. Ok* = 0;
  39. Timeout* = 4401;
  40. ParseError* = 4402;
  41. PacketTooBig* = 4403;
  42. BadReturnData* = 4404;
  43. MaxUDPPacketLength* = 10000H;
  44. MaxTCPPacketLength = MaxUDPPacketLength; (* TOOD: What value? *)
  45. ReceiveTimeout* = 1000; (* ms *)
  46. NotImplemented* = 101;
  47. Trace* = FALSE;
  48. UDPHack = TRUE;
  49. TYPE
  50. (* abstract class of all network clients *)
  51. OSCClient = OBJECT
  52. PROCEDURE Send*(p: OSC.OSCPacket): WORD;
  53. BEGIN HALT(NotImplemented); END Send;
  54. PROCEDURE Receive*(VAR p: OSC.OSCPacket): WORD;
  55. BEGIN HALT(NotImplemented); END Receive;
  56. PROCEDURE Close*;
  57. BEGIN HALT(NotImplemented); END Close;
  58. END OSCClient;
  59. (* This objecttype is used to store the IP and the Port of the remote client.
  60. This information is used when a packet should be returned to a sender. (See SetReturner(..) in OSCUDPServer) *)
  61. OSCUDPData = OBJECT
  62. VAR
  63. fip*: IP.Adr;
  64. fport*: LONGINT;
  65. END OSCUDPData;
  66. (* UDP Client *)
  67. OSCUDPClient* = OBJECT(OSCClient)
  68. VAR
  69. s: UDP.Socket;
  70. fip: IP.Adr;
  71. fport: LONGINT;
  72. (* Creates a new UDPClient which sends packets to fip:fport.
  73. Supply UDP.NilPort for lport, if you don't want to specify a fixed local port for communication.
  74. In res the returnvalue of the UDP.Socket's creations is returned. If res doesn't equal to UDP.Ok, the
  75. client shouldn't be used *)
  76. PROCEDURE &InitUDP*(fip: IP.Adr; fport, lport: LONGINT; VAR res: WORD);
  77. BEGIN
  78. SELF.fip := fip;
  79. SELF.fport := fport;
  80. NEW(s, lport, res);
  81. END InitUDP;
  82. (* sends an OSCMessage or an OSCBundle to fip:fport. Returns the statuscode of UDP.Socket.Send *)
  83. PROCEDURE Send*(p: OSC.OSCPacket): WORD;
  84. BEGIN
  85. RETURN SendUDP(s, fip, fport, p);
  86. END Send;
  87. (* receives a packet from the network. Only UDP packets from our partner are considered.
  88. You can also supply a timeout in miliseconds. Use -1 for a infinite wait.
  89. Returns Ok, ParseError or an UDP returncode (eg:. UDP.Timeout) *)
  90. PROCEDURE Recieve*(VAR p: OSC.OSCPacket; timeout (* in ms *): LONGINT): WORD;
  91. VAR
  92. fip2: IP.Adr; fport2: LONGINT;
  93. size: LONGINT;
  94. buffer: Strings.String;
  95. got: LONGINT;
  96. res: WORD;
  97. endticks: LONGINT;
  98. istimeout: BOOLEAN;
  99. BEGIN
  100. IF timeout # -1 THEN
  101. (* timeout *)
  102. istimeout := TRUE;
  103. endticks := Kernel.GetTicks () + timeout;
  104. END;
  105. NEW(buffer, MaxUDPPacketLength);
  106. REPEAT
  107. IF istimeout THEN timeout := endticks - Kernel.GetTicks (); END;
  108. s.Receive(buffer^, 0, MaxUDPPacketLength, timeout, fip2, fport2, got, res);
  109. UNTIL (res # UDP.Ok) OR (IP.AdrsEqual(fip, fip2) & (fport = fport2));
  110. IF res # UDP.Ok THEN RETURN res; END;
  111. (* parse packet *)
  112. size := got;
  113. p := OSC.ParseOSCPacket(buffer^, size);
  114. IF p = NIL THEN RETURN ParseError; END;
  115. RETURN Ok;
  116. END Recieve;
  117. (* closes the clientconnection *)
  118. PROCEDURE Close*;
  119. BEGIN
  120. s.Close();
  121. END Close;
  122. END OSCUDPClient;
  123. (* UDP Server *)
  124. OSCUDPServer* = OBJECT
  125. VAR
  126. s: UDP.Socket;
  127. serror: BOOLEAN;
  128. oscservice: OSCService.OSCService;
  129. stopping: BOOLEAN; (* flag to stop the service *)
  130. (* inernal variables of 'main'-procedure *)
  131. newPacket: OSC.OSCPacket;
  132. newUDPData: OSCUDPData;
  133. buffer: OSC.String; (* ARRAY MaxUDPPacketLength OF CHAR; *)
  134. receivefip: IP.Adr; receivefport: LONGINT;
  135. got: LONGINT;
  136. res: WORD;
  137. (* Sets the signal to stop the service *)
  138. PROCEDURE Stop*;
  139. BEGIN { EXCLUSIVE }
  140. stopping := TRUE;
  141. END Stop;
  142. (* Creates a new UDPServer listening on UDP port lport.. Sends received packets to service.
  143. If res is not UDP.Ok, then the server will immediately quit *)
  144. PROCEDURE &InitUDPServer*(service: OSCService.OSCService; lport: LONGINT; VAR res: WORD);
  145. BEGIN
  146. ASSERT(service # NIL);
  147. oscservice := service;
  148. NEW(buffer, MaxUDPPacketLength);
  149. NEW(s, lport, res);
  150. IF(res # UDP.Ok) THEN serror := TRUE; ELSE serror := FALSE; END;
  151. stopping := FALSE;
  152. END InitUDPServer;
  153. (* Returns an OSCMessage or an OSCBundle to the sender specified by data, which is indeed an instance of OSCUDPData *)
  154. PROCEDURE return(p: OSC.OSCPacket; data: OBJECT): WORD;
  155. BEGIN
  156. IF data IS OSCUDPData THEN
  157. WITH data: OSCUDPData DO
  158. IF Trace THEN KernelLog.String('UDPServer.Return called'); KernelLog.Ln;
  159. IP.OutAdr(data.fip); KernelLog.String(' Port: '); KernelLog.Int(data.fport, 10);
  160. KernelLog.Ln; END;
  161. RETURN SendUDP(s, data.fip, data.fport, p);
  162. END;
  163. ELSE
  164. IF Trace THEN KernelLog.String('UDPServer.Return: BadReturnData received'); KernelLog.Ln; END;
  165. RETURN BadReturnData;
  166. END;
  167. END return;
  168. BEGIN { ACTIVE }
  169. IF (~serror) THEN
  170. REPEAT
  171. (* receive packets and parse them *)
  172. s.Receive(buffer^, 0, MaxUDPPacketLength, ReceiveTimeout, receivefip, receivefport, got, res);
  173. IF res = UDP.Ok THEN
  174. newPacket := OSC.ParseOSCPacket(buffer^, got);
  175. IF newPacket # NIL THEN
  176. NEW(newUDPData);
  177. IF Trace THEN
  178. KernelLog.String('OSCUDPServer: Received Packet from: '); KernelLog.Hex(receivefip.ipv4Adr, 10);
  179. KernelLog.Hex(receivefip.usedProtocol, 10);
  180. KernelLog.Hex(receivefip.data, 10);
  181. KernelLog.String(' port: '); KernelLog.Int(receivefport, 10); KernelLog.Ln;
  182. END;
  183. IF UDPHack THEN
  184. newUDPData.fip := IP.StrToAdr('192.168.150.1');
  185. ELSE
  186. newUDPData.fip := receivefip;
  187. END;
  188. newUDPData.fport := receivefport;
  189. newPacket.SetReturner(return, newUDPData);
  190. oscservice.NewPacket(newPacket);
  191. END;
  192. ELSIF res # UDP.Timeout THEN
  193. (* closing service *)
  194. BEGIN { EXCLUSIVE }
  195. stopping := TRUE;
  196. END;
  197. END;
  198. UNTIL stopping;
  199. (* cleanup *)
  200. s.Close();
  201. END;
  202. END OSCUDPServer;
  203. (* TCP Client *)
  204. OSCTCPClient* = OBJECT(OSCClient)
  205. VAR
  206. connection: TCP.Connection;
  207. (* creates a new OSCTCPClient and connects to fip:fport. The user can also specify a local port to use for the outgoing
  208. connection. If TCP.NilPort is used, the operating system assigns a free local port number. If res doesn't euqal to
  209. TCP.Ok then this client shouldn't be used. *)
  210. PROCEDURE &InitTCP*(fip: IP.Adr; fport, lport: LONGINT; VAR res: WORD);
  211. BEGIN
  212. NEW(connection);
  213. connection.Open(lport, fip, fport, res);
  214. END InitTCP;
  215. PROCEDURE Close*;
  216. BEGIN
  217. connection.Close;
  218. END Close;
  219. (* sends a packet to the connected OSCServer. Returns TCP.Ok if sent successfully, otherwise an TCP.* errorcode is
  220. returned. *)
  221. PROCEDURE Send*(p: OSC.OSCPacket): WORD;
  222. BEGIN
  223. RETURN SendTCP(connection, p);
  224. END Send;
  225. (* receives a packet from the OSC Server. *)
  226. PROCEDURE Receive*(VAR p: OSC.OSCPacket): WORD;
  227. BEGIN
  228. RETURN ReceiveTCP(connection, p);
  229. END Receive;
  230. END OSCTCPClient;
  231. (* An OSCTCPServer will create for each new connection an OSCTCPAgent object. This object handles all the communication
  232. with the connected client. It also responsible to return messages to the sender of an OSCPacket.
  233. Note: The registred return-handler also includes the current SELF-pointer. Therfore, a call to returner(...) in
  234. OSC.OSCPacket will always be delivered to the right agent object *)
  235. OSCTCPAgent = OBJECT(TCPServices.Agent);
  236. VAR
  237. oscservice: OSCService.OSCService;
  238. newpacket: OSC.OSCPacket;
  239. res: WORD;
  240. PROCEDURE &StartOSCAgent*(oscs: OSCService.OSCService; c: TCP.Connection; s: TCPServices.Service);
  241. BEGIN
  242. ASSERT(oscs # NIL);
  243. oscservice := oscs;
  244. Start(c,s);
  245. END StartOSCAgent;
  246. (* returns a packet to the current client. data is ignored *)
  247. PROCEDURE return*(p: OSC.OSCPacket; data: OBJECT): WORD;
  248. BEGIN
  249. IF Trace THEN KernelLog.String('TCPServer.Return called IP: ');
  250. IP.OutAdr(client.fip); KernelLog.String(' Port: '); KernelLog.Int(client.fport, 10);
  251. KernelLog.Ln; END;
  252. RETURN SendTCP(client, p);
  253. END return;
  254. BEGIN { ACTIVE }
  255. LOOP
  256. res := ReceiveTCP(client, newpacket);
  257. IF res = Ok THEN
  258. ASSERT(newpacket # NIL);
  259. newpacket.SetReturner(return, NIL);
  260. oscservice.NewPacket(newpacket);
  261. ELSIF res # ParseError THEN EXIT END; (* Closing Connection on unrecoverableerror *)
  262. END;
  263. Terminate;
  264. END OSCTCPAgent;
  265. (* TCP Server *)
  266. OSCTCPServer* = OBJECT
  267. VAR
  268. tcpservice: TCPServices.Service;
  269. service: OSCService.OSCService;
  270. (* starts the server: registers the OSCService s and creates the TCPServices.Service, which listens for connections *)
  271. PROCEDURE &InitTCPServer*(s: OSCService.OSCService; lport: LONGINT; VAR res: WORD);
  272. BEGIN
  273. ASSERT(s # NIL);
  274. service := s;
  275. NEW(tcpservice, lport, newAgent, res);
  276. END InitTCPServer;
  277. (* This function is called by tcpservice to create a new agent *)
  278. PROCEDURE newAgent(c: TCP.Connection; s: TCPServices.Service): TCPServices.Agent;
  279. VAR agent: OSCTCPAgent;
  280. BEGIN
  281. NEW(agent, service, c, s);
  282. RETURN agent;
  283. END newAgent;
  284. (* Stops the OSCTCPServer. Closes the listening socket and all established connections *)
  285. PROCEDURE Stop*;
  286. BEGIN
  287. tcpservice.Stop;
  288. END Stop;
  289. END OSCTCPServer;
  290. PROCEDURE SendTCP(client: TCP.Connection; p: OSC.OSCPacket): WORD;
  291. VAR
  292. buffer: OSC.String;
  293. size: ARRAY 4 OF CHAR;
  294. res: WORD;
  295. BEGIN
  296. ASSERT(p # NIL);
  297. buffer := p.GetBytes();
  298. ASSERT(buffer # NIL);
  299. (* TCP: <size || packet> *)
  300. Network.PutNet4(size, 0, p.GetSize());
  301. client.Send(size, 0, 4, FALSE, res);
  302. IF(res # TCP.Ok) THEN RETURN res; END;
  303. client.Send(buffer^, 0, LEN(buffer^), FALSE, res);
  304. RETURN res;
  305. END SendTCP;
  306. PROCEDURE ReceiveTCP(client: TCP.Connection; VAR p: OSC.OSCPacket): WORD;
  307. VAR
  308. res: WORD; len: LONGINT;
  309. buffer: POINTER TO ARRAY OF CHAR;
  310. sizebuf: ARRAY 4 OF CHAR;
  311. packetsize: LONGINT;
  312. BEGIN
  313. client.Receive(sizebuf, 0, LEN(sizebuf), 4, len, res);
  314. IF res # TCP.Ok THEN RETURN res END;
  315. ASSERT(len = 4);
  316. packetsize := Network.GetNet4(sizebuf, 0);
  317. (* allocate new buffer *)
  318. IF (packetsize < 0) OR (packetsize > MaxTCPPacketLength) THEN
  319. IF Trace THEN KernelLog.String('OSCTCPAgent: Packet too big: '); KernelLog.Hex(packetsize, 10); KernelLog.Ln; END;
  320. RETURN PacketTooBig;
  321. END;
  322. NEW(buffer, packetsize);
  323. client.Receive(buffer^, 0, packetsize, packetsize, len, res);
  324. IF res # TCP.Ok THEN RETURN res; END;
  325. ASSERT(len = packetsize);
  326. p := OSC.ParseOSCPacket(buffer^, packetsize);
  327. IF p = NIL THEN RETURN ParseError; END;
  328. RETURN Ok;
  329. END ReceiveTCP;
  330. PROCEDURE SendUDP(s: UDP.Socket; fip: IP.Adr; fport: LONGINT; p: OSC.OSCPacket): WORD;
  331. VAR
  332. buffer: OSC.String;
  333. res: WORD;
  334. BEGIN
  335. ASSERT(p # NIL);
  336. buffer := p.GetBytes();
  337. ASSERT(buffer # NIL);
  338. s.Send(fip, fport, buffer^, 0, LEN(buffer^), res);
  339. IF Trace THEN KernelLog.String('SendUDP: buffer: '); KernelLog.Buffer(buffer^, 0, LEN(buffer^)); KernelLog.String( ' fip '); IP.OutAdr(fip);
  340. KernelLog.String(' fport: '); KernelLog.Int(fport, 10); KernelLog.Ln; END;
  341. RETURN res;
  342. END SendUDP;
  343. (*
  344. PROCEDURE RecieveUDP(s: UDP.Socket; timeout (* in ms *): LONGINT;
  345. VAR fip: IP.Adr; VAR fport: LONGINT; VAR p: OSC.OSCPacket): LONGINT;
  346. VAR
  347. fip2: IP.Adr; fport2: LONGINT;
  348. size: LONGINT;
  349. buffer: Strings.String;
  350. got, res: LONGINT;
  351. BEGIN
  352. NEW(buffer, MaxUDPPacketLength);
  353. ASSERT(buffer # NIL);
  354. (* if fip = NILAdr, fport = 0 then recive from all, otherwise only from this port *)
  355. REPEAT
  356. s.Receive(buffer^, 0, MaxUDPPacketLength, timeout, gotfip, gotfport, res);
  357. UNTIL res # Ok
  358. (* Should we only receive from fip - if fip # NILAdr !!!! ???? *)
  359. s.Receive(buffer^, 0, MaxUDPPacketLength, timeout, fip2, fport2, got, res);
  360. IF res # UDP.Ok THEN RETURN res; END;
  361. size := got;
  362. (* parse packet *)
  363. p := OSC.ParseOSCPacket(buffer^, size);
  364. IF p = NIL THEN RETURN ParseError; END;
  365. RETURN Ok;
  366. END Recieve;
  367. *)
  368. (* Testprocedures *)
  369. PROCEDURE TestUDPSend*;
  370. VAR
  371. socket: OSCUDPClient;
  372. p, p2: OSC.OSCMessage;
  373. attri: OSC.OSCParamInteger;
  374. attrs: OSC.OSCParamString;
  375. b: OSC.OSCBundle;
  376. tt: OSC.OSCTimeTag;
  377. ip: IP.Adr;
  378. res: WORD;
  379. BEGIN
  380. ip := IP.StrToAdr('192.168.150.1'); KernelLog.Int(res, 4);
  381. NEW(socket, ip, 57110, 57110, res); KernelLog.Int(res, 4);
  382. NEW(p, Strings.NewString('/abc/def/ghi'));
  383. NEW(attri, 01234H); p.AddArgument(attri);
  384. res := socket.Send(p);
  385. KernelLog.Int(res, 4); KernelLog.Ln;
  386. NEW(p2, Strings.NewString('/xyz'));
  387. NEW(attrs, Strings.NewString('<== This is a stirng in a Message ==>'));
  388. p2.AddArgument(attrs);
  389. NEW(tt); tt.SetLow(2005,12,26,18,12,15,999);
  390. NEW(b, tt, NIL, 0); b.AddPacket(p); b.AddPacket(p2);
  391. res := socket.Send(b);
  392. socket.Close;
  393. KernelLog.String('TestUDPSend done'); KernelLog.Ln;
  394. END TestUDPSend;
  395. PROCEDURE TestTCPSend*;
  396. VAR
  397. c: OSCTCPClient;
  398. p, p2: OSC.OSCMessage;
  399. attri: OSC.OSCParamInteger;
  400. attrs: OSC.OSCParamString;
  401. b: OSC.OSCBundle;
  402. tt: OSC.OSCTimeTag;
  403. ip: IP.Adr;
  404. res: WORD;
  405. BEGIN
  406. ip := IP.StrToAdr('192.168.150.1'); KernelLog.Int(res, 4);
  407. NEW(c, ip, 2009, TCP.NilPort, res); KernelLog.Int(res, 4);
  408. NEW(p, Strings.NewString('/abc/def/ghi'));
  409. NEW(attri, 01234H); p.AddArgument(attri);
  410. res := c.Send(p);
  411. KernelLog.Int(res, 4); KernelLog.Ln;
  412. NEW(p2, Strings.NewString('/xyz'));
  413. NEW(attrs, Strings.NewString('<== This is a stirng in a Message ==>'));
  414. p2.AddArgument(attrs);
  415. NEW(tt); tt.SetLow(2005,12,26,18,12,15,999);
  416. NEW(b, tt, NIL, 0); b.AddPacket(p); b.AddPacket(p2);
  417. res := c.Send(b);
  418. KernelLog.String('TestTCPSend done'); KernelLog.Ln;
  419. c.Close;
  420. END TestTCPSend;
  421. END OSCNet.
  422. OSCNet.TestUDPSend ~
  423. OSCNet.TestTCPSend ~
  424. OSCNet.TestUDPReceive ~
  425. *)