You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
11 KiB
401 lines
11 KiB
using System;
|
|
using System.Collections;
|
|
using System.Text;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
using System.IO;
|
|
using Apewer.Internals;
|
|
using Apewer;
|
|
|
|
namespace Apewer.Network
|
|
{
|
|
|
|
/// <summary>TCP 服务端。</summary>
|
|
internal class TcpServer
|
|
{
|
|
|
|
#region event
|
|
|
|
/// <summary>Exception。</summary>
|
|
public Event<Exception> Excepted { get; set; }
|
|
|
|
/// <summary>服务端已启动。</summary>
|
|
public Event<SocketEndPoint> Started { get; set; }
|
|
|
|
/// <summary>服务端已关闭。</summary>
|
|
public Event<SocketEndPoint> Quitted { get; set; }
|
|
|
|
/// <summary>客户端已连接。</summary>
|
|
public Event<SocketEndPoint> Connected { get; set; }
|
|
|
|
/// <summary>客户端已断开。</summary>
|
|
public Event<SocketEndPoint> Closed { get; set; }
|
|
|
|
/// <summary>已收到客户端数据。</summary>
|
|
public Event<SocketReceived> Received { get; set; }
|
|
|
|
#region raise
|
|
|
|
internal void RaiseExcepted(Exception exception)
|
|
{
|
|
if (Excepted != null) Excepted(this, exception);
|
|
}
|
|
|
|
internal void RaiseStarted()
|
|
{
|
|
if (Started != null)
|
|
{
|
|
var ip = _endpoint == null ? "" : _endpoint.Address.ToString();
|
|
var port = _endpoint == null ? 0 : _endpoint.Port;
|
|
Started?.Invoke(this, new SocketEndPoint(ip, port));
|
|
}
|
|
}
|
|
|
|
internal void RaiseConnected(string ip, int port)
|
|
{
|
|
Connected?.Invoke(this, new SocketEndPoint(ip, port));
|
|
}
|
|
|
|
internal void RaiseClosed(string ip, int port)
|
|
{
|
|
Closed?.Invoke(this, new SocketEndPoint(ip, port));
|
|
}
|
|
|
|
internal void RaiseQuitted()
|
|
{
|
|
var quitted = Quitted;
|
|
if (quitted != null)
|
|
{
|
|
var ip = _endpoint == null ? "" : _endpoint.Address.ToString();
|
|
var port = _endpoint == null ? 0 : _endpoint.Port;
|
|
quitted(this, new SocketEndPoint(ip, port));
|
|
}
|
|
}
|
|
|
|
internal void RaiseReceived(string ip, int port, byte[] bytes)
|
|
{
|
|
Received?.Invoke(this, new SocketReceived(ip, port, bytes));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region definition
|
|
|
|
private Socket _socket = null;
|
|
private Thread _listener = null;
|
|
private SortedList _client = null;
|
|
private IPEndPoint _endpoint = null;
|
|
|
|
private int _port = 0;
|
|
private int _max = 0;
|
|
private int _timeout = 0;
|
|
private bool _state = false;
|
|
private bool _background = true;
|
|
|
|
/// <summary>构造函数。</summary>
|
|
public TcpServer(int port = 0)
|
|
{
|
|
Port = port;
|
|
Max = 1000;
|
|
Timeout = 1000;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region accessor
|
|
|
|
/// <summary>获取或设置监听线程是否为后台线程,默认为“是”。</summary>
|
|
public bool Background
|
|
{
|
|
get { return _background; }
|
|
set
|
|
{
|
|
_background = value;
|
|
try { if (_listener != null) _listener.IsBackground = value; }
|
|
catch (Exception ex) { RaiseExcepted(ex); }
|
|
}
|
|
}
|
|
|
|
/// <summary>端口。</summary>
|
|
public int Port
|
|
{
|
|
get
|
|
{
|
|
int port = _port;
|
|
if (port > 65535) port = 65535;
|
|
if (port < 0) port = 0;
|
|
return port;
|
|
}
|
|
set
|
|
{
|
|
int port = value;
|
|
if (port < 0) port = 0;
|
|
if (port > 65535) port = 65535;
|
|
_port = port;
|
|
}
|
|
}
|
|
|
|
/// <summary>最大客户端数量。</summary>
|
|
public int Max
|
|
{
|
|
get { return _max; }
|
|
set { _max = (value > 0) ? value : 0; }
|
|
}
|
|
|
|
/// <summary>发送数据和接收数据的超时时间。</summary>
|
|
public int Timeout
|
|
{
|
|
get { return _timeout; }
|
|
set { _timeout = (value > 0) ? value : 0; }
|
|
}
|
|
|
|
/// <summary>服务端正在运行。</summary>
|
|
public bool Alive
|
|
{
|
|
get { return (_listener != null) ? _listener.IsAlive : false; }
|
|
}
|
|
|
|
/// <summary>已连接的客户端数量。</summary>
|
|
public int Count
|
|
{
|
|
get { return (_client != null) ? _client.Count : 0; }
|
|
}
|
|
|
|
/// <summary>启动服务端。</summary>
|
|
public bool Start(bool inBackground = true)
|
|
{
|
|
try
|
|
{
|
|
_endpoint = new IPEndPoint(IPAddress.Any, Port);
|
|
_client = new SortedList();
|
|
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
_socket.Bind(_endpoint);
|
|
_socket.Listen(Max);
|
|
_socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.AcceptConnection, 1);
|
|
_socket.ReceiveTimeout = Timeout;
|
|
_socket.SendTimeout = Timeout;
|
|
|
|
if (inBackground)
|
|
{
|
|
_listener = new Thread(Listener);
|
|
_listener.IsBackground = Background;
|
|
_listener.Start();
|
|
_state = _listener.IsAlive;
|
|
var cep = (IPEndPoint)_socket.LocalEndPoint;
|
|
_port = cep.Port;
|
|
RaiseStarted();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
_state = true;
|
|
var cep = (IPEndPoint)_socket.LocalEndPoint;
|
|
_port = cep.Port;
|
|
RaiseStarted();
|
|
Listener();
|
|
return true;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_endpoint = null;
|
|
RaiseExcepted(ex);
|
|
if ((_socket != null) && _socket.Connected) _socket.Close();
|
|
_state = false;
|
|
Quit();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>关闭服务端。</summary>
|
|
public void Quit()
|
|
{
|
|
Quit(true);
|
|
}
|
|
|
|
/// <summary>断开与所有客户端的连接。</summary>
|
|
public void Close()
|
|
{
|
|
if (_client != null)
|
|
{
|
|
foreach (Socket socket in _client.Values)
|
|
{
|
|
try
|
|
{
|
|
var ep = (IPEndPoint)socket.RemoteEndPoint;
|
|
Close(ep.Address.ToString(), ep.Port);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RaiseExcepted(ex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>断开与指定客户端的连接。</summary>
|
|
public void Close(string ip, int port)
|
|
{
|
|
if (port < 0) port = 0;
|
|
if (port > 65535) port = 65535;
|
|
|
|
if (!string.IsNullOrEmpty(ip))
|
|
{
|
|
var ck = ip + "-" + port.ToString();
|
|
var cs = Client(ip, port);
|
|
try
|
|
{
|
|
if (cs != null) cs.Close();
|
|
Remove(ck);
|
|
RaiseClosed(ip, port);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RaiseExcepted(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>向所有客户端广播数据。</summary>
|
|
public void Send(byte[] bytes)
|
|
{
|
|
var length = bytes.Length;
|
|
if ((_client.Count > 0) && (length > 0))
|
|
{
|
|
foreach (Socket i in _client.Values) Send(bytes, i);
|
|
}
|
|
}
|
|
|
|
/// <summary>向指定客户端发送数据。</summary>
|
|
public bool Send(byte[] bytes, string ip, int port)
|
|
{
|
|
if (port < 0) port = 0;
|
|
if (port > 65535) port = 65535;
|
|
|
|
if (string.IsNullOrEmpty(ip)) return false;
|
|
return Send(bytes, Client(ip, port));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region logic
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="event"></param>
|
|
private void Quit(bool @event)
|
|
{
|
|
Close();
|
|
if (_listener != null)
|
|
{
|
|
if (_listener.IsAlive) _listener.Abort();
|
|
_listener = null;
|
|
}
|
|
if (_socket != null)
|
|
{
|
|
_socket.Close();
|
|
_socket = null;
|
|
}
|
|
if (_client != null)
|
|
{
|
|
_client.Clear();
|
|
_client = null;
|
|
}
|
|
_endpoint = null;
|
|
if (@event) RaiseQuitted();
|
|
}
|
|
|
|
private void Close(Socket socket)
|
|
{
|
|
if (socket != null)
|
|
{
|
|
try { socket.Close(); }
|
|
catch (Exception ex) { RaiseExcepted(ex); }
|
|
}
|
|
}
|
|
|
|
internal void Remove(string key)
|
|
{
|
|
if ((_client != null) && (!string.IsNullOrEmpty(key)))
|
|
{
|
|
if (_client.ContainsKey(key))
|
|
{
|
|
try
|
|
{
|
|
Close((Socket)_client[key]);
|
|
_client.Remove(key);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RaiseExcepted(ex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool Send(byte[] bytes, Socket client)
|
|
{
|
|
var length = bytes.Length;
|
|
if ((client != null) && (length > 0))
|
|
{
|
|
try
|
|
{
|
|
return (client.Send(bytes, length, SocketFlags.None) > 0) ? true : false;
|
|
}
|
|
catch (Exception ex) { RaiseExcepted(ex); }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void Listener()
|
|
{
|
|
while (_socket != null)
|
|
{
|
|
try
|
|
{
|
|
var socket = _socket.Accept();
|
|
if (socket != null)
|
|
{
|
|
var ep = (IPEndPoint)socket.RemoteEndPoint;
|
|
var key = ep.Address.ToString() + "-" + ep.Port.ToString();
|
|
_client.Add(key, socket);
|
|
var instance = new TcpInstance(this, _socket, socket);
|
|
var thread = new Thread(instance.Process);
|
|
thread.IsBackground = true;
|
|
thread.Name = key;
|
|
thread.Start();
|
|
RaiseConnected(ep.Address.ToString(), ep.Port);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RaiseExcepted(ex);
|
|
}
|
|
}
|
|
Quit(false);
|
|
}
|
|
|
|
private Socket Client(string ip, int port)
|
|
{
|
|
if (port < 0) port = 0;
|
|
if (port > 65535) port = 65535;
|
|
try
|
|
{
|
|
var ck = ip + "-" + port.ToString();
|
|
if (_client.ContainsKey(ck)) return (Socket)_client[ck];
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
RaiseExcepted(ex);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|
|
|