123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- MODULE EnetUdpChannels;
- (*
- AUTHOR: Timothée Martiel
- PURPOSE: Channels over UDP that ensure communication with a single remote host.
- *)
- IMPORT Trace, EnetBase, EnetInterfaces, EnetUdp, EnetStreams;
- CONST
- ErrUnboundChannel * = 100;
- TYPE
- (**
- Channel descriptor.
- A channel is based on a socket. It uses only those packets from the socket that come from the channel's remote port.
- *)
- Channel * = POINTER TO ChannelDesc;
- ChannelDesc * = RECORD
- socket: EnetUdp.Socket; (** Underlying socket *)
- remoteIp *: EnetBase.IpAddr; (** IP address of the remote host *)
- remotePort *: EnetBase.Int; (** UDP port of the remote host *)
- channelHandler: RecvDatagramHandler; (** Channel receiver callback *)
- channelHandlerParam *: ANY; (** Channel receiver callback parameter *)
- next: Channel; (** Next channel based on the same socket *)
- END;
- (** Channel datagram receiver. The datagram is guaranteed to come from the channel's remote IP and remote port. *)
- RecvDatagramHandler * = PROCEDURE {DELEGATE} (channel: Channel; VAR data: ARRAY OF CHAR; dataOffs, dataLen: EnetBase.Int; packet: EnetBase.Packet);
- VAR
- handler: EnetBase.TaskHandler;
- (**
- Opens a new channel on 'socket'. The socket cannot be used in another way than with channels.
- If remoteIP is not a valid IP address, the first datagram received on the socket will bind the channel to its sender.
- *)
- PROCEDURE NewChannel * (VAR channel: Channel; socket: EnetUdp.Socket; remoteIp: EnetBase.IpAddr; remotePort: EnetBase.Int; VAR result: EnetBase.Int): BOOLEAN;
- VAR
- ch: ANY;
- BEGIN
- NEW(channel);
- channel.socket := socket;
- ch := socket.recvHandlerParam;
- IF ch = NIL THEN
- socket.recvHandlerParam := channel
- ELSE
- WITH ch: Channel DO
- WHILE ch.next # NIL DO ch := ch.next END;
- ch.next := channel
- END
- END;
- IF ~EnetBase.IsValidIpAddr(remoteIp) & (remotePort < 0) THEN RETURN FALSE END;
- channel.remoteIp := remoteIp;
- channel.remotePort := remotePort;
- IF ~EnetUdp.SetRecvHandler(socket, HandlePacket, result) THEN RETURN FALSE END;
- RETURN TRUE
- END NewChannel;
- (** Changes the receiver callback of a channel *)
- PROCEDURE SetReceiveHandler * (channel: Channel; handler: RecvDatagramHandler);
- BEGIN
- channel.channelHandler := handler
- END SetReceiveHandler;
- (** Send the [data[ofs], data[ofs + len]) on the channel. Send can only succeed on a bound channel. *)
- PROCEDURE Send * (channel: Channel; CONST data: ARRAY OF CHAR; ofs, len: EnetBase.Int; flags: SET; completionHandler: EnetBase.TaskHandler; VAR res: EnetBase.Int): BOOLEAN;
- BEGIN
- IF ~EnetBase.IsValidIpAddr(channel.remoteIp) OR (channel.remotePort < 0) THEN res := ErrUnboundChannel; RETURN FALSE END;
- RETURN EnetUdp.SendTo(channel.socket, channel.remoteIp, channel.remotePort, data, ofs, len, flags, completionHandler, res)
- END Send;
- (** Opens a Enet reader on the channel, with the specified buffer size *)
- PROCEDURE InitReader * (reader: EnetStreams.Reader; bufferSize: EnetBase.Int; channel: Channel);
- BEGIN
- ASSERT(reader # NIL);
- EnetStreams.InitReader(reader^, bufferSize, channel);
- channel.channelHandlerParam := reader;
- SetReceiveHandler(channel, StreamReceiveHandler)
- END InitReader;
- (** Opens an Enet writer on a channel, with the given send flags and the given buffer size *)
- PROCEDURE InitWriter * (writer: EnetStreams.Writer; bufferSize: EnetBase.Int; flags: SET; channel: Channel);
- BEGIN
- ASSERT(writer # NIL);
- EnetStreams.InitWriter(writer^, bufferSize, channel, flags, SendFromStreamWriter)
- END InitWriter;
- PROCEDURE HandlePacket (socket: EnetUdp.Socket; CONST srcAddr: EnetBase.IpAddr; srcPort: EnetBase.Int; VAR data: ARRAY OF CHAR; dataOffs, dataLen: EnetBase.Int; packet: EnetBase.Packet);
- VAR
- channel: Channel;
- res: EnetBase.Int;
- BEGIN
- channel := socket.recvHandlerParam(Channel);
- WHILE (channel # NIL) &
- ((((channel.remoteIp # srcAddr) & EnetBase.IsValidIpAddr(channel.remoteIp))
- OR ((channel.remotePort # srcPort) & (channel.remotePort >= 0)))) DO
- channel := channel.next
- END;
- IF channel # NIL THEN
- IF ~EnetBase.IsValidIpAddr(channel.remoteIp) THEN
- (* Channel is not bound yet *)
- ASSERT(channel.remotePort = srcPort);
- channel.remoteIp := srcAddr;
- IF handler = NIL THEN
- NEW(handler)
- END;
- handler.param := NIL;
- handler.handle := BlockingCompletionHandler;
- ASSERT(EnetUdp.SetDestination(channel.socket, srcAddr, srcPort, handler, res));
- IF res = EnetBase.OpInProgress THEN
- WHILE EnetInterfaces.UpdateAll(res) & (handler.param = NIL) DO END
- END;
- ASSERT(res = 0)
- END;
- IF channel.remotePort < 0 THEN
- ASSERT(channel.remoteIp = srcAddr);
- channel.remotePort := srcPort
- END;
- IF channel.channelHandler # NIL THEN channel.channelHandler(channel, data, dataOffs, dataLen, packet) END
- END
- END HandlePacket;
- PROCEDURE StreamReceiveHandler (channel: Channel; VAR data: ARRAY OF CHAR; dataOffs, dataLen: EnetBase.Int; packet: EnetBase.Packet);
- BEGIN
- packet.ownedByUser := TRUE;
- ASSERT(EnetBase.PacketFifoPut(channel.channelHandlerParam(EnetStreams.Reader).enetPackets, packet))
- END StreamReceiveHandler;
- PROCEDURE SendFromStreamWriter (access: ANY; CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; flags: SET; VAR res: LONGINT);
- VAR
- ignore: BOOLEAN;
- BEGIN
- WITH access: Channel DO
- ignore := Send(access, buf, ofs, len, flags, NIL, res)
- END
- END SendFromStreamWriter;
- PROCEDURE BlockingCompletionHandler (handler: EnetBase.TaskHandler);
- BEGIN
- handler.param := handler
- END BlockingCompletionHandler;
- END EnetUdpChannels.
|