5 changed files with 158 additions and 775 deletions
@ -1,25 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Net.Sockets; |
|
||||
using System.Text; |
|
||||
|
|
||||
namespace Apewer |
|
||||
{ |
|
||||
|
|
||||
internal class TcpBuffer |
|
||||
{ |
|
||||
|
|
||||
// TCP 传输缓冲区大小。
|
|
||||
public const int Size = 8192; |
|
||||
|
|
||||
public Socket Socket; |
|
||||
public byte[] Buffer; |
|
||||
|
|
||||
public TcpBuffer() |
|
||||
{ |
|
||||
Buffer = new byte[Size]; |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,99 +0,0 @@ |
|||||
using Apewer.Network; |
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.IO; |
|
||||
using System.Net; |
|
||||
using System.Net.Sockets; |
|
||||
using System.Text; |
|
||||
using System.Threading; |
|
||||
|
|
||||
namespace Apewer |
|
||||
{ |
|
||||
|
|
||||
internal class TcpInstance |
|
||||
{ |
|
||||
|
|
||||
private TcpServer _parent; |
|
||||
private Socket _server; |
|
||||
private Socket _client; |
|
||||
private IPEndPoint _ep; |
|
||||
|
|
||||
private string _ip; |
|
||||
private int _port; |
|
||||
|
|
||||
private ManualResetEvent _done = new ManualResetEvent(false); |
|
||||
private int _length = 0; |
|
||||
private byte[] _data; |
|
||||
|
|
||||
public TcpInstance(TcpServer parent, Socket server, Socket client) |
|
||||
{ |
|
||||
_parent = parent; |
|
||||
_server = server; |
|
||||
_client = client; |
|
||||
_ep = (IPEndPoint)_client.RemoteEndPoint; |
|
||||
_ip = _ep.Address.ToString(); |
|
||||
_port = _ep.Port; |
|
||||
} |
|
||||
|
|
||||
public void Process() |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
var vm = new TcpBuffer(); |
|
||||
vm.Socket = _client; |
|
||||
vm.Socket.BeginReceive(vm.Buffer, 0, vm.Buffer.Length, SocketFlags.None, Callback, vm); |
|
||||
_done.WaitOne(); |
|
||||
} |
|
||||
catch (Exception argex) { _parent.RaiseExcepted(argex); } |
|
||||
} |
|
||||
|
|
||||
private void Callback(IAsyncResult ar) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
var buffer = (TcpBuffer)ar.AsyncState; |
|
||||
if (buffer.Socket.Connected) |
|
||||
{ |
|
||||
_length = buffer.Socket.EndReceive(ar); |
|
||||
if (_length > 0) |
|
||||
{ |
|
||||
_data = new byte[_length]; |
|
||||
Array.Copy(buffer.Buffer, 0, _data, 0, _length); |
|
||||
_parent.RaiseReceived(_ip, _port, _data); |
|
||||
buffer.Socket.BeginReceive(buffer.Buffer, 0, buffer.Buffer.Length, SocketFlags.None, Callback, buffer); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (_client != null) |
|
||||
{ |
|
||||
if (_client.Connected) _client.Close(); |
|
||||
_client = null; |
|
||||
} |
|
||||
if (_ep != null) |
|
||||
{ |
|
||||
_parent.Remove(_ip + "-" + _port.ToString()); |
|
||||
_parent.RaiseClosed(_ip, _port); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (!string.IsNullOrEmpty(_ip)) _parent.Close(_ip, _port); |
|
||||
} |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_parent.RaiseExcepted(ex); |
|
||||
if (_client != null) |
|
||||
{ |
|
||||
if (_client.Connected) _client.Close(); |
|
||||
_client = null; |
|
||||
} |
|
||||
_parent.Close(_ip, _port); |
|
||||
} |
|
||||
_done.Set(); |
|
||||
} |
|
||||
|
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,401 +1,171 @@ |
|||||
using System; |
using Apewer.Internals; |
||||
using System.Collections; |
using System; |
||||
using System.Text; |
using System.Collections.Generic; |
||||
using System.Net; |
using System.Net; |
||||
using System.Net.Sockets; |
using System.Net.Sockets; |
||||
using System.Threading; |
using System.Threading; |
||||
using System.IO; |
|
||||
using Apewer.Internals; |
|
||||
using Apewer; |
|
||||
|
|
||||
namespace Apewer.Network |
namespace Apewer.Network |
||||
{ |
{ |
||||
|
|
||||
/// <summary>TCP 服务端。</summary>
|
/// <summary>TCP 服务端。</summary>
|
||||
internal class TcpServer |
public sealed class TcpServer : IDisposable |
||||
{ |
{ |
||||
|
|
||||
#region event
|
Socket _server = null; |
||||
|
List<Socket> _clients = new List<Socket>(1024); |
||||
|
|
||||
/// <summary>Exception。</summary>
|
/// <summary>获取当前监听的端口。</summary>
|
||||
public Event<Exception> Excepted { get; set; } |
public int Port { get => GetLocalPort(); } |
||||
|
|
||||
/// <summary>服务端已启动。</summary>
|
/// <summary>启动服务端,并在所有接口上监听端口。</summary>
|
||||
public Event<SocketEndPoint> Started { get; set; } |
public TcpServer(int port) : this(IPAddress.Any, port) { } |
||||
|
|
||||
/// <summary>服务端已关闭。</summary>
|
/// <summary>启动服务端,并监听端口。</summary>
|
||||
public Event<SocketEndPoint> Quitted { get; set; } |
public TcpServer(string ip, int port) : this(IPAddress.Parse(ip), port) { } |
||||
|
|
||||
/// <summary>客户端已连接。</summary>
|
/// <summary>启动服务端,并监听端口。</summary>
|
||||
public Event<SocketEndPoint> Connected { get; set; } |
public TcpServer(IPAddress address, int port) : this(new IPEndPoint(address, port)) { } |
||||
|
|
||||
/// <summary>客户端已断开。</summary>
|
/// <summary>启动服务端,并监听端口。</summary>
|
||||
public Event<SocketEndPoint> Closed { get; set; } |
public TcpServer(IPEndPoint endpoint) |
||||
|
|
||||
/// <summary>已收到客户端数据。</summary>
|
|
||||
public Event<SocketReceived> Received { get; set; } |
|
||||
|
|
||||
#region raise
|
|
||||
|
|
||||
internal void RaiseExcepted(Exception exception) |
|
||||
{ |
{ |
||||
if (Excepted != null) Excepted(this, exception); |
_server = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); |
||||
|
_server.Bind(endpoint); |
||||
|
_server.Listen(500); |
||||
|
_server.SendTimeout = 5000; |
||||
} |
} |
||||
|
|
||||
internal void RaiseStarted() |
/// <summary>关闭连接,释放系统资源。</summary>
|
||||
|
public void Dispose() |
||||
{ |
{ |
||||
if (Started != null) |
lock (_clients) |
||||
{ |
{ |
||||
var ip = _endpoint == null ? "" : _endpoint.Address.ToString(); |
foreach (var client in _clients) |
||||
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; |
try { client.Disconnect(false); } catch { } |
||||
var cep = (IPEndPoint)_socket.LocalEndPoint; |
try { client.Close(100); } catch { } |
||||
_port = cep.Port; |
#if !NET20
|
||||
RaiseStarted(); |
try { client.Dispose(); } catch { } |
||||
Listener(); |
#endif
|
||||
return true; |
|
||||
} |
} |
||||
|
_clients.Clear(); |
||||
} |
} |
||||
catch (Exception ex) |
if (_server != null) |
||||
{ |
{ |
||||
_endpoint = null; |
try { _server.Disconnect(false); } catch { } |
||||
RaiseExcepted(ex); |
try { _server.Close(100); } catch { } |
||||
if ((_socket != null) && _socket.Connected) _socket.Close(); |
#if !NET20
|
||||
_state = false; |
try { _server.Dispose(); } catch { } |
||||
Quit(); |
#endif
|
||||
return false; |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
/// <summary>关闭服务端。</summary>
|
/// <summary>获取当前所有客户端连接。</summary>
|
||||
public void Quit() |
public Socket[] GetClients() |
||||
{ |
{ |
||||
Quit(true); |
lock (_clients) |
||||
} |
|
||||
|
|
||||
/// <summary>断开与所有客户端的连接。</summary>
|
|
||||
public void Close() |
|
||||
{ |
|
||||
if (_client != null) |
|
||||
{ |
{ |
||||
foreach (Socket socket in _client.Values) |
var array = _clients.ToArray(); |
||||
{ |
return array; |
||||
try |
|
||||
{ |
|
||||
var ep = (IPEndPoint)socket.RemoteEndPoint; |
|
||||
Close(ep.Address.ToString(), ep.Port); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
RaiseExcepted(ex); |
|
||||
} |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
/// <summary>断开与指定客户端的连接。</summary>
|
/// <summary>等待接收客户端连接。此方法将会阻塞当前线程直到关闭监听的端口。</summary>
|
||||
public void Close(string ip, int port) |
/// <param name="callback">处理客户端连接,当此回调结束时将释放此连接。</param>
|
||||
|
/// <exception cref="ArgumentNullException"></exception>
|
||||
|
public void Accept(Action<Socket> callback) |
||||
{ |
{ |
||||
if (port < 0) port = 0; |
if (callback == null) throw new ArgumentNullException(nameof(callback)); |
||||
if (port > 65535) port = 65535; |
|
||||
|
|
||||
if (!string.IsNullOrEmpty(ip)) |
while (true) |
||||
{ |
{ |
||||
var ck = ip + "-" + port.ToString(); |
var socket = null as Socket; |
||||
var cs = Client(ip, port); |
|
||||
try |
try |
||||
{ |
{ |
||||
if (cs != null) cs.Close(); |
socket = _server.Accept(); |
||||
Remove(ck); |
if (socket != null) |
||||
RaiseClosed(ip, port); |
{ |
||||
|
Callback.Enqueue(this, socket, callback); |
||||
|
} |
||||
} |
} |
||||
catch (Exception ex) |
catch |
||||
{ |
{ |
||||
RaiseExcepted(ex); |
if (socket != null) |
||||
|
{ |
||||
|
try { socket.Close(); } catch { } |
||||
|
try { socket.Disconnect(false); } catch { } |
||||
|
#if !NET20
|
||||
|
try { socket.Dispose(); } catch { } |
||||
|
#endif
|
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
/// <summary>向所有客户端广播数据。</summary>
|
int GetLocalPort() |
||||
public void Send(byte[] bytes) |
|
||||
{ |
{ |
||||
var length = bytes.Length; |
var socket = _server; |
||||
if ((_client.Count > 0) && (length > 0)) |
if (socket != null) |
||||
{ |
{ |
||||
foreach (Socket i in _client.Values) Send(bytes, i); |
var endpoint = socket.LocalEndPoint as IPEndPoint; |
||||
|
if (endpoint != null) return endpoint.Port; |
||||
} |
} |
||||
|
return 0; |
||||
} |
} |
||||
|
|
||||
/// <summary>向指定客户端发送数据。</summary>
|
sealed class Callback |
||||
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
|
TcpServer _server; |
||||
|
Socket _socket; |
||||
|
Action<Socket> _action; |
||||
|
|
||||
/// <summary>
|
public static void Enqueue(TcpServer server, Socket socket, Action<Socket> callback) |
||||
///
|
|
||||
/// </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(); |
var state = new Callback(); |
||||
_socket = null; |
state._server = server ?? throw new ArgumentNullException(nameof(server)); |
||||
} |
state._socket = socket ?? throw new ArgumentNullException(nameof(socket)); |
||||
if (_client != null) |
state._action = callback ?? throw new ArgumentNullException(nameof(callback)); |
||||
{ |
|
||||
_client.Clear(); |
|
||||
_client = null; |
|
||||
} |
|
||||
_endpoint = null; |
|
||||
if (@event) RaiseQuitted(); |
|
||||
} |
|
||||
|
|
||||
private void Close(Socket socket) |
ThreadPool.QueueUserWorkItem(InThreadPool, state); |
||||
{ |
|
||||
if (socket != null) |
|
||||
{ |
|
||||
try { socket.Close(); } |
|
||||
catch (Exception ex) { RaiseExcepted(ex); } |
|
||||
} |
} |
||||
} |
|
||||
|
|
||||
internal void Remove(string key) |
static void InThreadPool(object state) |
||||
{ |
|
||||
if ((_client != null) && (!string.IsNullOrEmpty(key))) |
|
||||
{ |
{ |
||||
if (_client.ContainsKey(key)) |
var instance = state as Callback; |
||||
|
if (instance == null) return; |
||||
|
if (instance._server == null) return; |
||||
|
if (instance._socket == null) return; |
||||
|
if (instance._action == null) return; |
||||
|
|
||||
|
var socket = instance._socket; |
||||
|
lock (instance._server._clients) |
||||
{ |
{ |
||||
try |
instance._server._clients.Add(instance._socket); |
||||
{ |
|
||||
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 |
try |
||||
{ |
{ |
||||
return (client.Send(bytes, length, SocketFlags.None) > 0) ? true : false; |
instance._action.Invoke(instance._socket); |
||||
} |
} |
||||
catch (Exception ex) { RaiseExcepted(ex); } |
catch |
||||
} |
{ } |
||||
return false; |
finally |
||||
} |
|
||||
|
|
||||
private void Listener() |
|
||||
{ |
|
||||
while (_socket != null) |
|
||||
{ |
|
||||
try |
|
||||
{ |
{ |
||||
var socket = _socket.Accept(); |
lock (instance._server._clients) |
||||
if (socket != null) |
|
||||
{ |
{ |
||||
var ep = (IPEndPoint)socket.RemoteEndPoint; |
instance._server._clients.Remove(instance._socket); |
||||
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); |
|
||||
} |
} |
||||
} |
try { socket.Disconnect(false); } catch { } |
||||
catch (Exception ex) |
try { socket.Close(100); } catch { } |
||||
{ |
#if !NET20
|
||||
RaiseExcepted(ex); |
try { socket.Dispose(); } catch { } |
||||
|
#endif
|
||||
} |
} |
||||
} |
} |
||||
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
|
|
||||
|
|
||||
} |
} |
||||
|
|
||||
} |
} |
||||
|
Loading…
Reference in new issue