Fix for issue #9 - 2

This commit is contained in:
sta 2012-10-26 14:58:50 +09:00
parent 2ae1d35d03
commit 746c883b82
71 changed files with 230 additions and 84 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using WebSocketSharp; using WebSocketSharp;
using WebSocketSharp.Server; using WebSocketSharp.Server;
@ -6,8 +7,7 @@ namespace Example2 {
public class Chat : WebSocketService public class Chat : WebSocketService
{ {
private static object _forNum = new object(); private static int _num = 0;
private static uint _num = 0;
private string _name; private string _name;
@ -18,12 +18,9 @@ namespace Example2 {
: "anon#" + getNum(); : "anon#" + getNum();
} }
private uint getNum() private int getNum()
{ {
lock (_forNum) return Interlocked.Increment(ref _num);
{
return ++_num;
}
} }
protected override void OnOpen(object sender, EventArgs e) protected override void OnOpen(object sender, EventArgs e)

Binary file not shown.

View File

@ -20,6 +20,7 @@ namespace Example2
//var wssv = new WebSocketServiceHost<Chat>(4649); //var wssv = new WebSocketServiceHost<Chat>(4649);
//var wssv = new WebSocketServiceHost<Chat>(4649, "/Chat"); //var wssv = new WebSocketServiceHost<Chat>(4649, "/Chat");
//var wssv = new WebSocketServiceHost<Chat>(4649, "/チャット"); //var wssv = new WebSocketServiceHost<Chat>(4649, "/チャット");
//wssv.Sweeped = false; // Stop the Sweep inactive session Timer.
wssv.Start(); wssv.Start();
Console.WriteLine( Console.WriteLine(
@ -27,12 +28,13 @@ namespace Example2
wssv.Uri, wssv.Address, wssv.Port); wssv.Uri, wssv.Address, wssv.Port);
*/ */
// Multi services server /// Multi services server
var wssv = new WebSocketServer(4649); var wssv = new WebSocketServer(4649);
wssv.AddService<Echo>("/Echo"); wssv.AddService<Echo>("/Echo");
wssv.AddService<Echo>("/エコー"); wssv.AddService<Echo>("/エコー");
wssv.AddService<Chat>("/Chat"); wssv.AddService<Chat>("/Chat");
wssv.AddService<Chat>("/チャット"); wssv.AddService<Chat>("/チャット");
//wssv.Sweeped = false; // Must be set after any AddService methods done.
wssv.Start(); wssv.Start();
Console.WriteLine( Console.WriteLine(

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using WebSocketSharp; using WebSocketSharp;
using WebSocketSharp.Server; using WebSocketSharp.Server;
@ -6,8 +7,7 @@ namespace Example3 {
public class Chat : WebSocketService public class Chat : WebSocketService
{ {
private static object _forNum = new object(); private static int _num = 0;
private static uint _num = 0;
private string _name; private string _name;
@ -18,12 +18,9 @@ namespace Example3 {
: "anon#" + getNum(); : "anon#" + getNum();
} }
private uint getNum() private int getNum()
{ {
lock (_forNum) return Interlocked.Increment(ref _num);
{
return ++_num;
}
} }
protected override void OnOpen(object sender, EventArgs e) protected override void OnOpen(object sender, EventArgs e)

Binary file not shown.

View File

@ -14,6 +14,7 @@ namespace Example3
_httpsv = new HttpServer(4649); _httpsv = new HttpServer(4649);
_httpsv.AddService<Echo>("/Echo"); _httpsv.AddService<Echo>("/Echo");
_httpsv.AddService<Chat>("/Chat"); _httpsv.AddService<Chat>("/Chat");
//_httpsv.Sweeped = false; // Must be set after any AddService methods done.
_httpsv.OnGet += (sender, e) => _httpsv.OnGet += (sender, e) =>
{ {

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,11 +1,6 @@
<Properties> <Properties>
<MonoDevelop.Ide.Workspace ActiveConfiguration="Debug_Ubuntu" /> <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug_Ubuntu" />
<MonoDevelop.Ide.Workbench ActiveDocument="websocket-sharp/WebSocket.cs"> <MonoDevelop.Ide.Workbench />
<Files>
<File FileName="websocket-sharp/WebSocket.cs" Line="810" Column="1" />
<File FileName="websocket-sharp/Ext.cs" Line="390" Column="19" />
</Files>
</MonoDevelop.Ide.Workbench>
<MonoDevelop.Ide.DebuggingService.Breakpoints> <MonoDevelop.Ide.DebuggingService.Breakpoints>
<BreakpointStore /> <BreakpointStore />
</MonoDevelop.Ide.DebuggingService.Breakpoints> </MonoDevelop.Ide.DebuggingService.Breakpoints>

View File

@ -63,12 +63,22 @@ namespace WebSocketSharp.Server {
#endregion #endregion
#region Property #region Properties
public int Port { public int Port {
get { return _port; } get { return _port; }
} }
public bool Sweeped {
get {
return _services.Sweeped;
}
set {
_services.Sweeped = value;
}
}
#endregion #endregion
#region Events #region Events

View File

@ -32,6 +32,7 @@ namespace WebSocketSharp.Server {
public interface IServiceHost { public interface IServiceHost {
bool Sweeped { get; set; }
void BindWebSocket(WebSocket socket); void BindWebSocket(WebSocket socket);
void Broadcast(string data); void Broadcast(string data);
void Start(); void Start();

View File

@ -36,6 +36,7 @@ namespace WebSocketSharp.Server {
#region Field #region Field
private Dictionary<string, IServiceHost> _services; private Dictionary<string, IServiceHost> _services;
private bool _sweeped;
#endregion #endregion
@ -44,11 +45,12 @@ namespace WebSocketSharp.Server {
public ServiceManager() public ServiceManager()
{ {
_services = new Dictionary<string, IServiceHost>(); _services = new Dictionary<string, IServiceHost>();
_sweeped = true;
} }
#endregion #endregion
#region Property #region Properties
public int Count { public int Count {
get { get {
@ -56,6 +58,21 @@ namespace WebSocketSharp.Server {
} }
} }
public bool Sweeped {
get {
return _sweeped;
}
set {
if (value ^ _sweeped)
{
_sweeped = value;
foreach (var svcHost in _services.Values)
svcHost.Sweeped = value;
}
}
}
#endregion #endregion
#region Public Methods #region Public Methods

View File

@ -28,6 +28,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Timers;
using WebSocketSharp.Frame; using WebSocketSharp.Frame;
namespace WebSocketSharp.Server { namespace WebSocketSharp.Server {
@ -36,8 +38,11 @@ namespace WebSocketSharp.Server {
#region Private Fields #region Private Fields
private bool _isStopped; private object _forSweep;
private volatile bool _isStopped;
private volatile bool _isSweeping;
private Dictionary<string, WebSocketService> _sessions; private Dictionary<string, WebSocketService> _sessions;
private Timer _sweepTimer;
private object _syncRoot; private object _syncRoot;
#endregion #endregion
@ -46,15 +51,32 @@ namespace WebSocketSharp.Server {
public SessionManager() public SessionManager()
{ {
_isStopped = false; _forSweep = new object();
_sessions = new Dictionary<string, WebSocketService>(); _isStopped = false;
_syncRoot = new object(); _isSweeping = false;
_sessions = new Dictionary<string, WebSocketService>();
_sweepTimer = new Timer(30 * 1000);
_sweepTimer.Elapsed += (sender, e) =>
{
Sweep();
};
_syncRoot = new object();
startSweepTimer();
} }
#endregion #endregion
#region Properties #region Properties
public IEnumerable<string> ActiveID {
get {
return from result in Broadping(String.Empty)
where result.Value
select result.Key;
}
}
public int Count { public int Count {
get { get {
lock (_syncRoot) lock (_syncRoot)
@ -64,6 +86,37 @@ namespace WebSocketSharp.Server {
} }
} }
public IEnumerable<string> InactiveID {
get {
return from result in Broadping(String.Empty)
where !result.Value
select result.Key;
}
}
public IEnumerable<string> ID {
get {
lock (_syncRoot)
{
return _sessions.Keys;
}
}
}
public bool Sweeped {
get {
return _sweepTimer.Enabled;
}
set {
if (value && !_isStopped)
startSweepTimer();
if (!value)
stopSweepTimer();
}
}
public object SyncRoot { public object SyncRoot {
get { get {
return _syncRoot; return _syncRoot;
@ -82,11 +135,23 @@ namespace WebSocketSharp.Server {
} }
} }
private string getNewID() private string createID()
{ {
return Guid.NewGuid().ToString("N"); return Guid.NewGuid().ToString("N");
} }
private void startSweepTimer()
{
if (!Sweeped)
_sweepTimer.Start();
}
private void stopSweepTimer()
{
if (Sweeped)
_sweepTimer.Stop();
}
#endregion #endregion
#region Public Methods #region Public Methods
@ -98,7 +163,7 @@ namespace WebSocketSharp.Server {
if (_isStopped) if (_isStopped)
return null; return null;
var id = getNewID(); var id = createID();
_sessions.Add(id, service); _sessions.Add(id, service);
return id; return id;
@ -110,7 +175,10 @@ namespace WebSocketSharp.Server {
lock (_syncRoot) lock (_syncRoot)
{ {
foreach (var service in _sessions.Values) foreach (var service in _sessions.Values)
service.SendAsync(data); if (_isStopped || _isSweeping)
service.Send(data);
else
service.SendAsync(data);
} }
} }
@ -119,7 +187,10 @@ namespace WebSocketSharp.Server {
lock (_syncRoot) lock (_syncRoot)
{ {
foreach (var service in _sessions.Values) foreach (var service in _sessions.Values)
service.SendAsync(data); if (_isStopped || _isSweeping)
service.Send(data);
else
service.SendAsync(data);
} }
} }
@ -132,21 +203,10 @@ namespace WebSocketSharp.Server {
return result; return result;
} }
public IEnumerable<string> GetIDs()
{
lock (_syncRoot)
{
return _sessions.Keys;
}
}
public bool Remove(string id) public bool Remove(string id)
{ {
lock (_syncRoot) lock (_syncRoot)
{ {
if (_isStopped)
return false;
return _sessions.Remove(id); return _sessions.Remove(id);
} }
} }
@ -166,16 +226,43 @@ namespace WebSocketSharp.Server {
public void Stop(CloseStatusCode code, string reason) public void Stop(CloseStatusCode code, string reason)
{ {
stopSweepTimer();
lock (_syncRoot) lock (_syncRoot)
{ {
if (_isStopped) if (_isStopped)
return; return;
_isStopped = true; _isStopped = true;
foreach (var service in _sessions.Values) foreach (var service in copySessions().Values)
service.Stop(code, reason); service.Stop(code, reason);
}
}
_sessions.Clear(); public void Sweep()
{
if (_isStopped || _isSweeping || Count == 0)
return;
lock (_forSweep)
{
_isSweeping = true;
foreach (var id in InactiveID)
{
lock (_syncRoot)
{
if (_isStopped)
{
_isSweeping = false;
return;
}
WebSocketService service;
if (TryGetByID(id, out service))
service.Stop(CloseStatusCode.ABNORMAL, String.Empty);
}
}
_isSweeping = false;
} }
} }

View File

@ -74,6 +74,20 @@ namespace WebSocketSharp.Server {
#endregion #endregion
#region Property
public bool Sweeped {
get {
return _services.Sweeped;
}
set {
_services.Sweeped = value;
}
}
#endregion
#region Private Method #region Private Method
private void init() private void init()

View File

@ -47,9 +47,8 @@ namespace WebSocketSharp.Server {
public WebSocketService() public WebSocketService()
{ {
ID = String.Empty; ID = String.Empty;
IsBound = false; IsBound = false;
IsStopped = false;
} }
#endregion #endregion
@ -72,9 +71,8 @@ namespace WebSocketSharp.Server {
#region Public Properties #region Public Properties
public string ID { get; private set; } public string ID { get; private set; }
public bool IsBound { get; private set; } public bool IsBound { get; private set; }
public bool IsStopped { get; private set; }
#endregion #endregion
@ -89,8 +87,7 @@ namespace WebSocketSharp.Server {
_socket.OnClose += (sender, e) => _socket.OnClose += (sender, e) =>
{ {
if (!IsStopped) _sessions.Remove(ID);
_sessions.Remove(ID);
}; };
} }
@ -202,6 +199,7 @@ namespace WebSocketSharp.Server {
{ {
Send(data); Send(data);
}; };
ThreadPool.QueueUserWorkItem(sendCb); ThreadPool.QueueUserWorkItem(sendCb);
} }
@ -211,6 +209,7 @@ namespace WebSocketSharp.Server {
{ {
Send(data); Send(data);
}; };
ThreadPool.QueueUserWorkItem(sendCb); ThreadPool.QueueUserWorkItem(sendCb);
} }
@ -242,15 +241,22 @@ namespace WebSocketSharp.Server {
public void Stop() public void Stop()
{ {
Stop(CloseStatusCode.NORMAL, String.Empty); if (!IsBound)
return;
_socket.Close();
} }
public void Stop(CloseStatusCode code, string reason) public void Stop(CloseStatusCode code, string reason)
{ {
if (!IsBound || IsStopped) Stop((ushort)code, reason);
}
public void Stop(ushort code, string reason)
{
if (!IsBound)
return; return;
IsStopped = true;
_socket.Close(code, reason); _socket.Close(code, reason);
} }

View File

@ -79,7 +79,17 @@ namespace WebSocketSharp.Server {
#endregion #endregion
#region Property #region Properties
public bool Sweeped {
get {
return _sessions.Sweeped;
}
set {
_sessions.Sweeped = value;
}
}
public Uri Uri { public Uri Uri {
get { get {

View File

@ -418,19 +418,19 @@ namespace WebSocketSharp {
return; return;
} }
// Whether a close status code that must not be set for send is used ?
if (!canSendAsCloseFrame(data))
{
onClose(new CloseEventArgs(data));
return;
}
_readyState = WsState.CLOSING; _readyState = WsState.CLOSING;
} }
// Whether a close status code that must not be set for send is used ?
if (!canSendAsCloseFrame(data))
{
onClose(new CloseEventArgs(data));
return;
}
closeHandshake(data); closeHandshake(data);
#if DEBUG #if DEBUG
Console.WriteLine("WS: Info@close: Exit close method."); Console.WriteLine("WS: Info@close: Exits close method.");
#endif #endif
} }
@ -451,7 +451,7 @@ namespace WebSocketSharp {
var payloadData = new PayloadData(data.ToArray()); var payloadData = new PayloadData(data.ToArray());
if (payloadData.Length > 125) if (payloadData.Length > 125)
{ {
var msg = "Close frame must have a payload length of 125 bytes or less."; var msg = "A Close frame must have a payload length of 125 bytes or less.";
onError(msg); onError(msg);
return; return;
} }
@ -497,10 +497,7 @@ namespace WebSocketSharp {
{ {
var args = new CloseEventArgs(data); var args = new CloseEventArgs(data);
var frame = createFrame(Fin.FINAL, Opcode.CLOSE, data); var frame = createFrame(Fin.FINAL, Opcode.CLOSE, data);
if (send(frame) && !Thread.CurrentThread.IsBackground) send(frame);
if (!_exitMessageLoop.IsNull())
_exitMessageLoop.WaitOne(5 * 1000);
onClose(args); onClose(args);
} }
@ -738,6 +735,10 @@ namespace WebSocketSharp {
private void onClose(CloseEventArgs eventArgs) private void onClose(CloseEventArgs eventArgs)
{ {
if (!Thread.CurrentThread.IsBackground)
if (!_exitMessageLoop.IsNull())
_exitMessageLoop.WaitOne(5 * 1000);
if (closeConnection()) if (closeConnection())
eventArgs.WasClean = true; eventArgs.WasClean = true;
@ -772,7 +773,7 @@ namespace WebSocketSharp {
var buffer = Encoding.UTF8.GetBytes(message); var buffer = Encoding.UTF8.GetBytes(message);
if (buffer.Length > 125) if (buffer.Length > 125)
{ {
var msg = "Ping frame must have a payload length of 125 bytes or less."; var msg = "A Ping frame must have a payload length of 125 bytes or less.";
onError(msg); onError(msg);
return false; return false;
} }
@ -800,7 +801,7 @@ namespace WebSocketSharp {
var frame = _wsStream.ReadFrame(); var frame = _wsStream.ReadFrame();
if (frame.IsNull()) if (frame.IsNull())
{ {
var msg = "WebSocket data frame can not be read from network stream."; var msg = "The WebSocket frame can not be read from network stream.";
close(CloseStatusCode.ABNORMAL, msg); close(CloseStatusCode.ABNORMAL, msg);
} }
@ -846,7 +847,7 @@ namespace WebSocketSharp {
if (frame.Opcode == Opcode.CLOSE) if (frame.Opcode == Opcode.CLOSE)
{// FINAL & CLOSE {// FINAL & CLOSE
#if DEBUG #if DEBUG
Console.WriteLine("WS: Info@receive: Start closing handshake."); Console.WriteLine("WS: Info@receive: Starts closing handshake.");
#endif #endif
close(frame.PayloadData); close(frame.PayloadData);
return null; return null;
@ -855,7 +856,7 @@ namespace WebSocketSharp {
if (frame.Opcode == Opcode.PING) if (frame.Opcode == Opcode.PING)
{// FINAL & PING {// FINAL & PING
#if DEBUG #if DEBUG
Console.WriteLine("WS: Info@receive: Return Pong."); Console.WriteLine("WS: Info@receive: Returns Pong.");
#endif #endif
pong(frame.PayloadData); pong(frame.PayloadData);
return null; return null;
@ -863,10 +864,14 @@ namespace WebSocketSharp {
if (frame.Opcode == Opcode.PONG) if (frame.Opcode == Opcode.PONG)
{// FINAL & PONG {// FINAL & PONG
#if DEBUG
Console.WriteLine("WS: Info@receive: Receives Pong.");
#endif
_receivePong.Set(); _receivePong.Set();
return null;
} }
// FINAL & (TEXT | BINARY | PONG) // FINAL & (TEXT | BINARY)
return new MessageEventArgs(frame.Opcode, frame.PayloadData); return new MessageEventArgs(frame.Opcode, frame.PayloadData);
} }
@ -889,7 +894,7 @@ namespace WebSocketSharp {
} }
#if DEBUG #if DEBUG
Console.WriteLine("WS: Info@receiveFragmented: Start closing handshake."); Console.WriteLine("WS: Info@receiveFragmented: Starts closing handshake.");
#endif #endif
close(CloseStatusCode.INCORRECT_DATA, String.Empty); close(CloseStatusCode.INCORRECT_DATA, String.Empty);
return null; return null;
@ -904,7 +909,7 @@ namespace WebSocketSharp {
if (frame.Opcode == Opcode.CLOSE) if (frame.Opcode == Opcode.CLOSE)
{// FINAL & CLOSE {// FINAL & CLOSE
#if DEBUG #if DEBUG
Console.WriteLine("WS: Info@receiveFragmented: Start closing handshake."); Console.WriteLine("WS: Info@receiveFragmented: Starts closing handshake.");
#endif #endif
close(frame.PayloadData); close(frame.PayloadData);
return null; return null;
@ -913,7 +918,7 @@ namespace WebSocketSharp {
if (frame.Opcode == Opcode.PING) if (frame.Opcode == Opcode.PING)
{// FINAL & PING {// FINAL & PING
#if DEBUG #if DEBUG
Console.WriteLine("WS: Info@receiveFragmented: Return Pong."); Console.WriteLine("WS: Info@receiveFragmented: Returns Pong.");
#endif #endif
pong(frame.PayloadData); pong(frame.PayloadData);
continue; continue;
@ -921,14 +926,16 @@ namespace WebSocketSharp {
if (frame.Opcode == Opcode.PONG) if (frame.Opcode == Opcode.PONG)
{// FINAL & PONG {// FINAL & PONG
#if DEBUG
Console.WriteLine("WS: Info@receiveFragmented: Receives Pong.");
#endif
_receivePong.Set(); _receivePong.Set();
OnMessage.Emit(this, new MessageEventArgs(frame.Opcode, frame.PayloadData));
continue; continue;
} }
// FINAL & (TEXT | BINARY) // FINAL & (TEXT | BINARY)
#if DEBUG #if DEBUG
Console.WriteLine("WS: Info@receiveFragmented: Start closing handshake."); Console.WriteLine("WS: Info@receiveFragmented: Starts closing handshake.");
#endif #endif
close(CloseStatusCode.INCORRECT_DATA, String.Empty); close(CloseStatusCode.INCORRECT_DATA, String.Empty);
return null; return null;
@ -964,7 +971,7 @@ namespace WebSocketSharp {
if (_readyState == WsState.CONNECTING || if (_readyState == WsState.CONNECTING ||
_readyState == WsState.CLOSED) _readyState == WsState.CLOSED)
{ {
var msg = "Connection isn't established or has been closed."; var msg = "The WebSocket connection isn't established or has been closed.";
onError(msg); onError(msg);
return false; return false;
} }
@ -1011,7 +1018,7 @@ namespace WebSocketSharp {
{ {
if (_readyState != WsState.OPEN) if (_readyState != WsState.OPEN)
{ {
var msg = "Connection isn't established or has been closed."; var msg = "The WebSocket connection isn't established or has been closed.";
onError(msg); onError(msg);
return; return;
} }
@ -1215,7 +1222,7 @@ namespace WebSocketSharp {
{ {
if (_readyState == WsState.OPEN) if (_readyState == WsState.OPEN)
{ {
Console.WriteLine("WS: Info@Connect: Connection has been established already."); Console.WriteLine("WS: Info@Connect: The WebSocket connection has been established already.");
return; return;
} }
@ -1278,7 +1285,9 @@ namespace WebSocketSharp {
if (message.IsNull()) if (message.IsNull())
message = String.Empty; message = String.Empty;
return ping(message, 5 * 1000); return _isClient
? ping(message, 5 * 1000)
: ping(message, 1 * 1000);
} }
/// <summary> /// <summary>

Binary file not shown.