/* Blunk - Multi User Server Framework Copyright 2009 - 2010 Nils W [nillus.net] Michael Graham [script-o-matic.net] This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . * PORTED TO C# BY awa500! */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using Holo.Socket_servers.MUS; using System.Net.Sockets; using System.Threading; using System.IO; using System.ComponentModel; using MiscUtil.IO; using MiscUtil.Conversion; namespace Holo.Source.Socket_servers.MUS { public class MusClient { private int m_ID; private Socket m_socket; private bool m_alive; private int m_userID; private MusMessageHandler m_msgHandler; private byte[] dataBuffer = new byte[1024]; public MusClient(int ID, Socket socket) { m_ID = ID; m_socket = socket; m_alive = true; m_socket.BeginReceive(dataBuffer, 0, dataBuffer.Length, SocketFlags.None, new AsyncCallback(dataArrival), null); setMessageHandler(new MusMessageHandler()); } public void setMessageHandler(MusMessageHandler handler) { m_msgHandler = handler; } #region Connection management /// /// Immediately completes the current data transfer [if any], disconnects the client and flags the connection slot as free. /// internal void stop() { if (m_alive == false) return; try { m_socket.Dispose(); m_socket = null; MultiUserServer.freeConnection(m_ID); m_alive = false; } catch { } } #endregion #region Data receiving /// /// This void is triggered when a new datapacket arrives at the socket of this user. The packet is separated and processed. On errors, the client is disconnected. /// /// The IAsyncResult of this BeginReceive asynchronous action. private void dataArrival(IAsyncResult iAr) { try { if (m_alive) { int bytesReceived = m_socket.EndReceive(iAr); string connectionData = System.Text.Encoding.Default.GetString(dataBuffer, 0, bytesReceived); try { MemoryStream ms = new MemoryStream(System.Text.Encoding.Default.GetBytes(connectionData)); EndianBinaryReader br = new EndianBinaryReader(EndianBitConverter.Big, ms); parseData(br); } catch (Exception x) { Out.WriteError(x.ToString()); } m_socket.BeginReceive(dataBuffer, 0, dataBuffer.Length, SocketFlags.None, new AsyncCallback(dataArrival), null); } else stop(); } catch { stop(); } } #endregion #region Data sending /// /// Sends a single packet to the client via an asynchronous BeginSend action. /// /// The string of data to send. char[01] is added. internal void sendData(EndianBinaryWriter buffer) { try { buffer.BaseStream.Position = 0; byte[] body = new byte[buffer.BaseStream.Length]; int numBytesToRead = (int)buffer.BaseStream.Length; int numBytesRead = 0; while (numBytesToRead > 0) { // Read may return anything from 0 to 10. int n = buffer.BaseStream.Read(body, numBytesRead, 1); // The end of the file is reached. if (n == 0) { break; } numBytesRead += n; numBytesToRead -= n; } m_socket.BeginSend(body, 0, body.Length, 0, new AsyncCallback(sentData), null); string Data = System.Text.Encoding.Default.GetString(body); Out.WriteSpecialLine("[" + Data.Replace(Convert.ToChar(13).ToString(), "{13}").Replace(Convert.ToChar(9).ToString(), "{9}") + "]", Out.logFlags.MehAction, ConsoleColor.White, ConsoleColor.DarkYellow, "> [" + Thread.GetDomainID() + "]", 2, ConsoleColor.Yellow); } catch { stop(); } } /// /// Triggered when an asynchronous BeginSend action is completed. Virtual user completes the transfer action and leaves asynchronous action. /// /// The IAsyncResult of this BeginSend asynchronous action. private void sentData(IAsyncResult iAr) { try { m_socket.EndSend(iAr); } catch { stop(); } } #endregion //public void parseData(BinaryReader m_in) //{ // try // { // // Read & validate header tag // byte headerTag = m_in.ReadByte(); // m_in.ReadByte(); // if (headerTag != 'r') // { // // Bad header tag, or no more input on the stream // m_alive = false; // } // else // { // // Start parsing the incoming message // MusMessage msg = new MusMessage(); // // Message length // byte a = m_in.ReadByte(); // byte b = m_in.ReadByte(); // byte c = m_in.ReadByte(); // byte d = m_in.ReadByte(); // msg.size = BitUtil.bytesToInt(a, b, c, d);//m_in.ReadInt32(); // // Error code // a = m_in.ReadByte(); // b = m_in.ReadByte(); // c = m_in.ReadByte(); // d = m_in.ReadByte(); // msg.errorCode = BitUtil.bytesToInt(a, b, c, d);//m_in.ReadInt32(); // // Timestamp // a = m_in.ReadByte(); // b = m_in.ReadByte(); // c = m_in.ReadByte(); // d = m_in.ReadByte(); // msg.timeStamp = BitUtil.bytesToInt(a, b, c, d);//m_in.ReadInt32(); // // Subject // a = m_in.ReadByte(); // b = m_in.ReadByte(); // c = m_in.ReadByte(); // d = m_in.ReadByte(); // int length = BitUtil.bytesToInt(a, b, c, d); // if (length <= 0) // msg.subject = ""; // else // { // byte[] bytes = new byte[length]; // m_in.Read(bytes, 0, bytes.Length); // // Advance one byte if uneven // if ((length % 2) != 0) // { // m_in.ReadByte(); // } // msg.subject = System.Text.Encoding.Default.GetString(bytes); // } // //msg.subject = MusUtil.readEvenPaddedString(m_in); // // Sender // a = m_in.ReadByte(); // b = m_in.ReadByte(); // c = m_in.ReadByte(); // d = m_in.ReadByte(); // length = BitUtil.bytesToInt(a, b, c, d); // if (length <= 0) // msg.senderID = ""; // else // { // byte[] bytes = new byte[length]; // m_in.Read(bytes, 0, bytes.Length); // // Advance one byte if uneven // if ((length % 2) != 0) // { // m_in.ReadByte(); // } // msg.senderID = System.Text.Encoding.Default.GetString(bytes); // } // //msg.senderID = MusUtil.readEvenPaddedString(m_in); // // Receivers // a = m_in.ReadByte(); // b = m_in.ReadByte(); // c = m_in.ReadByte(); // d = m_in.ReadByte(); // msg.receivers = new string[BitUtil.bytesToInt(a, b, c, d)]; // for (int i = 0; i < msg.receivers.Length; i++) // { // a = m_in.ReadByte(); // b = m_in.ReadByte(); // c = m_in.ReadByte(); // d = m_in.ReadByte(); // length = BitUtil.bytesToInt(a, b, c, d); // if (length <= 0) // msg.receivers[i] = ""; // else // { // byte[] bytes = new byte[length]; // m_in.Read(bytes, 0, bytes.Length); // // Advance one byte if uneven // if ((length % 2) != 0) // { // m_in.ReadByte(); // } // msg.receivers[i] = System.Text.Encoding.Default.GetString(bytes); // } // //msg.receivers[i] = MusUtil.readEvenPaddedString(m_in); // } // // Content // // EXCEPTION: "Logon", breaks standard element formatting // if (msg.subject.Equals("Logon")) // { // // Read in remaining data // //byte[] tmpBytes = new byte[reader.]; // //reader.Read(tmpBytes,); // // Set fields // msg.contentType = MusTypes.String; // msg.contentString = ""; // Not needed // //msg.contentString = new String(tmpBytes.ToArray); // } // else // { // // Content type // msg.contentType = m_in.ReadInt16(); // // Read in content // if (msg.contentType == MusTypes.Integer) // { // msg.contentInt = m_in.ReadInt32(); // } // else if (msg.contentType == MusTypes.String) // { // msg.contentString = MusUtil.readEvenPaddedString(m_in); // } // else if (msg.contentType == MusTypes.PropList) // { // msg.contentPropList = MusUtil.readPropList(m_in); // } // } // // Message parsed, now handle it // Out.WriteLine(this + " <-- " + msg.toString()); // if (m_msgHandler != null) // { // m_msgHandler.handleIncomingMessage(this, msg); // } // } // } // catch (SocketException ex) // { // // Socket closed by remote end // m_alive = false; // } // catch (Exception ex) // { // // What happened? // Out.WriteError("MusClient: abnormal error while receiving/parsing data! " + ex.ToString()); // m_alive = false; // } //} public void parseData(EndianBinaryReader m_in) { try { // Read & validate header tag byte headerTag = m_in.ReadByte(); m_in.ReadByte(); if (headerTag != 'r') { // Bad header tag, or no more input on the stream m_alive = false; } else { // Start parsing the incoming message MusMessage msg = new MusMessage(); // Message length msg.size = m_in.ReadInt32(); // Error code msg.errorCode = m_in.ReadInt32(); // Timestamp msg.timeStamp = m_in.ReadInt32(); // Subject msg.subject = MusUtil.readEvenPaddedString(m_in); // Sender msg.senderID = MusUtil.readEvenPaddedString(m_in); // Receivers msg.receivers = new string[m_in.ReadInt32()]; for (int i = 0; i < msg.receivers.Length; i++) { msg.receivers[i] = MusUtil.readEvenPaddedString(m_in); } // Content // EXCEPTION: "Logon", breaks standard element formatting if (msg.subject.Equals("Logon")) { // Read in remaining data //byte[] tmpBytes = new byte[reader.]; //reader.Read(tmpBytes,); // Set fields msg.contentType = MusTypes.String; msg.contentString = ""; // Not needed //msg.contentString = new String(tmpBytes.ToArray); } else { // Content type msg.contentType = m_in.ReadInt16(); // Read in content if (msg.contentType == MusTypes.Integer) { msg.contentInt = m_in.ReadInt32(); } else if (msg.contentType == MusTypes.String) { msg.contentString = MusUtil.readEvenPaddedString(m_in); } else if (msg.contentType == MusTypes.PropList) { msg.contentPropList = MusUtil.readPropList(m_in); } } // Message parsed, now handle it Out.WriteLine(this + " <-- " + msg.toString()); if (m_msgHandler != null) { m_msgHandler.handleIncomingMessage(this, msg); } } } catch (SocketException ex) { // Socket closed by remote end m_alive = false; } catch (Exception ex) { // What happened? Out.WriteError("MusClient: abnormal error while receiving/parsing data! " + ex.ToString()); m_alive = false; } } public bool sendMessage(MusMessage msg) { if (msg != null) { EndianBinaryWriter m_out = new EndianBinaryWriter(BigEndianBitConverter.Big, new MemoryStream()); EndianBinaryWriter buffer = new EndianBinaryWriter(BigEndianBitConverter.Big, new MemoryStream()); // Log it Out.WriteLine(this + " --> " + msg.toString()); // Sign message by system msg.senderID = "System"; msg.receivers = new string[] { "*" }; msg.timeStamp = DateTime.Now.Millisecond; try { // Temporarily buffer for all data past message length //ByteArrayOutputStream bufferStream = new ByteArrayOutputStream(); //DataOutputStream buffer = new DataOutputStream(bufferStream); // Error code buffer.Write(msg.errorCode); // Timestamp buffer.Write(msg.timeStamp); // Subject MusUtil.writeEvenPaddedString(buffer, msg.subject); // Sender MusUtil.writeEvenPaddedString(buffer, msg.senderID); // Receivers buffer.Write(msg.receivers.Length); for (int i = 0; i < msg.receivers.Length; i++) { MusUtil.writeEvenPaddedString(buffer, msg.receivers[i]); } // Content type buffer.Write(msg.contentType); // Content if (msg.contentType != MusTypes.Void) { if (msg.contentType == MusTypes.Integer) { buffer.Write(msg.contentInt); } else if (msg.contentType == MusTypes.String) { MusUtil.writeEvenPaddedString(buffer, msg.contentString); } else if (msg.contentType == MusTypes.PropList) { MusUtil.writePropList(buffer, msg.contentPropList); } else { Out.WriteError("Unsupported MusMessage content type " + msg.contentType + "!"); } } // Flush buffer to ensure all data is in it //buffer.flush(); //bufferStream.flush(); // Extract buffer to a byte array buffer.BaseStream.Position = 0; byte[] body = new byte[buffer.BaseStream.Length]; int numBytesToRead = (int)buffer.BaseStream.Length; int numBytesRead = 0; while (numBytesToRead > 0) { // Read may return anything from 0 to 10. int n = buffer.BaseStream.Read(body, numBytesRead, 1); // The end of the file is reached. if (n == 0) { break; } numBytesRead += n; numBytesToRead -= n; } //buffer.close(); //bufferStream.close(); //bufferStream = null; //buffer = null; // Start writing the full message to the socket output // Header tag m_out.Write('r'); m_out.Write(0); // Length of body m_out.Write(body.Length); // Body m_out.Write(body); //// Flush the stream //m_out.flush(); sendData(m_out); // Sent! return true; } catch (Exception ex) { Out.WriteError("MusClient " + m_ID + ": error while sending MusMessage " + ex.ToString()); } } // Sending failed return false; } public int getID() { return m_ID; } /** * Sets the ID of the MUS user populating this MusClient. * * @param ID The ID of the MUS user */ public void setUserID(int ID) { m_userID = ID; } /** * Returns the ID (number) of the MUS user populating this MusClient. */ public int getUserID() { return m_userID; } public MusMessageHandler getMessageHandler() { return m_msgHandler; } public String toString() { return "MusClient [" + m_ID + "][]"; } } }