共同探讨QTP相关问题

发布新日志

  • C# Training

    2008-06-01 13:35:33

    Database:

    1. User Session table: UserId, LoginAddress

     

    Server:

    AddUserSession(string userName, IPAddress: ipAddress);

    RemoveUserSession(string userName);

    string GetUserList();

    SendMessage(string receiver, string messageContent);

     

    Client:

    ReceiveMessage(string sender, string messageContent);

     

  • WCF CallBack

    2008-04-07 20:31:20

    Callback 机制又被称之为 "duplex call",说白了就是在原有基础上,为客户端也添加一个服务端点,让服务器能调用这个客户端 "服务",从而实现所谓的回调机制。也正因为如此,通讯 binding 就必须支持双向通讯能力(bidirectional-capable),通常我们会选择 WSDualHttpBinding、NetTcpBinding 以及 NetNamedPipeBinding。

    接下来,我们回忆一下 Callback 的开发步骤。

    1. 创建服务契约。
    [ServiceContract(SessionMode=SessionMode.Required)]
    public interface IService
    {
      [OperationContract]
      void Test();
    }

    2. 定义回调接口。

    由于回调接口的实现类型在客户端执行,因此无需添加 ServiceContract。
    public interface ICallBack
    {
      [OperationContract]
      void Execute(DateTime d);
    }

    3. 实现服务契约。

    我们可以使用 "OperationContext.Current.GetCallbackChannel<T>()" 来回调客户端 "Callback"。
    public class MyService : IService, IDisposable
    {
      public void Test()
      {
        Console.WriteLine("Service Invoke CallBack...");
        OperationContext.Current.GetCallbackChannel<ICallBack>().Execute(DateTime.Now);
      }

      public void Dispose()
      {
        Console.WriteLine("Dispose:{0}", DateTime.Now);
      }
    }

    4. 创建 ServiceHost。

    注意发布 Metadata。
    ServiceHost host = new ServiceHost(typeof(MyService), new Uri("http://localhost:8080/myservice"));
    host.AddServiceEndpoint(typeof(IService), new WSDualHttpBinding(), "");

    ServiceMetadatabehavīor metadata = new ServiceMetadatabehavīor();
    metadata.HttpGetEnabled = true;
    host.Descrīption.behavīors.Add(metadata);

    host.Open();

    5. 使用 Svcutil.exe 或 VS2005 创建客户端代理。
    //------------------------------------------------------------------------------
    // <auto-generated>
    //   此代码由工具生成。
    //   运行库版本:2.0.50727.42
    //
    //   对此文件的更改可能会导致不正确的行为,并且如果
    //   重新生成代码,这些更改将会丢失。
    // </auto-generated>
    //------------------------------------------------------------------------------

    namespace ConsoleApplication1.localhost
    {
      [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
      [ServiceContractAttribute(ConfigurationName = "ConsoleApplication1.localhost.IService", CallbackContract = typeof(IServiceCallback), SessionMode = SessionMode.Required)]
      public interface IService
      {
        [OperationContractAttribute(Action = "http://.../IService/Test", ReplyAction = "http://.../IService/TestResponse")]
        void Test();
      }

      [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
      public interface IServiceCallback
      {
        [OperationContractAttribute(Action = "http://.../IService/Execute", ReplyAction = "http://.../IService/ExecuteResponse")]
        void Execute(System.DateTime d);
      }

      [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
      public interface IServiceChannel : IService, IClientChannel
      {
      }

      [DebuggerStepThroughAttribute()]
      [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
      public partial class ServiceClient : DuplexClientBase<IService>, IService
      {
        public ServiceClient(System.ServiceModel.InstanceContext callbackInstance)
          : base(callbackInstance)
        {
        }

        public void Test()
        {
          base.Channel.Test();
        }
      }
    }

    6. 创建客户端回调接口的实现类型。
    class CallBack : IServiceCallback
    {
      public void Execute(DateTime d)
      {
        Console.WriteLine("Client:{0}", d);
      }
    }

    7. 为客户端自动生成的配置文件添加 clientBaseAddress。

    服务器通过这个地址调用客户端回调服务。在同一台机器上调试,注意使用一个不同的端口。
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <bindings>
          <wsDualHttpBinding>
            <binding name="WSDualHttpBinding_IService" clientBaseAddress="http://localhost:8081/" >
            </binding>
          </wsDualHttpBinding>
        </bindings>
        <client>
          <endpoint address="http://localhost:8080/myservice" binding="wsDualHttpBinding"
            bindingConfiguration="WSDualHttpBinding_IService" contract="ConsoleApplication1.localhost.IService"
            name="WSDualHttpBinding_IService">
            <identity>
              <userPrincipalName value="HZH\Administrator" />
            </identity>
          </endpoint>
        </client>
      </system.serviceModel>
    </configuration>

    8. 创建客户端调用代码。
    InstanceContext instance = new InstanceContext(new CallBack());
    using (ServiceClient client = new ServiceClient(instance))
    {
      client.Test();
    }

    运行,看看结果。你看到了什么?异常!!!
    用户代码未处理 System.InvalidOperationException
      Message="This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on ServicebehavīorAttribute."
      Source="mscorlib"
      StackTrace:
        Server stack trace:
          在 System.ServiceModel.Channels.ServiceChannel.PrepareCall(ProxyOperationRuntime operation, Boolean oneway, ProxyRpc& rpc)

    死锁(deadlock)?没错,你找到了让本文继续下去的理由。

    在缺省情况下,服务实例是 single-threaded (ConcurrencyMode=ConcurrencyMode.Single) 的。当服务实例调用客户端回调服务时,方法被阻塞,直到客户端回调服务消息返回(reply message)。而问题是,Single模式不允许重入调用(reentrant calls),即便系统获得返回消息,也会因为无法重入调用方法解除阻塞而造成死锁。了解了这些背景,要解决这个死锁也很简单。

    方法1: 将回调接口方法设置为 "IsOneWay=true",这样就无需处理客户端返回消息。

    为回调接口方法添加 IsOneWay。
    public interface ICallBack
    {
      [OperationContract(IsOneWay=true)]
      void Execute(DateTime d);
    }

    修改客户端代理文件,添加 IsOneWay,删除 ReplyAction。
    [GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
    public interface IServiceCallback
    {
      [OperationContractAttribute(IsOneWay = true, Action = "http://.../IService/Execute")]
      void Execute(System.DateTime d);
    }

    方法2: 修改服务契约的并发模式,改为 "ConcurrencyMode.Reentrant" 或 "ConcurrencyMode.Multiple"。

    和方法1不同,本方法无需对客户端做出修改。
    [Servicebehavīor(ConcurrencyMode=ConcurrencyMode.Reentrant)]
    public class MyService : IService, IDisposable
    {
      public void Test()
      {
        Console.WriteLine("Service Invoke CallBack...");
        OperationContext.Current.GetCallbackChannel<ICallBack>().Execute(DateTime.Now);
      }

      public void Dispose()
      {
        Console.WriteLine("Dispose:{0}", DateTime.Now);
      }
    }

    无论使用哪种方法,修改后再次运行,我们得到了应有的结果。

    回调机制可以用来实现类似事件通知等功能,但对于多数情况下,并不推荐使用。服务器要连接客户端实现回调,可能面临很多问题,诸如内部网络地址、防火墙等等,而且回调机制也让服务实例的生存期管理变得更复杂。
  • C# 获取本地IP地址以及MAC地址

    2008-04-06 13:39:14

    1、通过主机名获取局域网IP地址;
                try
                {
                    IPHostEntry ipHostEntry = Dns.GetHostEntry(txtHost.Text);//传递计算机名
                    if (ipHostEntry.Aliases.Length > 0)
                    {
                        foreach (string alias in ipHostEntry.Aliases)
                            txtIP.Text = alias;
                    }
                    //获取IP地址
                    foreach (IPAddress addr in ipHostEntry.AddressList)
                        txtIPAddress.Text = addr.ToString();//获取IP地址
                 }
                catch
                {
                    MessageBox.Show("错误的主机名。");
                }
    2、通过局域网IP地址获取主机名;
    try
                {
                   IPHostEntry ipHostEntry = Dns.GetHostByAddress(txtIP.Text); ;//传递IP地址
                   txtHostName.Text = ipHostEntry.HostName.ToString()//取得主机名
                }
                catch
                {
                   MessageBox.Show("错误的IP地址。");
                }
    3、通过局域网IP地址获取Mac地址
    public static string ReadMac(string ip)//传递IP地址,即可返回MAC地址
            {
                string mac = "";
                System.Diagnostics.Process p = new System.Diagnostics.Process();
                p.StartInfo.FileName = "nbtstat";
                p.StartInfo.Arguments = "-a " + ip;
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.CreateNoWindow = true;
                p.StartInfo.RedirectStandardOutput = true;            
                p.Start();
                string ōutput = p.StandardOutput.ReadToEnd();
                int len = output.IndexOf("MAC Address = ");
                if(len>0)
                {
                     mac = output.Substring(len + 14, 17);
                }            
                p.WaitForExit();            
                return mac;
            }


    4.直接获取本机IP地址
       
    string   strHostIP="";  
        IPHostEntry   ōIPHost=Dns.Resolve(Environment.MachineName);  
        if(oIPHost.AddressList.Length>0)  
        strHostIP=oIPHost.AddressList[0].ToString();  
        Console.WriteLine(strHostIP);
Open Toolbar