<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      傳說中的WCF(12):服務器回調有啥用

      你說,服務器端回調有啥用呢?這樣問,估計不好回答,是吧。不急,先討論一個情景。

      假設現有服務器端S,客戶端A開始連接S并調用相關操作,其中有一個操作,在功能上有些特殊,調用后無法即時回復,因為在服務器上要作一些后續,而這些處理也許會消耗一定時間,比如:

      向服務器上傳了一個文件,但是,為了節約空間或出于其他目的,服務器要對剛上傳的文件進行處理(壓縮或者多媒體文件轉碼),這些操作無法馬上向客戶端回復,而客戶端也不可能就停在這里一直在等。我們希望,在客戶端上傳文件后馬上返回,而服務器對文件處理完成后再通知一下客戶端。

      這樣就引出一個東東——回調,E文叫Call Back。我估計用E文表述可能更好理解,Call back就是相對于Call to而言的,即調用的方向與Call to相反。

      是啊,有必要解釋一下,什么叫回調。我講一個故事吧。

      有一天,腦殘去書店買書,之前他聽別人說有一本書叫《吹牛沉思錄》很好看,于是腦殘也想買一本。可是,當他到書店后,東找西尋了一番,硬是沒看見那本書的影子。

      于是,他跑到柜臺問工作人員:“我想找《吹牛沉思錄》,沒找到。”

      工作人員馬上啟動書店的信息管理系統,但可以由于該系統品德不太好,居然用了35秒才啟動,然后,工作人員在上面查了一下,回過頭說:“抱歉,這本書太搶手了,賣完了,需要拿貨。”

      腦殘追問:“那要啥時候有貨?”

      工作人員說:“大概兩三天后吧,這樣吧,你留個聯系方式,等到貨到了我再聯系你。”

      ……

      對的,這就是回調的故事。

      腦殘(調用方)不知道書店什么時候有貨(不清楚調用的操作什么時候返回),但他總不能每天都跑去書店看看,這樣太不滑算(消耗資源),于是,書店(被調用方)建議,留下聯系方式(只保留內存中函數指針的地址,即回調地址),只要貨到了就通知腦殘(反調用)。

      回調比較典型的一種就是事件,事件驅動模型以前是在VB中被大量使用,后來.NET也繼承了這些優點,在此之前,C++/MFC大家都知道的,是通過消息來處理的(消息循環),其實,事件就是對消息的進一步封裝,這使得應用更加簡便和靈活。

      在.NET中我們知道,事件其實就是一個委托,由于委托可以同時綁定多個方法的特點,故被選為事件的表現類型,估計是這樣的。

      比如,我們常用的,為按鈕的Click事件定義一個處理。

      button.Click += new EventHandler(onClick)

      這樣,事件Click的訂閱者就是onClick方法,所謂訂閱事件,就像我們平時訂閱XX雜志一樣,只要有新一期發布就發快遞給你,你不用天天打電話去雜志社問。

      onClick并不是每一刻都去問button:“你被Click了嗎?”,onClick就像一個報警系統,只要特定的事件發生,它就會報警。這就是一種回調,onClick不必主動去調用button,只要處于監聽狀態即可,只要button被Click,onClick就會執行,不用你去調用它。

       

      講了這么多,不知道各位理解了沒?

       

      在WCF中使用回調,只需要多定義一個接口即可,這個接口的方法和服務協定一樣,要附加OperationContractAttribute特性。

      然后在定義服務類時,在ServiceContractAttribute的CallbackContract中設置一個回調接口的Type。

      在服務操作中,通過OperationContext的GetCallbackChannel方法取出回調協定的實例,調用回調的方法,就會在客戶端尋找回調接口的實現類并調用對應的成員。

       

      這樣說顯然不好理解,還是實踐出真知。我們來做一個選號程序。

      一:服務端實現

      這次我們的實現和前些有些區別,我們這里增加了配置文件來代替部分程序功能。

      第一步,新建一個控制臺應用程序。

      第二步,定義一個回調接口。

      namespace Server
      {
          interface ICallback
          {
              // 回調操作也必須One Way
              [OperationContract(IsOneWay = true)]
              void CallClient(int v);
          }
      }

      第三步,定義服務協定。

      namespace Server
      {
          [ServiceContract(
              Namespace = "MyNamespace",
              CallbackContract = typeof(ICallback), /* 標注回調協定 */
              SessionMode = SessionMode.Required /* 要求會話 */
              )]
          public interface IServer
          {
              // 會話從調用該操作啟動
              [OperationContract(IsOneWay = true, /* 必須 */
                  IsInitiating = true, /* 啟動會話 */
                  IsTerminating = false)]
              void CallServerOp();
      
              // 調用該操作后,會話結束
              [OperationContract(IsOneWay = true, /* 使用回調,必須為OneWay */
                  IsTerminating = true, /* 該操作標識會話終止 */
                  IsInitiating = false)]
              void End();
          }
      }

      CallbackContract屬性指向ICallback的Type。因為我要使用計時器每隔3秒鐘生成一個隨機數,并回調到客戶端,故要啟用會話。
      第四步,實現服務協定。

      namespace Server
      {
          public class MyServer : IServer, IDisposable
          {
              private ICallback icb;
              private Timer timer = null;//計時器,定時干活
              Random rand = null;//生成隨機整數
      
              public void CallServerOp()
              {
                  this.icb = OperationContext.Current.GetCallbackChannel<ICallback>();
                  rand = new Random();
                  // 生成隨整數,并回調到客戶端
                  // 每隔3秒生成一次
                  timer = new Timer((obj) => icb.CallClient(rand.Next()), null, 10, 3000);
              }
      
              public void Dispose()
              {
                  timer.Dispose();
                  Console.WriteLine("{0} - 服務實例已釋放。", DateTime.Now.ToLongTimeString());
              }
      
      
              public void End()  //結束
              {
                  Console.WriteLine("會話即將結束。");
              }
          }
      }

      第五步,完成服務器端的配置(通過App.config配置文件)。

       新建配置文件,命名App.config,內容如下。

      <?xml version="1.0" encoding="utf-8" ?>
      <configuration>
        <system.serviceModel>
          <client />
          <services>
            <service name="Server.MyServer"  behaviorConfiguration="Mybehavior" >
              <endpoint binding="netTcpBinding" bindingConfiguration="MynetTcpBinding" contract="Server.IServer" address="net.tcp://localhost:1211/rr">
                <identity>
                  <dns value="localhost"/>
                </identity>
              </endpoint>
              <host>
                <baseAddresses>
                  <add baseAddress="http://localhost:1378/services" />
                </baseAddresses>
              </host>
            </service>
          </services>
          <behaviors>
            <serviceBehaviors>
              <behavior name="Mybehavior" >
                <serviceMetadata httpGetEnabled="True"/>
              </behavior>
            </serviceBehaviors>
          </behaviors>
          <bindings>
            <netTcpBinding>
              <binding name="MynetTcpBinding">
                <security mode="None" />
              </binding>
            </netTcpBinding>
          </bindings>
        </system.serviceModel>
      </configuration>

      既支持會話,傳輸速度又快的,非TCP莫屬了,所以這里我選擇NetTcpBinding,這樣在默認行為下,每啟動一個會話就創建一個服務實例,而當會話結束時就會釋放。

      第六步,實現服務的寄宿程序。

      第二-五步實現了服務端的功能,結下來我們建一個服務的寄宿程序,用于服務的啟動和停止。

      添加Window服務類:

      namespace Server
      {
          partial class Host : ServiceBase
          {
              public Host()
              {
                  InitializeComponent();
              }
      
              #region 啟動入口
              /// <summary>
              /// 啟動入口
              /// </summary>
              /// <param name="args"></param>
              static void Main(string[] args)
              {
                  var host = new Host();
                  try
                  {
                      host.OnStart(args);
                      Console.WriteLine("服務已啟動");
                      Console.ReadLine();
                  }
                  catch (Exception e)
                  {
                  }
              }
              #endregion
      
              protected override void OnStart(string[] args)
              {
                  var service = new ServiceHost(typeof(Server.MyServer));
                  service.Open();
              }
      
              protected override void OnStop()
              {
                  // TODO: Add code here to perform any tear-down necessary to stop your service.
              }
          }
      }

      至此服務端的代碼就完成了。

      第五-六步功能類似于:

      static void Main(string[] args)
      {
      Console.Title = "WCF服務端";
      // 服務器基址
      Uri baseAddress = new Uri("http://localhost:1378/services");
      // 聲明服務器主機
      using (ServiceHost host = new ServiceHost(typeof(MyService), baseAddress))
      {
      // 添加綁定和終結點
      // tcp綁定支持會話
      NetTcpBinding binding = new NetTcpBinding();
      binding.Security.Mode = SecurityMode.None;
      host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://localhost:1211/rr");
      // 添加服務描述
      host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
      try
      {
      // 打開服務
      host.Open();
      Console.WriteLine("服務已啟動。");
      }
      catch (Exception ex)
      {
      Console.WriteLine(ex.Message);
      }
      Console.ReadKey();
      }
      }
      View Code

      二:客戶端實現

      第一步,新建一個wpf窗體應用項目。

      第二步,到對應目錄以管理員身份運行服務器端,然后在客戶端添加服務引用。可以看到引用后客戶端增加了app.config文件。

      <?xml version="1.0" encoding="utf-8" ?>
      <configuration>
          <system.serviceModel>
              <bindings>
                  <netTcpBinding>
                      <binding name="NetTcpBinding_IServer">
                          <security mode="None" />
                      </binding>
                  </netTcpBinding>
              </bindings>
              <client>
                  <endpoint address="net.tcp://localhost:1211/rr" binding="netTcpBinding"
                      bindingConfiguration="NetTcpBinding_IServer" contract="ServiceReference1.IServer"
                      name="NetTcpBinding_IServer">
                      <identity>
                          <dns value="localhost" />
                      </identity>
                  </endpoint>
              </client>
          </system.serviceModel>
      </configuration>

      第三步,在客戶端實現回調接口。

      namespace Client
      {
          /// <summary>
          /// 實現回調接口
          /// </summary>
          class MyCallback : ServiceReference1.IServerCallback
          {
              // 因為該方法是由服務器調用的
              // 如果希望在客戶端能即時作出響應
              // 應當使用事件
              public void CallClient(int v)
              {
                  if (this.ValueCallbacked != null)
                  {
                      this.ValueCallbacked(this, v);
                  }
              }
      
              /// <summary>
              /// 回調引發該事件
              /// </summary>
              public delegate void EventHandler(Object sender, int e);
              public event EventHandler ValueCallbacked;
          }
      }

      注意,回調的接口是在客戶端實現的,不是服務器端。

      第四步,設計窗口。

      namespace Client
      {
          /// <summary>
          /// Interaction logic for MainWindow.xaml
          /// </summary>
          public partial class MainWindow : Window
          {
              ServiceReference1.ServerClient cl = null;
              MyCallback cb = null;
      
              public MainWindow()
              {
                  InitializeComponent();
                  cb = new MyCallback();
                  cb.ValueCallbacked += new MyCallback.EventHandler(cb_ValueCallbacked);
              }
      
              void cb_ValueCallbacked(object sender, int e)
              {
                  this.label2.Content = e.ToString();
              }
      
              private void button1_Click(object sender, RoutedEventArgs e)
              {
                  cl = new ServiceReference1.ServerClient(new System.ServiceModel.InstanceContext(cb));
                  cl.CallServerOp();
                  button1.IsEnabled = false;
                  button2.IsEnabled = true;
              }
      
              private void button2_Click(object sender, RoutedEventArgs e)
              {
                  cl.End();
                  button1.IsEnabled = true;
                  button2.IsEnabled = false;
              }
          }
      }

      現在來測試一下吧。

       

      本文源碼:https://files.cnblogs.com/yuanli/MyApp12.zip

      posted @ 2016-08-09 11:10  閆寶平  閱讀(264)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久久精品av国产一区二区| 亚洲精品成人福利网站| 精品一精品国产一级毛片| 成人3d动漫一区二区三区| 欧美级特黄aaaaaa片| 国产一区二区三区禁18| 不卡在线一区二区三区视频| jizzjizz少妇亚洲水多| 亚洲中文字幕无码爆乳| 亚洲欧美人成电影在线观看| 狠狠躁夜夜躁人人爽蜜桃| 午夜激情福利在线免费看| 中文字幕成人精品久久不卡| 毛片在线播放网址| 午夜高清福利在线观看| 色婷婷五月综合久久| 国产高清免费午夜在线视频| 崇信县| 国产精品制服丝袜白丝| 人妻在线无码一区二区三区| 国产成人综合95精品视频 | 亚洲综合精品一区二区三区| 成人国产一区二区三区精品| 99精品国产综合久久久久五月天| 99国产欧美另类久久久精品| 波多野结衣久久一区二区| 9l精品人妻中文字幕色| 久久国产精品精品国产色婷婷| 亚洲码国产精品高潮在线| 日韩av在线一区二区三区| 又爆又大又粗又硬又黄的a片| 又爽又黄又无遮掩的免费视频 | 骚虎视频在线观看| 成人亚洲一级午夜激情网| 日韩加勒比一本无码精品| 玩弄人妻少妇500系列| 福利一区二区不卡国产| 亚洲国模精品一区二区| 久久国产一区二区三区| 欧美性猛交xxxx乱大交丰满| 久久久精品国产精品久久|