JD2022-TU1/main/tools/framework/JD.Remoting/Connections/TCPServerConnection.cs

274 lines
9.7 KiB
C#

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using JD.Interfaces.Remoting;
using JD.Remoting.Details;
namespace JD.Remoting.Connections
{
/// <summary>
/// Represents the implementation of a TCP server listening to other tools.
/// </summary>
public class TcpServerConnection : TcpConnection
{
private Socket m_connectionSocket;
private readonly Mutex m_connectionSocketMutex;
private static object connectionLocker;
private bool m_initialized;
private volatile bool m_accepting;
/// <summary>
/// Default constructor.
/// </summary>
public TcpServerConnection(IPlugin plugin)
: base(plugin)
{
m_connectionSocketMutex = new Mutex();
connectionLocker = new object();
DisconnectionReceived += OnDisconnectionMessageReceived;
}
/// <summary>
/// Opens a TCP connection listening to distant targets. This operation is asynchronous.
/// <remarks>Use the Connection.IsConnected property to determine if the connection is established or not. </remarks>
/// </summary>
/// <returns>Always zero. </returns>
public override int Open(IEndPointId endPointId = null)
{
var _endPointId = endPointId ?? EndPointId.Default;
if (IsConnected)
throw new InvalidOperationException("The connection is already opened. Use Close() function to terminate it before calling Open() again. ");
if (m_socket != null)
throw new InvalidOperationException("A connection socket already exists. Use Close() function to close the connection properly before calling Open() again. ");
m_endPoint = new IPEndPoint(_endPointId.IpAddress, _endPointId.Port);
m_endPointId = _endPointId;
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
return 0;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (IsConnected)
throw new InvalidOperationException("Connection is still active. Use Close() before disposing the connection. ");
DisposeSocket(ref m_connectionSocket, m_connectionSocketMutex);
base.Dispose(true);
}
}
/// <summary>
/// Gets a value that indicates whether internal socket is initialized.
/// <remarks>The connection state may change between the moment you call IsConnected and the moment you use the connection. </remarks>
/// </summary>
public bool IsInitialiazed
{
get
{
return m_initialized && m_socket != null;
}
}
/// <summary>
/// Gets a value that indicates whether the connection is established.
/// </summary>
public override bool IsConnected
{
get
{
try
{
return IsInitialiazed && null != m_connectionSocket && m_connectionSocket.Connected;
}
catch
{
return false;
}
}
}
/// <summary>
/// Tries to connect the socket.
/// </summary>
/// <returns>0 if operation succeeded, a non-zero value otherwise.
/// See Windows Sockets Error Codes page for more information on error codes. </returns>
/// <exception cref="System.ObjectDisposedException">The System.Net.Socket object has been closed. </exception>
/// <exception cref="System.Security.SecurityException">A caller higher in the call stack does not have permission for the requested operation. </exception>
/// <exception cref="System.InvalidOperationException">The Socket is listening.</exception>
protected override int Connect()
{
int errorCode = 0;
try
{
using (new AutoMutex(m_socketMutex))
{
m_socket.Bind(m_endPoint);
m_socket.Listen(10);
m_initialized = true;
}
}
catch (SocketException ex)
{
errorCode = ex.ErrorCode;
}
return errorCode;
}
/// <summary>
/// Tries to disconnect the socket.
/// </summary>
/// <returns>0 if operation succeeded, a non-zero value otherwise.
/// See Windows Sockets Error Codes page for more information on error codes. </returns>
/// <exception cref="System.ObjectDisposedException">The System.Net.Socket object has been closed. </exception>
/// <exception cref="System.PlatformNotSupportedException">This method requires Windows 2000 or earlier. </exception>
protected override int Disconnect()
{
int errorCode = 0;
try
{
if (m_connectionSocket != null)
{
DisposeSocket(ref m_connectionSocket, m_connectionSocketMutex);
m_connectionSocket = null;
}
if (m_socket != null)
{
// Accept is a blocking operation, and the socket mutex is locked during that time. Force a disposal.
if (m_accepting)
DisposeSocket(ref m_socket);
else
DisposeSocket(ref m_socket, m_socketMutex);
m_socket = null;
}
m_initialized = false;
}
catch (SocketException ex)
{
errorCode = ex.ErrorCode;
}
return errorCode;
}
protected override int Send(byte[] data)
{
return m_connectionSocket.Send(data);
}
/// <summary>
/// Receives data from the socket.
/// </summary>
/// <param name="bufferOffset"></param>
/// <param name="readCount">The number of bytes read from the socket. </param>
/// <param name="buffer"></param>
/// <param name="bufferSize"></param>
/// <returns>0 if operation succeeded, a non-zero value otherwise.
/// See Windows Sockets Error Codes page for more information on error codes. </returns>
/// <exception cref="System.ArgumentOutOfRangeException">Offset or size values are invalid. </exception>
/// <exception cref="System.ObjectDisposedException">The System.Net.Socket object has been closed. </exception>
protected override int Receive(byte[] buffer, int bufferSize, int bufferOffset, out int readCount)
{
using (new AutoMutex(m_connectionSocketMutex))
{
readCount = 0;
if (m_connectionSocket == null)
{
throw new InvalidOperationException("Socket is not connected. Call Accept() method before trying to receive a message again. ");
}
if (!m_connectionSocket.Connected)
{
// Socket is disconnected. Dispose it.
DisposeSocket(ref m_connectionSocket, false);
return (int)SocketError.ConnectionAborted;
}
int errorCode = 0;
try
{
readCount = m_connectionSocket.Receive(buffer, bufferOffset, bufferSize, SocketFlags.None);
}
catch (SocketException ex)
{
errorCode = ex.ErrorCode;
DisposeSocket(ref m_connectionSocket, false);
m_connectionSocket = null;
}
return errorCode;
}
}
protected int Accept()
{
using (new AutoMutex(m_socketMutex))
using (new AutoMutex(m_connectionSocketMutex))
{
int errorCode = 0;
try
{
m_accepting = true;
m_connectionSocket = m_socket.Accept();
}
catch (SocketException ex)
{
errorCode = ex.ErrorCode;
}
catch (ObjectDisposedException)
{
// Socket was disposed while listening to incoming connections.
errorCode = -1;
}
finally
{
m_accepting = false;
}
return errorCode;
}
}
protected override void ConnectionTaskCallback()
{
if (m_socket != null)
{
if (!IsInitialiazed)
{
// Initialize the socket.
Connect();
}
if (IsInitialiazed)
{
if (m_connectionSocket == null)
{
Accept();
}
}
if (IsConnected)
{
if (m_connectionSocket != null)
{
Receive();
}
}
}
}
private void OnDisconnectionMessageReceived(object sender, MessageReceivedEventArgs message)
{
// The connection socket must be disposed.
DisposeSocket(ref m_connectionSocket, m_connectionSocketMutex);
m_connectionSocket = null;
}
}
}