#region MIT License /** * WebSocketServerBase.cs * * The MIT License * * Copyright (c) 2012 sta.blockhead * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #endregion using System; using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Threading; namespace WebSocketSharp.Server { public abstract class WebSocketServerBase { #region Fields private Thread _acceptClientThread; private IPAddress _address; private bool _isSecure; private bool _isSelfHost; private int _port; private TcpListener _tcpListener; private Uri _uri; #endregion #region Constructors protected WebSocketServerBase() { _isSelfHost = false; } protected WebSocketServerBase(string url) { if (url == null) throw new ArgumentNullException("url"); Uri uri; string msg; if (!tryCreateUri(url, out uri, out msg)) throw new ArgumentException(msg, "url"); init(uri); } protected WebSocketServerBase(IPAddress address, int port, string absPath, bool secure) { if (address == null) throw new ArgumentNullException("address"); if (absPath == null) throw new ArgumentNullException("absPath"); string msg; if (!Ext.IsValidAbsolutePath(absPath, out msg)) throw new ArgumentException(msg, "absPath"); if ((port == 80 && secure) || (port == 443 && !secure)) { msg = String.Format( "Invalid pair of 'port' and 'secure': {0}, {1}", port, secure); throw new ArgumentException(msg); } _address = address; _port = port > 0 ? port : secure ? 443 : 80; _uri = Ext.ToUri(absPath); _isSecure = secure; init(); } #endregion #region Protected Property protected Uri BaseUri { get { return _uri; } set { _uri = value; } } #endregion #region Public Properties public IPAddress Address { get { return _address; } } public bool IsSecure { get { return _isSecure; } } public bool IsSelfHost { get { return _isSelfHost; } } public int Port { get { return _port; } } #endregion #region Events public event EventHandler OnError; #endregion #region Private Methods private void acceptClient() { while (true) { try { var client = _tcpListener.AcceptTcpClient(); acceptSocketAsync(client); } catch (SocketException) { // TcpListener has been stopped. break; } catch (Exception ex) { onError(ex.Message); break; } } } private void acceptSocketAsync(TcpClient client) { WaitCallback acceptSocketCb = (state) => { try { AcceptWebSocket(client); } catch (Exception ex) { onError(ex.Message); } }; ThreadPool.QueueUserWorkItem(acceptSocketCb); } private void init() { _tcpListener = new TcpListener(_address, _port); _isSelfHost = true; } private void init(Uri uri) { var scheme = uri.Scheme; var host = uri.DnsSafeHost; var port = uri.Port; var addrs = Dns.GetHostAddresses(host); _uri = uri; _address = addrs[0]; _isSecure = scheme == "wss" ? true : false; _port = port > 0 ? port : _isSecure ? 443 : 80; init(); } private void onError(string message) { #if DEBUG var callerFrame = new StackFrame(1); var caller = callerFrame.GetMethod(); Console.WriteLine("WSSV: Error@{0}: {1}", caller.Name, message); #endif Ext.Emit(OnError, this, new ErrorEventArgs(message)); } private void startAcceptClientThread() { _acceptClientThread = new Thread(new ThreadStart(acceptClient)); _acceptClientThread.IsBackground = true; _acceptClientThread.Start(); } private bool tryCreateUri(string uriString, out Uri result, out string message) { if (!Ext.TryCreateWebSocketUri(uriString, out result, out message)) return false; if (!Ext.IsNullOrEmpty(result.Query)) { result = null; message = "Must not contain the query component: " + uriString; return false; } return true; } #endregion #region Protected Methods protected abstract void AcceptWebSocket(TcpClient client); protected virtual void Error(string message) { onError(message); } #endregion #region Public Methods public virtual void Start() { if (!_isSelfHost) return; _tcpListener.Start(); startAcceptClientThread(); } public virtual void Stop() { if (!_isSelfHost) return; _tcpListener.Stop(); _acceptClientThread.Join(5 * 1000); } #endregion } }