Created
February 8, 2012 23:57
-
-
Save zahardzhan/1775601 to your computer and use it in GitHub Desktop.
network actor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Net.Sockets; | |
namespace T | |
{ | |
///<summary> | |
/// Сетевой актор подключается к серверу и пересылает сообщения. При обрыве связи самостоятельно | |
/// её восстанавливает. | |
/// </summary> | |
public class Network : Actor | |
{ | |
static readonly Log log = new Log(typeof(Network)); | |
public readonly HostAddress Address1, Address2; | |
volatile bool isConnected; | |
/// <summary> | |
/// Возвращает true, если есть подключение к серверу. | |
/// </summary> | |
public bool IsConnected | |
{ | |
get { return IsAlive && isConnected; } | |
} | |
public event EventHandler<NetworkMessageStream.ConnectedEventArgs> Connected; | |
public event EventHandler<NetworkMessageStream.MessageReceivedEventArgs> MessageReceived; | |
public event EventHandler<NetworkMessageStream.MessageTransmittedEventArgs> MessageTransmitted; | |
public event EventHandler<NetworkMessageStream.DisconnectedEventArgs> Disconnected; | |
public struct Disconnect { public Exception Exception; } | |
public Network(HostAddress address1, HostAddress address2) | |
: base() | |
{ | |
Address1 = address1; | |
Address2 = address2; | |
} | |
class Connection : Actor | |
{ | |
static readonly Log log = new Log(typeof(Network.Connection)); | |
public readonly Network Network; | |
public readonly HostAddress Address; | |
public Connection(Network network, HostAddress address) | |
: base() | |
{ | |
Network = network; | |
Address = address; | |
} | |
/// <summary> | |
/// Команда на установку соединения с сервером. | |
/// </summary> | |
internal struct Connect | |
{ | |
/// <summary> | |
/// Время за которое соединение должно установиться. | |
/// </summary> | |
public static readonly int Timeout = 10000; | |
} | |
/// <summary> | |
/// Команда разрыва соединения с сервером. | |
/// </summary> | |
internal struct Disconnect { } | |
internal struct Connected { public Connection Connection; public TcpClient Client; } | |
public override void Act() | |
{ | |
object message; | |
while (true) | |
{ | |
// Ждем прихода команды соединения с сервером. | |
do message = Receive<object>(); while (message is Disconnect); | |
// Подключаемся к серверу до тех пор пока не придет команда разрыва соединения с сервером. | |
while (message is Connect) | |
{ | |
try | |
{ | |
using (var client = new TcpClient(Address.Hostname, Address.Port)) | |
{ | |
Network.Send(new Connected { Connection = this, Client = client }); | |
message = Receive<object>(); | |
} | |
} | |
catch (Exception e) | |
{ | |
log.Trace("{0}", e.Message); | |
log.Trace("стэк: {0}", e.StackTrace); | |
// В случае сбоя ожидаем команды разрыва соединения в течении одной секунды. | |
var emergentDisconnect = Receive<Disconnect>(1000); | |
if (emergentDisconnect.HasValue) break; | |
else continue; | |
} | |
} | |
} | |
} | |
} | |
public override void Act() | |
{ | |
try | |
{ | |
log.Debug("запуск сетевого актора"); | |
while (true) | |
{ | |
// Сообщение о причине разрыва соединения. | |
var disconnect = new Network.Disconnect { Exception = new Exception("соединение разорвано по неизвестной причине") }; | |
try | |
{ | |
using (var connection1 = new Connection(this, Address1)) | |
using (var connection2 = new Connection(this, Address2)) | |
{ | |
connection1.Startup(); | |
connection2.Startup(); | |
connection1.Send(new Connection.Connect { }); | |
connection2.Send(new Connection.Connect { }); | |
var connected = Receive<Connection.Connected>(Connection.Connect.Timeout); | |
if (connected.HasValue) | |
{ | |
if (connected.Value.Connection == connection1) connection2.Send(new Connection.Disconnect { }); | |
if (connected.Value.Connection == connection2) connection1.Send(new Connection.Disconnect { }); | |
using (var stream = new NetworkMessageStream(connected.Value.Client)) | |
{ | |
stream.Disconnected += (s, e) => this.Send(new Network.Disconnect { Exception = e.Exception }); | |
stream.Connected += (s, e) => | |
{ | |
isConnected = true; | |
Connected.Raise(this, new NetworkMessageStream.ConnectedEventArgs { }); | |
}; | |
stream.MessageReceived += (s, e) => MessageReceived.Raise(this, e); | |
stream.MessageTransmitted += (s, e) => | |
{ | |
log.Debug("отправлено сообщение «{0}»", e.Message.GetType()); | |
MessageTransmitted.Raise(this, e); | |
}; | |
stream.Startup(); | |
while (true) | |
{ | |
var message = Receive<object>(); | |
if (message is Network.Disconnect) | |
{ | |
disconnect = (Network.Disconnect)message; | |
break; | |
} | |
else if (message is Connection.Connected) | |
{ | |
((Connection.Connected)message).Connection.Send(new Connection.Disconnect { }); | |
} | |
else stream.Send(message); | |
} | |
} | |
} | |
else disconnect = new Network.Disconnect | |
{ | |
Exception = new TimeoutException(string.Format | |
("не удалось подключиться к серверу за {0} мс", Connection.Connect.Timeout)) | |
}; | |
} | |
} | |
catch (Exception e) | |
{ | |
disconnect = new Network.Disconnect { Exception = e }; | |
} | |
finally | |
{ | |
isConnected = false; | |
ClearInbox(); | |
log.Debug("{0}", disconnect.Exception.Message); | |
log.Trace("стэк: {0}", disconnect.Exception.StackTrace); | |
Disconnected.Raise(this, new NetworkMessageStream.DisconnectedEventArgs { Exception = disconnect.Exception }); | |
} | |
} | |
} | |
finally | |
{ | |
log.Debug("сетевой актор остановлен"); | |
} | |
} | |
} | |
public struct NetworkConnected { } | |
/// <summary> | |
/// Сообщение о разрыве связи с сервером. | |
/// </summary> | |
public struct NetworkDisconnected { } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment