EnetUdpChannels.Mod 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. MODULE EnetUdpChannels;
  2. (*
  3. AUTHOR: Timothée Martiel
  4. PURPOSE: Channels over UDP that ensure communication with a single remote host.
  5. *)
  6. IMPORT Trace, EnetBase, EnetInterfaces, EnetUdp, EnetStreams;
  7. CONST
  8. ErrUnboundChannel * = 100;
  9. TYPE
  10. (**
  11. Channel descriptor.
  12. A channel is based on a socket. It uses only those packets from the socket that come from the channel's remote port.
  13. *)
  14. Channel * = POINTER TO ChannelDesc;
  15. ChannelDesc * = RECORD
  16. socket: EnetUdp.Socket; (** Underlying socket *)
  17. remoteIp *: EnetBase.IpAddr; (** IP address of the remote host *)
  18. remotePort *: EnetBase.Int; (** UDP port of the remote host *)
  19. channelHandler: RecvDatagramHandler; (** Channel receiver callback *)
  20. channelHandlerParam *: ANY; (** Channel receiver callback parameter *)
  21. next: Channel; (** Next channel based on the same socket *)
  22. END;
  23. (** Channel datagram receiver. The datagram is guaranteed to come from the channel's remote IP and remote port. *)
  24. RecvDatagramHandler * = PROCEDURE {DELEGATE} (channel: Channel; VAR data: ARRAY OF CHAR; dataOffs, dataLen: EnetBase.Int; packet: EnetBase.Packet);
  25. VAR
  26. handler: EnetBase.TaskHandler;
  27. (**
  28. Opens a new channel on 'socket'. The socket cannot be used in another way than with channels.
  29. If remoteIP is not a valid IP address, the first datagram received on the socket will bind the channel to its sender.
  30. *)
  31. PROCEDURE NewChannel * (VAR channel: Channel; socket: EnetUdp.Socket; remoteIp: EnetBase.IpAddr; remotePort: EnetBase.Int; VAR result: EnetBase.Int): BOOLEAN;
  32. VAR
  33. ch: ANY;
  34. BEGIN
  35. NEW(channel);
  36. channel.socket := socket;
  37. ch := socket.recvHandlerParam;
  38. IF ch = NIL THEN
  39. socket.recvHandlerParam := channel
  40. ELSE
  41. WITH ch: Channel DO
  42. WHILE ch.next # NIL DO ch := ch.next END;
  43. ch.next := channel
  44. END
  45. END;
  46. IF ~EnetBase.IsValidIpAddr(remoteIp) & (remotePort < 0) THEN RETURN FALSE END;
  47. channel.remoteIp := remoteIp;
  48. channel.remotePort := remotePort;
  49. IF ~EnetUdp.SetRecvHandler(socket, HandlePacket, result) THEN RETURN FALSE END;
  50. RETURN TRUE
  51. END NewChannel;
  52. (** Changes the receiver callback of a channel *)
  53. PROCEDURE SetReceiveHandler * (channel: Channel; handler: RecvDatagramHandler);
  54. BEGIN
  55. channel.channelHandler := handler
  56. END SetReceiveHandler;
  57. (** Send the [data[ofs], data[ofs + len]) on the channel. Send can only succeed on a bound channel. *)
  58. PROCEDURE Send * (channel: Channel; CONST data: ARRAY OF CHAR; ofs, len: EnetBase.Int; flags: SET; completionHandler: EnetBase.TaskHandler; VAR res: EnetBase.Int): BOOLEAN;
  59. BEGIN
  60. IF ~EnetBase.IsValidIpAddr(channel.remoteIp) OR (channel.remotePort < 0) THEN res := ErrUnboundChannel; RETURN FALSE END;
  61. RETURN EnetUdp.SendTo(channel.socket, channel.remoteIp, channel.remotePort, data, ofs, len, flags, completionHandler, res)
  62. END Send;
  63. (** Opens a Enet reader on the channel, with the specified buffer size *)
  64. PROCEDURE InitReader * (reader: EnetStreams.Reader; bufferSize: EnetBase.Int; channel: Channel);
  65. BEGIN
  66. ASSERT(reader # NIL);
  67. EnetStreams.InitReader(reader^, bufferSize, channel);
  68. channel.channelHandlerParam := reader;
  69. SetReceiveHandler(channel, StreamReceiveHandler)
  70. END InitReader;
  71. (** Opens an Enet writer on a channel, with the given send flags and the given buffer size *)
  72. PROCEDURE InitWriter * (writer: EnetStreams.Writer; bufferSize: EnetBase.Int; flags: SET; channel: Channel);
  73. BEGIN
  74. ASSERT(writer # NIL);
  75. EnetStreams.InitWriter(writer^, bufferSize, channel, flags, SendFromStreamWriter)
  76. END InitWriter;
  77. PROCEDURE HandlePacket (socket: EnetUdp.Socket; CONST srcAddr: EnetBase.IpAddr; srcPort: EnetBase.Int; VAR data: ARRAY OF CHAR; dataOffs, dataLen: EnetBase.Int; packet: EnetBase.Packet);
  78. VAR
  79. channel: Channel;
  80. res: EnetBase.Int;
  81. BEGIN
  82. channel := socket.recvHandlerParam(Channel);
  83. WHILE (channel # NIL) &
  84. ((((channel.remoteIp # srcAddr) & EnetBase.IsValidIpAddr(channel.remoteIp))
  85. OR ((channel.remotePort # srcPort) & (channel.remotePort >= 0)))) DO
  86. channel := channel.next
  87. END;
  88. IF channel # NIL THEN
  89. IF ~EnetBase.IsValidIpAddr(channel.remoteIp) THEN
  90. (* Channel is not bound yet *)
  91. ASSERT(channel.remotePort = srcPort);
  92. channel.remoteIp := srcAddr;
  93. IF handler = NIL THEN
  94. NEW(handler)
  95. END;
  96. handler.param := NIL;
  97. handler.handle := BlockingCompletionHandler;
  98. ASSERT(EnetUdp.SetDestination(channel.socket, srcAddr, srcPort, handler, res));
  99. IF res = EnetBase.OpInProgress THEN
  100. WHILE EnetInterfaces.UpdateAll(res) & (handler.param = NIL) DO END
  101. END;
  102. ASSERT(res = 0)
  103. END;
  104. IF channel.remotePort < 0 THEN
  105. ASSERT(channel.remoteIp = srcAddr);
  106. channel.remotePort := srcPort
  107. END;
  108. IF channel.channelHandler # NIL THEN channel.channelHandler(channel, data, dataOffs, dataLen, packet) END
  109. END
  110. END HandlePacket;
  111. PROCEDURE StreamReceiveHandler (channel: Channel; VAR data: ARRAY OF CHAR; dataOffs, dataLen: EnetBase.Int; packet: EnetBase.Packet);
  112. BEGIN
  113. packet.ownedByUser := TRUE;
  114. ASSERT(EnetBase.PacketFifoPut(channel.channelHandlerParam(EnetStreams.Reader).enetPackets, packet))
  115. END StreamReceiveHandler;
  116. PROCEDURE SendFromStreamWriter (access: ANY; CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; flags: SET; VAR res: LONGINT);
  117. VAR
  118. ignore: BOOLEAN;
  119. BEGIN
  120. WITH access: Channel DO
  121. ignore := Send(access, buf, ofs, len, flags, NIL, res)
  122. END
  123. END SendFromStreamWriter;
  124. PROCEDURE BlockingCompletionHandler (handler: EnetBase.TaskHandler);
  125. BEGIN
  126. handler.param := handler
  127. END BlockingCompletionHandler;
  128. END EnetUdpChannels.