/* * Copyright (C) 2009-2011 Ferreri Alessio * Copyright (C) 2009-2018 PCSX2 Dev Team * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; using System.IO; using System.ComponentModel; using System.Text; namespace TCPLibrary.Core { /// /// Base TCP server class wrapped around TcpListener. /// public class Server { /// /// Socket maintaining the connection. /// private TcpListener _socket; /// /// Port to which the server will listen. /// private Int32 _port; /// /// Whether the server is enabled or not. /// private Boolean _enabled; /// /// List of the clients connected to the server. /// private List _clients; /// /// Number of connection permitted in the backlog of the server. /// private Int32 _connectionbacklog; /// /// Delegate for the event of the Enabled property change. /// /// Sender of the event. public delegate void EnabledChangedHandler(Server sender); /// /// Occurs when the Enabled property is changed. /// public event EnabledChangedHandler OnEnabledChanged; /// /// Delegate for the event of receiving a line of data from a client. /// /// Server raising the event. /// Client involved in the communication. /// Line of data received. public delegate void DataCommunicationHandler(Server server, ClientS client, Data Data); /// /// Occurs when a client send a line of data to the server. /// public event DataCommunicationHandler OnClientDataReceived; /// /// Occurs before the server send a line of data to a client. /// public event DataCommunicationHandler OnClientBeforeDataSent; /// /// Occurs after the server send a line of data to a client. /// public event DataCommunicationHandler OnClientAfterDataSent; /// /// Delegate for the event of a connection of a client. /// /// Server raising the event. /// The new client connected. public delegate void ConnectedHandler(Server server, ClientS sender); /// /// Occurs after a client is connected to the server. /// public event ConnectedHandler OnClientAfterConnect; /// /// Delegate for the event of a connection of a client. /// /// Server raising the event. /// The new client to be connected. /// Specify if the client should be accepted into the server. public delegate void BeforeConnectedHandler(Server server, ClientS client, CancelArgs args); /// /// Occurs before a client is allowed to connect to the server. /// public event BeforeConnectedHandler OnClientBeforeConnect; /// /// Delegate for the event of disconnection of a client. /// /// Server raising the event. /// The client disconnected. public delegate void DisconnectedHandler(Server server, ClientS sender); /// /// Occurs right after a client disconnect from the server. /// public event DisconnectedHandler OnClientAfterDisconnected; /// /// Occurs before a client disconnect from the server. /// public event DisconnectedHandler OnClientBeforeDisconnected; /// /// Get/set the port number to which the server will listen. Cannot be set while the server is active. /// /// public Int32 Port { get { return _port; } set { if (Enabled == false) _port = value; else throw new ArgumentException("Impossibile eseguire l'operazione a server attivo"); } } /// /// Get/set the enabled state of the server. Setting this to true will actually activate the server. /// /// public Boolean Enabled { get { return _enabled; } set { if (value == true) { if (_enabled == false) ActivateServer(); } else { if (_enabled == true) DeactivateServer(); } } } /// /// Get/set the number of connection permitted in the backlog of the server. /// /// public Int32 ConnectionBackLog { get { return _connectionbacklog; } set { if (Enabled == false) _connectionbacklog = value; else throw new ArgumentException("Impossibile eseguire l'operazione a server attivo"); } } /// /// Get the list of the clients connected to the server. /// public List Clients { get { return _clients; } } /// /// Deactivate the server. /// protected virtual void DeactivateServer() { _enabled = false; _socket.Stop(); _socket = null; lock (_clients) { for (int i = 0; i < _clients.Count; i++) _clients[i].Disconnect(); } if (OnEnabledChanged != null) OnEnabledChanged(this); } /// /// Activate the server. /// protected virtual void ActivateServer() { _socket = new TcpListener(IPAddress.Any, Port); _socket.Start(ConnectionBackLog); Thread thd = new Thread(new ThreadStart(MainThread)); thd.Name = "Server on port " + Port.ToString(); thd.IsBackground = true; thd.Start(); _enabled = true; if (OnEnabledChanged != null) OnEnabledChanged(this); } /// /// Broadcast a line of data to all the clients connected to the server. /// /// Line of data to be sent. /// public void Broadcast(Data Data) { if (Enabled) { lock (_clients) { foreach (var itm in _clients) if (itm.Connected) itm.Send(Data); } } else throw new ArgumentException("Unable to execute this operation when the server is inactive."); } /// /// Base constructor of the class. /// public Server() { _clients = new List(); _port = 0; _connectionbacklog = 0; _enabled = false; } /// /// Thread function that actually run the server socket work. /// private void MainThread() { try { while (Enabled == true) { TcpClient client = _socket.AcceptTcpClient(); CancelArgs args = new CancelArgs(false); ClientS cl = CreateClient(client); if (OnClientBeforeConnect != null) OnClientBeforeConnect(this, cl, args); if (args.Cancel != true) { lock (_clients) { _clients.Add(cl); } ASCIIEncoding ae = new ASCIIEncoding(); byte[] arr = ae.GetBytes("CONNECTEDTCPSERVER"); cl.Send(new Data(arr)); if (OnClientAfterConnect != null) OnClientAfterConnect(this, cl); cl.Start(); } else { client.GetStream().Close(); client.Close(); } } } catch (SocketException) { Enabled = false; } } /// /// Overridable function that create the structure to memorize the client data. /// /// Socket of the client. /// The structure in which memorize all the information of the client. protected virtual ClientS CreateClient(TcpClient socket) { ClientS cl = new ClientS(this, socket); return cl; } /// /// Raise the OnClientAfterDataSent event. /// /// Client that raised the event. /// Line of data sent. internal void RaiseAfterDataSentEvent(ClientS cl, Data data) { if (OnClientAfterDataSent != null) OnClientAfterDataSent(this, cl, data); } /// /// Raise the OnClientBeforeDataSent event. /// /// Client that raised the event. /// Line of data sent. internal void RaiseBeforeDataSentEvent(ClientS cl, Data data) { if (OnClientBeforeDataSent != null) OnClientBeforeDataSent(this, cl, data); } /// /// Raise the OnDataReceived event. /// /// Client that raised the event. /// Line of data received. internal void RaiseDataReceivedEvent(ClientS cl, Data data) { if (OnClientDataReceived != null) OnClientDataReceived(this, cl, data); } /// /// Raise the OnClientAfterDisconnected event. /// /// Client that raised the event. internal void RaiseClientAfterDisconnectedEvent(ClientS cl) { if (OnClientAfterDisconnected != null) OnClientAfterDisconnected(this, cl); } /// /// Raise the OnClientBeforeDisconnected event. /// /// Client that raised the event. internal void RaiseClientBeforeDisconnectedEvent(ClientS cl) { if (OnClientBeforeDisconnected != null) OnClientBeforeDisconnected(this, cl); } } }