/*
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 + "][]";
}
}
}