Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@zahardzhan
Created February 8, 2012 23:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zahardzhan/1775601 to your computer and use it in GitHub Desktop.
Save zahardzhan/1775601 to your computer and use it in GitHub Desktop.
network actor
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