using System; using System.Net; using System.Net.Sockets; using System.IO; using System.Threading; using System.Text; using System.Collections.Generic; namespace Protocol { public class SocketClient { // fixme: hook in the onconnectionfailed event // fixme: on byte data received? (if we are not using newlines) #region events public delegate void ConnectionInitiatedEvent(string server); public delegate void ConnectionFailedEvent(string message); public delegate void ConnectedEvent(); public delegate void DisconnectedEvent(); public delegate void AutoReconnectEvent(); public delegate void ErrorEvent(string code, string message); public delegate void LogEvent(string message); public delegate void DataReceivedEvent(string message); public delegate void DataSentEvent(string message); // connection public event ConnectionInitiatedEvent OnConnectionAttempt; public event ConnectionFailedEvent OnConnectionFailed; public event ConnectedEvent OnConnected; public event DisconnectedEvent OnDisconnected; public event AutoReconnectEvent OnAutoReconnect; public event ErrorEvent OnError; public event LogEvent OnLog; public event DataSentEvent OnDataSent; public event DataReceivedEvent OnDataReceived; #endregion #region declares private string _UserAgent = "fuIRC 0.5"; private string _Server = ""; private int _Port = 2628; private bool _UseSSL = false; private bool _AutoReconnect = false; private int _AutoReconnectDelay = 0; private System.Threading.Timer AutoReconnectTimer; #endregion #region properties public string Server { get { return _Server; } set { _Server = value; } } public int Port { get { return _Port; } set { _Port = value; } } public bool AutoReconnect { get { return _AutoReconnect; } set { _AutoReconnect = value; } } public int AutoReconnectDelay { get { return _AutoReconnectDelay; } set { _AutoReconnectDelay = value; } } public bool UseSSL { get { return _UseSSL; } set { _UseSSL = value; } } public string UserAgent { get { return _UserAgent; } set { _UserAgent = value; } } #endregion #region client private Socket client; private byte[] data = new byte[1024]; private int size = 1024; private string buffer = ""; public void Connect() { // check the connection InitializeAutoReconnectTimer(); if (OnConnectionAttempt != null) { OnConnectionAttempt(Server); } if (client != null) { client = null; } client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); client.Blocking = false; client.LingerState = new LingerOption(false, 0); try { IPHostEntry he = Dns.GetHostEntry(Server); IPEndPoint iep = new IPEndPoint(he.AddressList[0], Port); client.BeginConnect(iep, new AsyncCallback(Connected), client); } catch (Exception e) { if (OnConnectionFailed != null) { OnConnectionFailed(e.ToString()); } if (OnError != null) { OnError("000", e.ToString()); } } // fixme: onconnectionerror // fixme: what if we never connect or something else happens } public void Disconnect() { if (client == null) { return; } client.Shutdown(SocketShutdown.Both); client.Close(); DisconnectedHandler(); } public void WriteRAW(string message) { if (OnDataSent != null) { OnDataSent(message); } // fixme: what if the client is null or we are not connected // any other checks we can install here if (client == null) { return; } if (!client.Connected) { return; } byte[] outgoing = Encoding.ASCII.GetBytes(message + "\r\n"); try { client.BeginSend(outgoing, 0, outgoing.Length, SocketFlags.None, new AsyncCallback(SendData), client); } catch (SocketException e) { if (OnError != null) { OnError("000", e.ToString()); } } } #region callbacks private void Connected(IAsyncResult iar) { client = (Socket)iar.AsyncState; try { client.EndConnect(iar); if (OnConnected != null) { OnConnected(); } client.BeginReceive(data, 0, size, SocketFlags.None, new AsyncCallback(ReceiveData), client); } catch (SocketException e) { if (OnError != null) { OnError("", e.ToString()); } DisconnectedHandler(); } } private void ReceiveData(IAsyncResult iar) { // client.Available // this is the amount of data in the receive buffer // const int FIONREAD = 0x4004667F; // Check how many bytes have been received. // client.IOControl(FIONREAD, null, outValue); try { Socket remote = (Socket)iar.AsyncState; int recv = remote.EndReceive(iar); // fixme: check the lendth of stringdata if (recv <= 0) { DisconnectedHandler(); return; } string stringData = Encoding.ASCII.GetString(data, 0, recv); if (stringData != "") { IncomingDataHandler(stringData); } if (client.Connected) { client.BeginReceive(data, 0, size, SocketFlags.None, new AsyncCallback(ReceiveData), client); } else { DisconnectedHandler(); } } catch (Exception e) { if (OnError != null) { OnError("", e.ToString()); } DisconnectedHandler(); } } private void IncomingDataHandler(string line) { line = buffer + line.Replace("\r", ""); buffer = ""; if ((!line.EndsWith("\n")) && (line.Contains("\n"))) { int clippoint = line.LastIndexOf('\n'); buffer = line.Substring(clippoint + 1, line.Length - clippoint - 1); line = line.Substring(0, clippoint); } string[] lines = line.Trim().Split(new char[] { '\n' }); for (int i = 0; i < lines.Length; i++) { if (OnDataReceived != null) { OnDataReceived(lines[i]); } DataHandler(lines[i]); } } // placeholder protected virtual void DataHandler(string message) { // } private void SendData(IAsyncResult iar) { // fixme: if we are not connected // if iar is null // other postential issues try { Socket remote = (Socket)iar.AsyncState; int sent = remote.EndSend(iar); } catch (Exception e) { if (OnError != null) { OnError("000", e.ToString()); } } } #endregion #region are we connected public bool Connected() { // if ((client.Poll(3000, SelectMode.SelectRead) && client.Available <= 0)) return client.Poll(3000, SelectMode.SelectRead); } #endregion #region relog timer protected void DisconnectedHandler() { if (OnDisconnected != null) { OnDisconnected(); } if (AutoReconnect) { StartReconnectTimer(); } // } private void StartReconnectTimer() { AutoReconnectTimer.Change(AutoReconnectDelay, System.Threading.Timeout.Infinite); } private void InitializeAutoReconnectTimer() { if (AutoReconnectTimer != null) { AutoReconnectTimer = null; } AutoReconnectTimer = new System.Threading.Timer(new TimerCallback(AutoReconnectTimer_Elapsed)); AutoReconnectTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); } private void AutoReconnectTimer_Elapsed(object sender) { AutoReconnectTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); if (OnAutoReconnect != null) { OnAutoReconnect(); } Connect(); } #endregion #endregion #region event handlers protected void LogHandler(string message) { if (OnLog != null) { OnLog(message); } } protected void ErrorHandler(string errorcode, string message) { if (OnError != null) { OnError(errorcode, message); } } #endregion } }