/*
* Copyright (c) 2009 Ferreri Alessio
*
* 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);
}
}
}