namespace IvyBus { using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Collections; using System.Collections.Specialized; using System.IO; using IvyBus.Properties; /// /// Description résumée de IvyStream. /// internal class IvyTCPStreamV3 : NetworkStream, IvyProtocol { /// the protocol separator internal const char ArgStart = '\x02'; internal const char ArgEnd = '\x03'; internal const char MsgEnd = '\n'; private StreamReader input; private StreamWriter output; private IvyProtocol receiver; internal IvyTCPStreamV3(Socket socket, IvyProtocol receiver) : base(socket) { this.output = new StreamWriter(this, Ivy.ivyEncoding); this.output.NewLine = MsgEnd.ToString(); this.input = new StreamReader(this, Ivy.ivyEncoding); this.receiver = receiver; } void IvyProtocol.Close() { this.output.Close(); this.input.Close(); if ( this.Socket != null ) // Correct problem in Mono ( under linux ) this.Socket.Shutdown(SocketShutdown.Both); base.Close(); } /* the protocol magic numbers */ internal enum MessageType : int { 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 SendMsg(MessageType msgType, int msgId, string msgData) { // IOException Should be traited upstairs lock (this.output) { this.output.Write((int)msgType); this.output.Write(' '); this.output.Write(msgId); this.output.Write(ArgStart); this.output.Write(msgData); this.output.Write(MsgEnd); this.output.Flush(); } } void IvyProtocol.TokenStartRegexp(int port, string appName) { this.SendMsg(MessageType.StartRegexp, port, appName); } void IvyProtocol.TokenEndRegexp() { this.SendMsg(MessageType.EndRegexp, 0, string.Empty); } void IvyProtocol.TokenAddBinding(BindingType type, int id, string expression) { switch (type) { case BindingType.RegularExpression: this.SendMsg(MessageType.AddRegexp, id, expression); /* perhaps we should perform some checking here */ break; case BindingType.Simple: // NO Simple Binding in this protocol break; } } void IvyProtocol.TokenDelBinding(int id) { this.SendMsg(MessageType.DelBinding, id, string.Empty); } void IvyProtocol.TokenDirectMsg(int id, string message) { this.SendMsg(MessageType.DirectMsg, id, message); } void IvyProtocol.TokenPong(int id, string s) { this.SendMsg(MessageType.Pong, id, s); } void IvyProtocol.TokenPing(int id, string s) { this.SendMsg(MessageType.Ping, id, s); } void IvyProtocol.TokenBye(int id, string message) { this.SendMsg(MessageType.Bye, id, message); } void IvyProtocol.TokenDie(int id, string message) { this.SendMsg(MessageType.Die, id, message); } void IvyProtocol.TokenMsg(int key, string[] args) { string delimiter = string.Empty + ArgEnd; string data = string.Join(delimiter, args); // a bad protocol implementation in C add a delimiter to the end of each arg // we must add a delimiter to the end data += delimiter; this.SendMsg(MessageType.Msg, key, data); } void IvyProtocol.TokenError(int key, string arg) { this.SendMsg(MessageType.Error, key, arg); } private int DeserializeInt() { int read; int ret = 0; char digit; // this will eat next non digit char ie space do { read = this.input.Read(); if (read < 0) throw new EndOfStreamException(); digit = (char)read; if (Char.IsDigit(digit)) ret = (int)(ret * 10 + (digit - 0x30)); } while (Char.IsDigit(digit)); return ret; } private string DeserializeString(char sep) { int read; char car; StringBuilder str = new StringBuilder(); // this will eat next non separator char do { read = input.Read(); if (read < 0) throw new EndOfStreamException(); car = (char)read; if (car != sep ) str.Append(car); } while (car != sep); return str.ToString(); } bool IvyProtocol.ReceiveMsg() { MessageType msgType = MessageType.Die; int msgId = 0; string msgData = null; try { msgType = (MessageType)DeserializeInt(); msgId = DeserializeInt(); msgData = DeserializeString(MsgEnd); switch (msgType) { case MessageType.Die: this.receiver.TokenDie(msgId, msgData); break; case MessageType.Bye: this.receiver.TokenBye(msgId, msgData); break; case MessageType.AddRegexp: this.receiver.TokenAddBinding(BindingType.RegularExpression, msgId, msgData); break; case MessageType.DelBinding: this.receiver.TokenDelBinding(msgId); break; case MessageType.EndRegexp: this.receiver.TokenEndRegexp(); break; case MessageType.Msg: // a bad protocol implementation in C add a delimiter to the end of each arg // we must remove a delimiter to the end if ( msgData.Length > 0 ) msgData = msgData.Remove(msgData.Length - 1,1); this.receiver.TokenMsg(msgId, msgData.Split( ArgEnd )); break; case MessageType.Pong: this.receiver.TokenPong(msgId,msgData); break; case MessageType.Ping: this.receiver.TokenPing(msgId,msgData); break; case MessageType.Error: this.receiver.TokenError(msgId, msgData); break; case MessageType.StartRegexp: this.receiver.TokenStartRegexp(msgId, msgData); break; case MessageType.DirectMsg: this.receiver.TokenDirectMsg(msgId, msgData); break; default: throw new IvyException(Resources.UnknownMessage + msgType); } } catch (NullReferenceException) { return false; } catch (EndOfStreamException) { return false; } catch (FormatException) { throw new IvyException(Resources.ProtocolError); } return true; } } }