274 lines
9.7 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|