/*
* 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.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
using System.ComponentModel;
using System.Text;
using System.Diagnostics;
namespace TCPLibrary.Core
{
///
/// Base TCP client class wrapped around TcpClient.
///
public class Client
{
///
/// Lock object to assure that certain operation over the socket class are executed
/// in an exclusive way.
///
private Object _lock;
///
/// Wrapper around the Network Stream of the socket. (Read-Only)
///
private BinaryReader tr;
///
/// Wrapper around the Network Stream of the socket. (Write-Only)
///
private BinaryWriter tw;
///
/// Address to which the client is connected.
///
private IPEndPoint _address;
///
/// Flag to permit thread exit from the external.
///
protected Boolean _active;
///
/// Socket maintaining the connection.
///
protected TcpClient _socket;
///
/// Get/set the address to which the client is connected.
///
public IPEndPoint Address
{
get { return _address; }
}
///
/// Get the state of the connection.
///
public Boolean Connected
{
get { return _socket != null; }
}
///
/// Delegate for the event of receiving/sending a line of data from/to the server.
///
/// Sender of the event.
/// Line of data received.
public delegate void DataCommunicationHandler(Client sender, Data Data);
///
/// Occurs when a line of data is received from the server.
///
public event DataCommunicationHandler OnDataReceived;
///
/// Occurs before the client send a line of data to the server.
///
public event DataCommunicationHandler OnBeforeDataSent;
///
/// Occurs after the client send a line of data to the server.
///
public event DataCommunicationHandler OnAfterDataSent;
///
/// Delegate for the event of connection/disconnection to the server.
///
/// Sender of the event.
public delegate void ConnectionStateHandler(Client sender);
///
/// Occurs when the client successfully connect the server.
///
public event ConnectionStateHandler OnConnected;
///
/// Occurs when the client is disconnected from the server.
///
public event ConnectionStateHandler OnDisconnected;
///
/// Delegate for the event of connection failed for rejection by the server.
///
/// Sender of the event.
/// Message of fail sent by the server.
public delegate void ConnectionFailedHandler(Client sender, byte[] Message);
///
/// Occurs when the client failed to connect to the server because the server rejected the connection.
///
public event ConnectionFailedHandler OnConnectFailed;
///
/// Base constructor of the class.
///
public Client()
{
_lock = new object();
_address = null;
}
///
/// Try to connect to the server specified.
///
/// Address of the server to connect.
/// Port of the server to connect.
/// True if the connection is successfull, false otherwise.
///
///
public virtual Boolean Connect(String Indirizzo, Int32 Port)
{
if (!Connected)
{
IPHostEntry addr = Dns.GetHostEntry(Indirizzo);
IPAddress ip = null;
foreach (var itm in addr.AddressList)
{
if (itm.AddressFamily == AddressFamily.InterNetwork)
{
ip = itm;
break;
}
}
if (ip != null)
{
_address = new IPEndPoint(ip, Port);
_socket = new TcpClient();
try
{
_socket.Connect(_address);
}
catch (SocketException)
{
_socket = null;
_address = null;
throw;
}
tr = new BinaryReader(_socket.GetStream());
tw = new BinaryWriter(_socket.GetStream());
// Receive the confirmation of the status of the connection to the server.
// Is CONNECTEDTCPSERVER if the connection is successfull, all other cases are wrong.
Int32 Length = Convert.ToInt32(tr.ReadInt32());
byte[] arr = new byte[Length];
tr.Read(arr, 0, Length);
ASCIIEncoding ae = new ASCIIEncoding();
String Accept = ae.GetString(arr, 0, arr.Length);
if (Accept == "CONNECTEDTCPSERVER")
{
_active = true;
Thread thd = new Thread(new ThreadStart(MainThread));
thd.IsBackground = true;
thd.Name = "Client connected to " + Indirizzo + ":" + Port.ToString();
thd.Start();
if (OnConnected != null)
OnConnected(this);
return true;
}
else
{
Stop();
if (OnConnectFailed != null)
OnConnectFailed(this, arr);
return false;
}
}
else
return false;
}
else
throw new ArgumentException("The client is already connected!");
}
///
/// Disconnect a Client if connected.
///
public virtual void Disconnect()
{
lock (_lock)
{
_active = false;
tr.Close();
tw.Close();
}
}
///
/// Disconnect a Client if connected.
///
protected virtual void Stop()
{
if (_socket != null)
{
tr.Close();
tw.Close();
_socket.Close();
_socket = null;
_address = null;
if (OnDisconnected != null)
OnDisconnected(this);
}
}
///
/// Thread function that actually run the socket work.
///
private void MainThread()
{
while (_active)
{
byte[] arr = null;
try
{
int length = Convert.ToInt32(tr.ReadInt32());
arr = new byte[length];
int index = 0;
while (length > 0)
{
int receivedBytes = tr.Read(arr, index, length);
length -= receivedBytes;
index += receivedBytes;
}
}
catch (Exception) { }
lock (_lock)
{
if (_active)
{
Boolean Stato = _socket.Client.Poll(100, SelectMode.SelectRead);
if ((arr == null) && (Stato == true))
break;
else
if (OnDataReceived != null)
OnDataReceived(this, new Data(arr));
}
else
break;
}
}
Stop();
}
///
/// Send a line of data to the server.
///
/// Data to send to the server.
///
public void Send(Data msg)
{
if (_active)
{
if (OnBeforeDataSent != null)
OnBeforeDataSent(this, msg);
try
{
tw.Write(msg.Message.Length);
tw.Write(msg.Message);
tw.Flush();
if (OnAfterDataSent != null)
OnAfterDataSent(this, msg);
}
catch (IOException)
{
// Pensare a cosa fare quà. Questo è il caso in cui il server ha chiuso forzatamente
// la connessione mentre il client mandava roba.
}
}
else
throw new ArgumentException("The link is closed. Unable to send data.");
}
}
}