Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
Рассмотрим прежде всего код сервера.
Сервер
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Threading;
using System.Runtime.Remoting.Contexts;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
namespace MyServer {
[Serializable]
public class MyCallContextUserName:
ILogicalThreadAffinative {
private String _userName;
public MyCallContextUserName {
_userName = Environment.UserName;
}
public String UserName {
get { return _userName; }
}
}
[Serializable]
public class MyCallContextServerName:
ILogicalThreadAffinative {
private Assembly _assembly;
private String _serverName;
public MyCallContextServerName {
_assembly = Assembly.GetExecutingAssembly;
_serverName = _assembly.FullName;
}
public String ServerName {
get { return _serverName; }
}
}
public interface IAccumulator {
void Add(int sum);
}
public interface IAudit {
int Total;
}
[Synchronization]
public class Account: ContextBoundObject,
IAccumulator, IAudit{
protected int sum = 0;
public void Add(int sum) {
this.sum += sum;
MyCallContextUserName userName =
(MyCallContextUserName)CallContext.GetData("UserName");
Console.WriteLine("UserName = " +
userName.UserName);
CallContext.SetData("ServerName",
new MyCallContextServerName);
}
public int Total {
return this.sum;
}
}
public class AccountApp {
public static void Main {
HttpChannel myChannel = new HttpChannel(8080);
ChannelServices.RegisterChannel(myChannel);
RemotingConfiguration.RegisterWellKnownServiceType {
typeof(Account), "Account",
WellKnownObjectMode.Singleton);
Console.WriteLine("Server is listening");
Console.ReadLine;
Console.WriteLine("Bye");
}
}
}
Относительно
В методе Add наш сервер будет не только получать очередной вклад и зачислять его на счет, но и работать с контекстом вызова.
Во-первых, предполагается, что клиент перед вызовом метода Add добавил в контекст вызова свойство с именем UserName. Соответствующее значение содержит учетные данные пользователя, от имени которого было запущено клиентское приложение. Значение свойства UserName
задается ссылкой на экземпляр класса[Serializable]
public class MyCallContextUserName:
ILogicalThreadAffinative {…}
Из определения этого класса видно, что его экземпляры могут передаваться в контексте вызова за пределы текущего контекста, т. к. этот класс определяется с атрибутом сериализации и наследует интерфейс ILogicalThreadAffinative.
Конструктор данного класса
public MyCallContextUserName {
_userName = Environment.UserName;
}
сохраняет в строковом поле _userName значение соответствующей переменной среды, получаемой как статическое свойство UserName класса Environment.
Используя свойство userName контекста вызова сервер в методе Add выясняет имя пользователя и выводит его на консоль:
MyCallContextUserName userName =
(MyCallContextUserName)CallContext.GetData("UserName");
Console.WriteLine("UserName = " +
userName.UserName);
Для доступа к нужному свойству используется статический метод GetData класса CallContext, которому в качестве параметра передается имя свойства. Полученное значение приводится к типу MyCallContextUserName.
Во-вторых, получив и выведя на консоль имя пользователя, сервер заканчивает выполнение метода Add, включая в контекст вызова свою информацию. Эту новую информацию сможет получить клиент, дождавшийся возврата из метода Add.
Итак, сервер добавляет в контекст вызова новое свойство с именем ServerName:
CallContext.SetData("ServerName",
new MyCallContextServerName};
Класс MyCallContextS erverName определяется аналогично классу MyCallContextUserName.
Основная функциональность этого класса определяется его конструктором:
public MyCallContextServerName {
_assembly = Assembly.GetExecutingAssembly;
_serverName = _assembly.FullName;
}
Здесь мы получаем ссылку на исполняемую сборку (т. е. на сборку сервера) и сохраняем в _serverName ее полное имя.
Остальная часть кода сервера не претерпела каких-либо изменений.
Клиент
using System; using MyServer;
using System. Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Net;
using System.Runtime.Remoting.Messaging;
public class MyApp {
public static void Main {
HttpChannel с = new HttpChannel;
ChannelServices.RegisterChannel(c);
try {
Account a = (Account)Activator.GetObject(typeof(Account),
"http://localhost:8080/Account",