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.
|
|
#if !NET20
using Apewer; using System; using System.Collections.Generic; using System.Net;
namespace Apewer.WebSocket {
/// <summary></summary>
public sealed class GenericServer : IDisposable {
private Dictionary<int, Connection> _connections = new Dictionary<int, Connection>(); private WebSocketServer _server = null;
private bool _running = false; private IPAddress _address = null; private int _port = 0;
/// <summary></summary>
public event SocketEvent OnOpen;
/// <summary></summary>
public event SocketEvent OnClose;
/// <summary></summary>
public event SocketEvent<string> OnMessage;
/// <summary></summary>
public event SocketEvent<byte[]> OnBytes;
/// <summary></summary>
public event SocketEvent<byte[]> OnPing;
/// <summary></summary>
public event SocketEvent<byte[]> OnPong;
/// <summary></summary>
public event SocketEvent<Exception> OnError;
/// <summary></summary>
public event ServerEvent<Exception> Excepted;
#region Properties
/// <summary>正在监听。</summary>
public bool Running { get { return _running; } }
/// <summary>监听的地址。</summary>
public IPAddress Address { get { return _address; } }
/// <summary>监听的端口号。</summary>
public int Port { get { return _port; } }
/// <summary>当前 Sockets 数量。</summary>
public int Count { get { var count = 0; lock (_connections) { count = _connections.Count; } return count; } }
#endregion
#region Methods
/// <summary></summary>
public GenericServer() { }
/// <summary>关闭服务,并释放系统资源。</summary>
public void Dispose() { lock (_connections) { _connections.Clear(); } lock (_server) { if (_server != null) { _server.Dispose(); } } _server = null; _running = false; }
/// <summary>启动监听,如果监听正在运行则失败。在所有 IPv4 网络接口上自动选择可用的端口号。</summary>
/// <returns>已监听的端口号。</returns>
/// <exception cref="FormatException" />
/// <exception cref="InvalidOperationException" />
public void Start() => Start(0, null);
/// <summary>启动监听,如果监听正在运行则失败。</summary>
/// <param name="endPoint">要监听的终结点。指定为 NULL 时将在所有 IPv4 网络接口上自动选择可用的端口号。</param>
/// <returns>已监听的端口号。</returns>
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="FormatException" />
/// <exception cref="InvalidOperationException" />
public void Start(IPEndPoint endPoint) { if (endPoint == null) Start(0, null); else Start(endPoint.Port, endPoint.Address); }
/// <summary>启动监听,如果监听正在运行则失败。</summary>
/// <param name="port">要监听的端口号。指定为 0 时将自动选择可用的端口号。</param>
/// <param name="address">要监听的网络接口。指定为 NULL 时将在所有 IPv4 网络接口监听,等同于 <see cref="IPAddress.Any"/>。</param>
/// <returns>已监听的端口号。</returns>
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="FormatException" />
/// <exception cref="InvalidOperationException" />
public void Start(int port, IPAddress address = null) { if (address == null) address = IPAddress.Any; if (port < 0) throw new ArgumentOutOfRangeException(nameof(port)); if (port > 65535) throw new ArgumentOutOfRangeException(nameof(port)); if (_running) throw new InvalidOperationException("示例已经在运行中,无法再次启动。");
_address = address; _port = port;
var location = "ws://" + _address.ToString() + ":" + port.ToString(); _server = new WebSocketServer(location); _server.Start(Initialize); _port = _server.Port; _running = true; }
/// <summary>对所有连接发送文本。</summary>
/// <exception cref="InvalidOperationException"></exception>
public int Send(params char[] message) { var text = null as string; if (message == null || message.Length < 1) return 0; if (message.Length == 1) { text = message[0].ToString(); } else { var sb = new System.Text.StringBuilder(); foreach (var i in message) { if ((object)i != null) sb.Append(i); } text = sb.ToString(); } if (text.Length < 1) return 0;
var connections = GetConnections(); var count = 0; foreach (var connection in connections) { try { var sent = connection.Send(message); if (sent != null) count += 1; } catch (Exception exception) { RaiseExcepted(exception); } } return count; }
/// <summary>发送文本。</summary>
/// <exception cref="InvalidOperationException"></exception>
public int Send(params string[] message) { var text = null as string; if (message == null || message.Length < 1) return 0; if (message.Length == 1) { text = message[0].ToString(); } else { var sb = new System.Text.StringBuilder(); foreach (var i in message) { if (i != null) sb.Append(i); } text = sb.ToString(); } if (text.Length < 1) return 0;
var connections = GetConnections(); var count = 0; foreach (var connection in connections) { try { var sent = connection.Send(message); if (sent != null) count += 1; } catch (Exception exception) { RaiseExcepted(exception); } } return count; }
/// <summary>发送字节数组。</summary>
/// <exception cref="InvalidOperationException"></exception>
public int Send(params byte[] message) { var connections = GetConnections(); var count = 0; foreach (var connection in connections) { try { var sent = connection.Send(message); if (sent != null) count += 1; } catch (Exception exception) { RaiseExcepted(exception); } } return count; }
/// <summary>发送 PING。</summary>
/// <exception cref="InvalidOperationException"></exception>
public int Ping(params byte[] message) { var connections = GetConnections(); var count = 0; foreach (var connection in connections) { try { var sent = connection.Ping(message); if (sent != null) count += 1; } catch (Exception exception) { RaiseExcepted(exception); } } return count; }
/// <summary>发送 PONG。</summary>
/// <exception cref="InvalidOperationException"></exception>
public int Pong(params byte[] message) { var connections = GetConnections(); var count = 0; foreach (var connection in connections) { try { var sent = connection.Pong(message); if (sent != null) count += 1; } catch (Exception exception) { RaiseExcepted(exception); } } return count; }
/// <summary>关闭 Socket 连接。</summary>
public void Close() { lock (_server) { try { if (_server != null && _server.ListenerSocket != null) { _server.ListenerSocket.Close(); } } catch (Exception exception) { RaiseExcepted(exception); } } }
#endregion
#region Private
List<Connection> GetConnections() { var list = new List<Connection>(); lock (_connections) { foreach (var i in _connections.Values) { if (i != null) list.Add(i); } } return list; }
void RaiseExcepted(Exception exception) { if (exception == null) return; if (Excepted == null) return; try { InBackground(() => Excepted(this, exception)); } catch { } }
void Initialize(Connection socket) { if (socket == null) return;
socket.OnOpen = () => InBackground(() => { var hashcode = socket.GetHashCode(); lock (_connections) { if (!_connections.ContainsKey(hashcode)) { _connections.Add(hashcode, socket); } } try { OnOpen?.Invoke(socket); } catch (Exception exception) { RaiseExcepted(exception); } });
socket.OnClose = () => InBackground(() => { var hashcode = socket.GetHashCode(); try { OnClose?.Invoke(socket); } catch (Exception exception) { RaiseExcepted(exception); } });
socket.OnBytes = (content) => InBackground(() => { try { OnBytes?.Invoke(socket, content); } catch (Exception exception) { RaiseExcepted(exception); } });
socket.OnError = (content) => InBackground(() => { try { OnError?.Invoke(socket, content); } catch (Exception exception) { RaiseExcepted(exception); } });
socket.OnMessage = (content) => InBackground(() => { try { OnMessage?.Invoke(socket, content); } catch (Exception exception) { RaiseExcepted(exception); } });
socket.OnPing = (content) => InBackground(() => { try { if (OnPing == null) socket.Pong(content); else OnPing?.Invoke(socket, content); } catch (Exception exception) { RaiseExcepted(exception); } });
socket.OnPong = (content) => InBackground(() => { try { OnPong?.Invoke(socket, content); } catch (Exception exception) { RaiseExcepted(exception); } }); }
int GetHashCode(Connection connection) { if (connection == null) return 0; if (connection.ConnectionInfo == null) return 0; return connection.ConnectionInfo.Id.GetHashCode(); }
#endregion
#region static
/// <summary></summary>
public static LogLevel LogLevel { get { return WebSocketLog.Level; } set { WebSocketLog.Level = value; } }
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static void InBackground(System.Action action) { if (action == null) return; var thread = new System.Threading.Thread(delegate (object v) { ((System.Action)v)(); }); thread.IsBackground = true; thread.Start(action); }
#endregion
}
}
#endif
|