using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace NETCoreServer
{
///
/// TCP server is used to connect, disconnect and manage TCP sessions
///
public class TcpServer
{
///
/// Initialize TCP server with a given IP address and port number
///
/// IP address
/// Port number
public TcpServer(IPAddress address, int port) : this(new IPEndPoint(address, port)) {}
///
/// Initialize TCP server with a given IP address and port number
///
/// IP address
/// Port number
public TcpServer(string address, int port) : this(new IPEndPoint(IPAddress.Parse(address), port)) {}
///
/// Initialize TCP server with a given IP endpoint
///
/// IP endpoint
public TcpServer(IPEndPoint endpoint) { Endpoint = endpoint; }
///
/// IP endpoint
///
public IPEndPoint Endpoint { get; internal set; }
///
/// Number of sessions connected to the server
///
public long ConnectedSessions { get; internal set; }
///
/// Number of bytes pending sent by the server
///
public long BytesPending { get; internal set; }
///
/// Number of bytes sent by the server
///
public long BytesSent { get; internal set; }
///
/// Number of bytes received by the server
///
public long BytesReceived { get; internal set; }
///
/// Option: keep alive
///
///
/// This option will setup SO_KEEPALIVE if the OS support this feature
///
public bool OptionKeepAlive { get; set; }
///
/// Option: no delay
///
///
/// This option will enable/disable Nagle's algorithm for TCP protocol
///
public bool OptionNoDelay { get; set; }
///
/// Option: reuse address
///
///
/// This option will enable/disable SO_REUSEADDR if the OS support this feature
///
public bool OptionReuseAddress { get; set; }
///
/// Option: reuse port
///
///
/// This option will enable/disable SO_REUSEPORT if the OS support this feature
///
public bool OptionReusePort { get; set; }
#region Start/Stop server
private int _acceptorBacklog = 1024;
private Socket _acceptorSocket;
private SocketAsyncEventArgs _acceptorEventArg;
///
/// Is the server started?
///
public bool IsStarted { get; private set; }
///
/// Is the server accepting new clients?
///
public bool IsAccepting { get; private set; }
///
/// Start the server
///
/// 'true' if the server was successfully started, 'false' if the server failed to start
public bool Start()
{
Debug.Assert(!IsStarted, "TCP server is already started!");
if (IsStarted)
return false;
// Create a new acceptor socket
_acceptorSocket = new Socket(Endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
// Bind the acceptor socket to the IP endpoint
_acceptorSocket.Bind(Endpoint);
// Start listen to the acceptor socket with the given accepting backlog size
_acceptorSocket.Listen(_acceptorBacklog);
// Reset statistic
BytesPending = 0;
BytesSent = 0;
BytesReceived = 0;
// Update the started flag
IsStarted = true;
// Call the server started handler
OnStarted();
// Perform the first server accept
IsAccepting = true;
_acceptorEventArg = new SocketAsyncEventArgs();
_acceptorEventArg.Completed += AcceptorEventArg_Completed;
StartAccept(_acceptorEventArg);
return true;
}
///
/// Stop the server
///
/// 'true' if the server was successfully stopped, 'false' if the server is already stopped
public bool Stop()
{
Debug.Assert(IsStarted, "TCP server is not started!");
if (!IsStarted)
return false;
// Stop accepting new clients
IsAccepting = false;
// Close the acceptor socket
_acceptorSocket.Close();
// Disconnect all sessions
DisconnectAll();
// Update the started flag
IsStarted = false;
// Clear multicast buffer
ClearBuffers();
// Call the server stopped handler
OnStopped();
return true;
}
///
/// Restart the server
///
/// 'true' if the server was successfully restarted, 'false' if the server failed to restart
public bool Restart()
{
if (!Stop())
return false;
while (IsStarted)
Thread.Yield();
return Start();
}
///
/// Handle server started notification
///
protected virtual void OnStarted() {}
///
/// Handle server stopped notification
///
protected virtual void OnStopped() {}
///
/// Handle error notification
///
/// Socket error code
protected virtual void OnError(SocketError error) {}
#endregion
#region Accepting clients
///
/// Start accept a new client connection
///
private void StartAccept(SocketAsyncEventArgs e)
{
// Socket must be cleared since the context object is being reused
e.AcceptSocket = null;
if (!_acceptorSocket.AcceptAsync(e))
ProcessAccept(e);
}
///
/// Process accepted client connection
///
private void ProcessAccept(SocketAsyncEventArgs e)
{
// Accept the next client connection
if (IsAccepting)
StartAccept(e);
}
///
/// This method is the callback method associated with Socket.AcceptAsync()
/// operations and is invoked when an accept operation is complete
///
private void AcceptorEventArg_Completed(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
}
#endregion
#region Multicasting
///
/// Clear multicast buffer
///
private void ClearBuffers()
{
}
#endregion
#region Session management
///
/// Disconnect all connected sessions
///
/// 'true' if all sessions were successfully disconnected, 'false' if the server is not started
public bool DisconnectAll()
{
return true;
}
#endregion
}
}