/* * 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."); } } }