using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Collections; using System.Collections.Specialized; using System.IO; namespace IvyBus { /// /// Description résumée de IvyStream. /// internal class IvyTCPStreamV3 : IvyTCPStream { StreamReader input; StreamWriter output; IvyProtocolInterface receiver; /// the protocol separator internal const char ARG_START = '\x02'; internal const char ARG_END = '\x03'; internal const char MSG_END = '\n'; internal IvyTCPStreamV3(Socket socket, IvyProtocolInterface _receiver) : base ( socket ) { output = new StreamWriter(stream, Encoding.ASCII); output.NewLine = MSG_END.ToString(); input = new StreamReader(stream, Encoding.ASCII); receiver = _receiver; } /* the protocol magic numbers */ internal enum MessageType : ushort { Bye = 0, /* end of the peer */ AddRegexp = 1, /* the peer adds a regexp */ Msg = 2, /* the peer sends a message */ Error = 3, /* error message */ DelBinding = 4, /* the peer removes one of his regex */ // OLD DelRegexp rename to DelBinding EndRegexp = 5, /* no more regexp in the handshake */ StartRegexp = 6, /* avoid race condition in concurrent connexions */ DirectMsg = 7, /* the peer sends a direct message */ Die = 8, /* the peer wants us to quit */ Ping = 9, /* checks the presence of the other */ Pong = 10, /* checks the presence of the other */ }; /* * message Syntax: * this is text formated message 'type id STX ARG0 {[ETX] ARG1 [ETX] ARGn}\n' * * message Format: MessageType, id , length, string */ private void Serialize(ushort arg, char sep) { output.Write(arg); output.Write(sep); } private void Serialize(string[] arg, char sep) { for (int i = 0; i < arg.Length; i++) { output.Write(arg[i]); if ( i != (arg.Length-1) ) output.Write(sep); } } private void sendMsg(MessageType msgType, ushort msgId,params string[] msgData) { #if DEBUG Console.WriteLine(" sendMsg {0} id={1} data={2}", msgType, msgId, msgData); #endif Serialize((ushort)msgType, ' '); Serialize(msgId, ARG_START); Serialize(msgData, ARG_END); output.Write(MSG_END); output.Flush(); } public override void TokenStartRegexp(ushort port, string appName) { sendMsg(MessageType.StartRegexp, port, appName); } public override void TokenEndRegexp() { sendMsg(MessageType.EndRegexp, 0, ""); } public override void TokenApplicationId(ushort priority, string appId) { // NOt implemented in this protocol version } public override void TokenAddRegexp(ushort id, string bind) { sendMsg(MessageType.AddRegexp, id, bind); /* perhaps we should perform some checking here */ } public override void TokenAddBinding(ushort id, string bind) { // NOt implemented in this protocol version } public override void TokenDelBinding(ushort id) { sendMsg(MessageType.DelBinding, id, ""); } public override void TokenDirectMsg(ushort id, string message) { sendMsg(MessageType.DirectMsg, id, message); } public override void TokenPong(string s) { sendMsg(MessageType.Pong, 0, s); } public override void TokenPing(string s) { sendMsg(MessageType.Ping, 0, s); } public override void TokenBye(ushort id, string message) { sendMsg(MessageType.Bye, id, message); } public override void TokenDie(ushort id, string message) { sendMsg(MessageType.Die, id, message); } public override void TokenMsg(ushort key, string[] args) { sendMsg(MessageType.Msg, key, args); } public override void TokenError(ushort key, string arg) { sendMsg(MessageType.Msg, key, arg); } private ushort DeserializeShort() { int read; ushort ret = 0; char digit; // this will eat next non digit char ie space do { read = input.Read(); if (read < 0) break; digit = (char)read; if (Char.IsDigit(digit)) ret = (ushort)(ret * 10 + (digit - 0x30)); } while (Char.IsDigit(digit)); return ret; } private string DeserializeString(char sep,char eol_sep, out bool eol) { int read; char car = eol_sep; StringBuilder str = new StringBuilder(); // this will eat next non separator char do { read = input.Read(); if (read < 0) break; car = (char)read; if (car != sep && car != eol_sep) str.Append(car); } while (car != sep && car != eol_sep); eol = car == eol_sep || (read < 0 ); return str.ToString(); } private string[] DeserializeArgument() { StringCollection coll; string[] args; string str; bool eol; coll = new StringCollection(); do { str = DeserializeString(ARG_END, MSG_END, out eol); coll.Add(str); } while (!eol); args = new string[coll.Count]; coll.CopyTo(args, 0); return args; } internal override bool receiveMsg() { MessageType msgType = MessageType.Die; ushort msgId = 0; string[] msgData = null; try { msgType = (MessageType)DeserializeShort(); msgId = DeserializeShort(); msgData = DeserializeArgument(); #if DEBUG Console.WriteLine(" receiveMsg {0} id={1} data={2}", msgType, msgId, msgData); #endif switch (msgType) { case MessageType.Die: receiver.TokenDie(msgId, msgData[0]); break; case MessageType.Bye: receiver.TokenBye(msgId, msgData[0]); break; case MessageType.AddRegexp: receiver.TokenAddRegexp(msgId, msgData[0]); break; case MessageType.DelBinding: receiver.TokenDelBinding(msgId); break; case MessageType.EndRegexp: receiver.TokenEndRegexp(); break; case MessageType.Msg: receiver.TokenMsg(msgId, msgData); break; case MessageType.Pong: receiver.TokenPong(msgData[0]); break; case MessageType.Ping: receiver.TokenPing(msgData[0]); break; case MessageType.Error: receiver.TokenError(msgId, msgData[0]); break; case MessageType.StartRegexp: receiver.TokenStartRegexp(msgId, msgData[0]); break; case MessageType.DirectMsg: receiver.TokenDirectMsg(msgId, msgData[0]); break; default: throw new IvyException("protocol error, unknown message type " + msgType); } } catch (EndOfStreamException nfe) { return false; } catch (FormatException nfe) { throw new IvyException("protocol error on msgType"); } return true; } } }