精华内容
下载资源
问答
  • 基于Aispeech的智能对话APP()--唤醒相关设置以及全双工/半双工模式一.唤醒相关设置1.1 命令唤醒词1.2 打断唤醒词1.3 清空当前设置的所有增量唤醒词二.全双工/半双工模式2.1 全双工模式2.2 半双工模式2.3 获取dds...


    本篇博文是我自己在学习Aispeech的过程中,分享我遇到的问题以及如何解决。在文章的最后我会上传其代码。感兴趣的小伙伴,请自行下载。

    一.唤醒相关设置

    1.1 命令唤醒词

    如果您期望在唤醒的时候执行一条指令,可以在需要的时候调用下面的接口。命令唤醒词和DUI控制台所设置的快捷唤醒词是相同的功能,且互不影响,可同时存在,根据需求来选择设置方式。
    参数说明:
    1)actions 命令唤醒词指令,为string数组,不为null。
    2)words 命令唤醒词,为string数组,不为null。
    3)pinyins 命令唤醒词的拼音,形如:ni hao xiao chi,为string数组, 不为null。
    4)thresholds 命令唤醒词的阈值,形如:0.120(取值范围:0-1),为string数组,不为null。
    5)greetings 命令唤醒词的欢迎语,为string二维数组,不为null,每维对应一个唤醒词的欢迎语。

    updateCommandWakeupWord

    // 实时更新命令唤醒词-更新一条命令唤醒词(1.1.2.1版本及以上支持)
    WakeupWord commandWord = new WakeupWord()
            .setPinyin("xia yi shou")
            .setWord("下一首")
            .setThreshold("0.18")
            .addGreeting("我在")
            .setAction("sys.next");
    DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWord(commandWord);
     
     
    // 实时更新命令唤醒词-更新多条命令唤醒词(1.1.2.1版本及以上支持)
    WakeupWord commandWord = new WakeupWord()
            .setPinyin("xia yi shou")
            .setWord("下一首")
            .setThreshold("0.18")
            .addGreeting("我在")
            .setAction("sys.next");
    WakeupWord commandWord1 = new WakeupWord()
            .setPinyin("shang yi shou")
            .setWord("上一首")
            .addGreeting("我在")
            .setThreshold("0.20")
            .setAction("sys.play");
    List<WakeupWord> commandList = new ArrayList<>();
    commandList.add(commandWord);
    commandList.add(commandWord1);
    DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWords(commandList);
     
      
    //清空当前设置的命令唤醒词
    DDS.getInstance().getAgent().getWakeupEngine().clearCommandWakeupWord();
    

    如果您期望添加或者移除一些命令唤醒词而保留之前的命令唤醒词(即控制台上的快捷唤醒词),可以调用下面的接口。
    add remove

    // 实时添加命令唤醒词-添加一条命令唤醒词(1.1.2.1版本及以上支持)
    WakeupWord commandWord = new WakeupWord()
            .setPinyin("xia yi shou")
            .setWord("下一首")
            .setThreshold("0.18")
            .addGreeting("我在")
            .setAction("sys.next");
    DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWord(commandWord);
     
    // 实时添加命令唤醒词-添加多条命令唤醒词(1.1.2.1版本及以上支持)
    WakeupWord commandWord = new WakeupWord()
            .setPinyin("xia yi shou")
            .setWord("下一首")
            .setThreshold("0.18")
            .addGreeting("我在")
            .setAction("sys.next");
    WakeupWord commandWord1 = new WakeupWord()
            .setPinyin("shang yi shou")
            .setWord("上一首")
            .addGreeting("我在")
            .setThreshold("0.20")
            .setAction("sys.play");
    List<WakeupWord> commandList = new ArrayList<>();
    commandList.add(commandWord);
    commandList.add(commandWord1);
    DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(commandList);
    
     
    // 实时移除命令唤醒词-移除一条命令唤醒词(1.1.2.1版本及以上支持)
    WakeupWord commandWord = new WakeupWord().setWord("下一首");
    
    DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWord(commandWord);
     
     
    // 实时移除命令唤醒词-移除多条命令唤醒词(1.1.2.1版本及以上支持)
    WakeupWord commandWord = new WakeupWord().setWord("下一首");
    WakeupWord commandWord1 = new WakeupWord().setWord("上一首");
    List<WakeupWord> commandList = new ArrayList<>();
    commandList.add(commandWord);
    commandList.add(commandWord1);
    DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWords(commandList);
    

    相关代码:

     public void addCommandWord() throws DDSNotInitCompleteException {
            WakeupWord commandWord = new WakeupWord()
                    .setWord("下一首")
                    .addGreeting("我在")
                    .setAction("sys.next")
                    .setIncrementWord("true");
            WakeupWord commandWord1 = new WakeupWord()
                    .setWord("上一首")
                    .addGreeting("我在")
                    .setAction("sys.play")
                    .setIncrementWord("true");;
            List<WakeupWord> commandList = new ArrayList<>();
            commandList.add(commandWord);
            commandList.add(commandWord1);
            DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(commandList);
            toMain();
        }
    
        public void updateCommandWord() throws DDSNotInitCompleteException {
            WakeupWord commandWord = new WakeupWord()
                    .setWord("下一首")
                    .addGreeting("我在呢")
                    .setAction("sys.next")
                    .setIncrementWord("true");;
            WakeupWord commandWord1 = new WakeupWord()
                    .setWord("上一首")
                    .addGreeting("我在呢")
                    .setAction("sys.play")
                    .setIncrementWord("true");;
            List<WakeupWord> commandList = new ArrayList<>();
            commandList.add(commandWord);
            commandList.add(commandWord1);
            DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWords(commandList);
            toMain();
        }
    
        public void removeCommand() {
            WakeupWord commandWord = new WakeupWord()
                    .setWord("下一首")
    //                .setPinyin("xia yi shou")
                    .setIncrementWord("true");;
            WakeupWord commandWord1 = new WakeupWord()
                    .setWord("上一首")
    //                .setPinyin("shang yi shou")
                    .setIncrementWord("true");;
            List<WakeupWord> commandList = new ArrayList<>();
            commandList.add(commandWord);
            commandList.add(commandWord1);
            try {
                DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWords(commandList);
            } catch (DDSNotInitCompleteException e) {
    //            Log.d("xlg","--------------error---------");
                e.printStackTrace();
            }
    
            toMain();
        }
    
        public void clearCommandWord() throws DDSNotInitCompleteException {
            //清空当前设置的增量命令唤醒词
            DDS.getInstance().getAgent().getWakeupEngine().clearIncrementCommandWakeupWord();
            toMain();
        }
    
    

    1.2 打断唤醒词

    如果您期望在唤醒的时候能同时打断语音播报,可以在需要的时候调用下面的接口。打断唤醒词暂不支持在DUI控制台设置。(说明:打断需要设备开启回声消除)
    参数说明:
    1)words 打断唤醒词,为string数组,不为null。
    2)pinyins 打断唤醒词的拼音,形如:ni hao xiao chi,为string数组,不为null。
    3)thresholds 打断唤醒词的阈值,形如:0.120(取值范围:0-1)为string数组,不为null。

    updateShortcutWakeupWord

    //实时更新打断唤醒词-更新一条打断唤醒词(1.1.2.1版本及以上支持)
    WakeupWord fixWord = new WakeupWord()
            .setPinyin("da duan")
            .setWord("打断")
            .setThreshold("0.18");
    DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWord(fixWord);
     
     
    //实时更新打断唤醒词-更新多条打断唤醒词(1.1.2.1版本及以上支持)
    WakeupWord fixWord = new WakeupWord()
            .setPinyin("da duan")
            .setWord("打断")
            .setThreshold("0.18");
    WakeupWord fixWord1 = new WakeupWord()
            .setPinyin("da duan ba")
            .setWord("打断吧")
            
            .setThreshold("0.16");
    List<WakeupWord> fixList = new ArrayList<>();
    fixList.add(fixWord);
    fixList.add(fixWord1);
    DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(fixList);
      
    //清空当前设置的打断唤醒词
    DDS.getInstance().getAgent().getWakeupEngine().clearShortCutWakeupWord();
    

    如果您期望添加或者移除一些打断唤醒词而保留之前的打断唤醒词,可以在需要的时候调用下面的接口。

    add remove

    //实时添加打断唤醒词-添加一条打断唤醒词(1.1.2.1版本及以上支持)
    WakeupWord fixWord = new WakeupWord()
            .setPinyin("da duan")
            .setWord("打断")
            .setThreshold("0.18");
    DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWord(fixWord);
     
     
    //实时添加打断唤醒词-添加多条打断唤醒词(1.1.2.1版本及以上支持)
    WakeupWord fixWord = new WakeupWord()
            .setPinyin("da duan")
            .setWord("打断")
            .setThreshold("0.18");
    WakeupWord fixWord1 = new WakeupWord()
            .setPinyin("da duan ba")
            .setWord("打断吧")
            .setThreshold("0.16");
    List<WakeupWord> fixList = new ArrayList<>();
    fixList.add(fixWord);
    fixList.add(fixWord1);
    DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWords(fixList);
      
    
    //实时移除打断唤醒词-移除一条打断唤醒词(1.1.2.1版本及以上支持)
    WakeupWord fixWord = new WakeupWord().setWord("打断");
    
    DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWord(fixWord);
     
     
    //实时移除打断唤醒词-移除多条打断唤醒词(1.1.2.1版本及以上支持)
    WakeupWord fixWord = new WakeupWord().setWord("打断");
    WakeupWord fixWord1 = new WakeupWord().setWord("打断吧");
    List<WakeupWord> fixList = new ArrayList<>();
    fixList.add(fixWord);
    fixList.add(fixWord1);
    DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWords(fixList);
    

    相关代码:

     //打断唤醒词
    
        public void addShortCutWord() throws DDSNotInitCompleteException {
            WakeupWord commandWord = new WakeupWord()
                    .setWord("下一首")
                    .setIncrementWord("true");;
            WakeupWord commandWord1 = new WakeupWord()
                    .setWord("上一首")
                    .setIncrementWord("true");;
            List<WakeupWord> commandList = new ArrayList<>();
            commandList.add(commandWord);
            commandList.add(commandWord1);
            DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWords(commandList);
            toMain();
        }
    
        public void updateShortCutWord() throws DDSNotInitCompleteException {
            WakeupWord commandWord = new WakeupWord()
                    .setWord("下一首")
                    .setIncrementWord("true");;
            WakeupWord commandWord1 = new WakeupWord()
                    .setWord("上一首")
                    .setIncrementWord("true");;
            List<WakeupWord> commandList = new ArrayList<>();
            commandList.add(commandWord);
            commandList.add(commandWord1);
            DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(commandList);
            toMain();
        }
    
        public void removeShortCutWord() throws DDSNotInitCompleteException {
            WakeupWord commandWord = new WakeupWord()
                    .setWord("下一首")
                    .setIncrementWord("true");
            WakeupWord commandWord1 = new WakeupWord()
                    .setWord("上一首")
                    .setIncrementWord("true");;
            List<WakeupWord> commandList = new ArrayList<>();
            commandList.add(commandWord);
            commandList.add(commandWord1);
            DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWords(commandList);
            toMain();
    
        }
    
        public void clearShortCutWord() throws DDSNotInitCompleteException {
            //清空当前设置的增量打断唤醒词
            DDS.getInstance().getAgent().getWakeupEngine().clearIncrementShortCutWakeupWord();
            toMain();
        }
    
    
    

    1.3 清空当前设置的所有增量唤醒词

    清空当前设置的所有增量唤醒词代码:

    public void clearAllWakeupWord() throws DDSNotInitCompleteException {
            // 清空当前设置的所有增量唤醒词
            DDS.getInstance().getAgent().getWakeupEngine().clearIncrementWakeupWord();
            toMain();
        }
    

    二.全双工/半双工模式

    模式切换

    // 设置dds模式
    DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.HALF_DUPLEX);// 半双工,一问一答模式
    DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.FULL_DUPLEX);// 全双工模式
      
    注意: 在切换全双工时如需要唤醒还请调用: DDS.getInstance().getAgent().getWakeupEngine().enableWakeup();
     
    // 获取dds模式
    DDS.getInstance().getAgent().getDuplexMode();
    

    全双工不支持接口与功能

    1. 由于全双工模式的特性不再需要vad来做音频的切割, 所以设置vad前端与后端停顿时间无效 @4.5 识别配置项

    2. 由于全双工模式为全程识别状态, 所以开启识别时startListening不可用 @3.13 开启/终止识别

    3. 由于全双工模式为全程识别状态, 在模拟半双工下的打断唤醒由识别代替,主唤醒词正常开启下一轮对话

    4. 由于全双工模式下暂时没有快捷指令的使用场景, 所以快捷指令在全双工模式下不可用 @3.21 快捷指令

    5. 基于全双工模式下性能优化的考虑, 目前在全双工模式下默认禁用vad功能, 如有oneshot需求请添加配置K_USE_VAD_IN_FULLDUPLEX打开vad

    2.1 全双工模式

    全双工模式实现代码:

        public void fullDuplex() {
            try {
                DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.FULL_DUPLEX);
                toMain();
            } catch (DDSNotInitCompleteException e) {
                e.printStackTrace();
            }
        }  
    

    2.2 半双工模式

    半双工模式实现代码:

    public void halfDuplex() {
            try {
                DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.HALF_DUPLEX);
                toMain();
            } catch (DDSNotInitCompleteException e) {
                e.printStackTrace();
            }
        }
    

    2.3 获取dds模式

    获取dds模式实现代码:

     public void getDuplexMode() {
            // 获取dds模式
            try {
                Agent.DuplexMode mode = DDS.getInstance().getAgent().getDuplexMode();
                Log.d(TAG,"DuplexMode:"+mode);
            } catch (DDSNotInitCompleteException e) {
                e.printStackTrace();
            }
        }
    

    2.4 跳过Vad的超时检测

    跳过Vad的超时检测

    // 在全双工模式下, 如果你想跳过Vad的检测直接进入识别状态则可以调用下面的接口
    DDS.getInstance().getAgent().getASREngine().killVadTimeoutInFullDuplex();
    

    过Vad的超时检测实现代码:

    //ASREngine.killVadTimeoutInFullDuplex(), 在全双工模式下,跳过Vad的超时检测
        //只在当前对话时生效
        public void killVadTimeoutInFullDuplex() {
            try {
                DDS.getInstance().getAgent().getASREngine().killVadTimeoutInFullDuplex();
            } catch (DDSNotInitCompleteException e) {
                e.printStackTrace();
            }
        }在这里插入代码片
    

    三.代码下载

    如果有问题无法解决或者对代码有兴趣的小伙伴,可以直接下载DUIdemo2.9.zip

    展开全文
  • WCF学习之旅—请求与答复模式和单向模式(十) WCF学习之旅—HTTP双工模式(二十) 五、TCP双工模式 上一篇文章中我们学习了HTTP的双工模式,我们今天就学习一下TCP的双工模式。 在一个基于面向服务的...

                    WCF学习之旅—请求与答复模式和单向模式(十九)

                    WCF学习之旅—HTTP双工模式(二十)

     

    五、TCP双工模式

         上一篇文章中我们学习了HTTP的双工模式,我们今天就学习一下TCP的双工模式。

          在一个基于面向服务的分布式环境中,借助一个标准的、平台无关的通信协议,使各个服务通过SOAP Message实现相互之间的交互。这个交互的过程实际上就是信息交换的过程。WCF支持不同形式的信息交换,我们把这称之为信息交换模式(Message Exchange Pattern(简称MEP),下同), 常见的MEP包括: 请求/答复,单向模式和双工模式。通过采用双工的MEP,我们可以实现在服务端调用客户端的操作。虽然WCF为我们实现底层的通信细节,使得我们把精力转移到业务逻辑的实现,进行与通信协议无关的编程,但是对通信协议的理解有利于我们根据所处的具体环境选择一个合适的通信协议。说到通信协议, WCF 经常使用的是以下4个:Http,TCP,Named Pipe,MSMQ。

          我们用上一文章( WCF学习之旅—HTTP双工模式(二十))中的示例,进行一下修改,变成一个TCP双向通信的WCF服务应用程序。下面直接上代码。


    1
    Contract

     

    using Contracts;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text; 
    
    namespace Contracts
    {
    
        // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IBookService”。
        [ServiceContract(CallbackContract = typeof(ICallback))]
        public interface IBookService
        {
    
           /// <summary>
           /// 请求与答复模式,默认模式
           /// </summary>
           /// <param name="Id">书籍ID</param>
           /// <returns></returns>
            [OperationContract]
            string GetBook(string Id);
            /// <summary>
    
            /// 单工模式,显示名称
            /// </summary>
            /// <param name="name">书籍名称</param>
            [OperationContract(IsOneWay = true)]
            void ShowName(string name);
            /// <summary>
            /// 双工模式,显示名称
            /// </summary>
            /// <param name="name">书籍名称</param>
            [OperationContract(IsOneWay = true)]
            void DisplayName(string name);
     
    
        }
    
    }
    
     
    
     
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text; 
    
    namespace Contracts
    { 
    
        public interface ICallback
        {
    
            [OperationContract(IsOneWay = true)]
            void DisplayResult(string result);
    
        }
    }

     

     

    2WcfServiceLib

    using Contracts;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
     
    
    namespace WcfServiceLib
    {
    
        // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“BookService”。
    
        // 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 BookService.svc 或 BookService.svc.cs,然后开始调试。
        public class BookService : IBookService
        {
    
            /// <summary>
            /// 请求与答复模式,默认模式
            /// </summary>
            /// <param name="Id">书籍ID</param>
            /// <returns></returns>
            public string GetBook(string Id)
            {
    
                System.Threading.Thread.Sleep(20000);
                int bookId = Convert.ToInt32(Id);
                Books book = SetBook(bookId);
                string xml = XMLHelper.ToXML<Books>(book);
                return xml; 
    
            }
    
     
    
            public Books SetBook(int Id)
            {
    
                Books book = new Books();
                book.BookID = Id;
                book.AuthorID = 1;
                book.Category = "IBM";
                book.Price = 39.99M;
                book.Numberofcopies = 25;
                book.Name = "DB2数据库性能调整和优";
                book.PublishDate = new DateTime(2015, 2, 23);
                return book;
            }
    
            /// <summary>
            /// 单工模式,显示名称
            /// </summary>
            /// <param name="name">名称</param>
            public void ShowName(string name)
            {
    
                string result = string.Format("书籍名称:{0},日期时间{1}", name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                Console.WriteLine("\r\n" + result);          
    
            }
    
            /// <summary>
            /// 双工模式,回调显示结果
            /// </summary>
            /// <param name="name">名称</param>
            public void DisplayName(string name)
            {
                string result=string.Format("书籍名称:{0},日期时间{1}", name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                Console.WriteLine("\r\n" + result);
                ICallback call = OperationContext.Current.GetCallbackChannel<ICallback>();
                call.DisplayResult("回调客户端---"+result);
    
            }
    
        }
    
    }

     

          在服务端,通过OperationContext.Current.GetCallbackChannel来获得客户端指定的CallbackContext 实例,进而调用客户端的操作。

    3Hosting

    宿主配置文件:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    
      </startup>
      <system.serviceModel>
        <diagnostics>
          <messageLogging logEntireMessage="true" logKnownPii="false" logMalformedMessages="true"
            logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" />
          <endToEndTracing propagateActivity="true" activityTracing="true"
            messageFlowTracing="true" />
        </diagnostics>
     
    
        <services>
          <service  name="WcfServiceLib.BookService">
            <endpoint address="net.tcp://127.0.0.1:9999/BookService" binding="netTcpBinding"
            contract="Contracts.IBookService" />
    
          </service>
        </services>
      </system.serviceModel>
    </configuration>

     

     

          我们通过netTcpBinding来模拟基于TCP的双向通信。代码如下:

    using Contracts;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.Text;
    using System.Threading.Tasks;
    using WcfServiceLib;
    
     
    
    namespace ConsoleHosting
    {
    
        class Program
        {
    
            static void Main(string[] args)
            {
                Console.WriteLine("输入启动方式,C--Code A -- App.config方式!");
                string key = Console.ReadLine();
                switch (key)
                {
                    case "C":
                        StartByCode();
                        break;
                    case "A":
                        StartByConfig();
                        break;
                    default:
                        Console.WriteLine("没有选择启动方式,使用默认方式");
                        StartByCode();
                        break;
    
                }
            }
    
            private static void StartByCode()
            {
    
                //创建宿主的基地址
                Uri baseAddress = new Uri("http://localhost:8080/BookService");
                //创建宿主
                using (ServiceHost host = new ServiceHost(typeof(BookService), baseAddress))
                {
    
                    //向宿主中添加终结点
                    host.AddServiceEndpoint(typeof(IBookService), new WSDualHttpBinding(), baseAddress);
                    if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
    
                    {
                        //将HttpGetEnabled属性设置为true
                        ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                        behavior.HttpGetEnabled = true;
                        behavior.HttpGetUrl = baseAddress;
                        //将行为添加到Behaviors中 
    
                        host.Description.Behaviors.Add(behavior);
                        //打开宿主
    
                        host.Opened += delegate
                        {
                            Console.WriteLine("BookService控制台程序寄宿已经启动,HTTP监听已启动....,按任意键终止服务!");
                        };
    
                        host.Open();
                        //print endpoint information                  
    
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        foreach (ServiceEndpoint se in host.Description.Endpoints)
                        {
                            Console.WriteLine("[终结点]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-绑定]: {2} \r\n\t [C-协定]: {3}",
                         se.Name, se.Address, se.Binding.Name, se.Contract.Name);
                        }
                        Console.Read();
                    }
                }
    
            }
    
            private static void StartByConfig()
            {
                using (ServiceHost host = new ServiceHost(typeof(BookService)))
                {
                    host.Opened += delegate
                    {
    
                        Console.WriteLine("BookService控制台程序寄宿已经启动,TCP监听已启动....,按任意键终止服务!");
                    };
    
                    host.Open();
                    //print endpoint information                 
    
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    foreach (ServiceEndpoint se in host.Description.Endpoints)
                    {
                        Console.WriteLine("[终结点]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-绑定]: {2} \r\n\t [C-协定]: {3}",
                     se.Name, se.Address, se.Binding.Name, se.Contract.Name);
     
                    }
    
                    Console.Read();
                }
    
            }
    
        }
    
    }
    
     

     

     

    4.客户端:

        配置文件中的信息进行修改:

      <system.serviceModel>
            <client>
               <endpoint address="net.tcp://localhost:9999/BookService" binding="netTcpBinding"
    
                     bindingConfiguration="" contract="Contracts.IBookService"
    
                     name="BookServiceEndpoint" />
            </client>
        </system.serviceModel>

     

          接下来实现对双工服务的调用,下面是相关的配置和托管程序。在服务调用程序中,通过 DuplexChannelFactory<TChannel>创建服务代理对 象,DuplexChannelFactory<TChannel>和ChannelFactory<TChannel>的功能 都是一个服务代理对象的创建工厂,不过DuplexChannelFactory<TChannel>专门用于基于双工通信的服务代理的创 建。在创建DuplexChannelFactory<TChannel>之前,先创建回调对象,并通过InstanceContext对回 调对象进行包装。代码如下:

    private void btnTcpDuplex_Click(object sender, EventArgs e)
            { 
    
                DuplexChannelFactory<IBookService> channelFactory = new DuplexChannelFactory<IBookService>(instanceContext, "BookServiceEndpoint");
                IBookService client = channelFactory.CreateChannel();
    
                           
    
                //在BookCallBack对象的mainThread(委托)对象上搭载两个方法,在线程中调用mainThread对象时相当于调用了这两个方法。 
    
                textBox1.Text += string.Format("开始调用wcf服务:{0}\r\n\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    
                client.DisplayName("TCP---科学可以这样看丛书");
    
                textBox1.Text += string.Format("\r\n\r\n调用结束:{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            }

     

           在创建 DuplexChannelFactory< IBookService >中,指定了Callback Context Instance: 一个实现了Callback Contract的BookCallBack对象。该对象在Service中通过 OperationContext.Current.GetCallbackChannel<ICallback>()获得。

    通过运行程序之后的结果如下图:

     

     

      2. 基于Http的双向通讯V.S.基于TCP的双向通讯

           由于Http和TCP在各自协议上的差异,他们实现双向通信的发式是不同的。

           Http是一个应用层的协议,它的主要特征就是无连接和无状态。它采用传统的“请求/回复”的方式进行通信,客户端发送Http Request请求服务端的某个资源,服务端接收到该Http Request, 回发对应的Http Response。当客户端接收到对应的Response,该连接就会关闭。也就是说客户端和服务端的 连接仅仅维持在发送Request到接收到Response这一段时间内。同时,每次基于Http的 连接是相互独立,互不相干的,当前连接无法获得上一次连接的状态。为了保存调用的的状态信 息,ASP.NET通过把状态信息保存在服务端的Session之中,具体的做法是:ASP.NET为每个Session创建一个 Unique ID,与之关联一个HttpSessionState对象,并把状态信息保存在内存中或者持久的存储介质(比如SQL Server)中。而WCF则采用另外的方式实现对Session的支持:每个Session关联到某个Service Instance上。

           我们来讲一下HTTP双向通信的过程,当客户端通过HTTP请求调用WCF服务之前,会有一个终结点在客户端被创建,用于监听服务端对它的Request。客户端对 WCF服务的调用会建立一个客户端到服务端的连接,当WCF服务在执行操作过程中需要回调对应的客户端,实际上会建立另一个服务端到客户端的Http 连接。虽然我们时候说WCF为支持双向通信提供一个双工通道,实际上这个双工通道是由两个HTTP连接组成的。

          再来看一下TCP的双向通信的过程,对于TCP传输层协议,它则是一个基于连接的协议,在正式进行数据传输的之前,必须要在客户端和服务端之间建立一个连接,连接的建立通过经典的“3次握手”来实现。TCP天生就具有双工的特性,也就是说当连接 被创建之后,从客户端到服务端,和从服务端到客户端的数据传递都可以利用同一个连接来实现。对于WCF中的双向通信,客户端调用服务端,服务端回调客户端的操作使用的都是同一个连接、同一个通道。所以基于TCP的双工通信模式才是真正意义上的双工通信模式。

     

    转载于:https://www.cnblogs.com/chillsrc/p/5776049.html

    展开全文
  • WCF学习之旅—请求与答复模式和单向模式(十) 四、HTTP双工模式  双工模式建立在上文所实现的两种模式的基础之上,实现客户端与服务端相互调用:前面介绍的两种方法只是在客户端调用服务端的方法,然后...

          WCF学习之旅—请求与答复模式和单向模式(十九)

     

    四、HTTP双工模式

      双工模式建立在上文所实现的两种模式的基础之上,实现客户端与服务端相互调用:前面介绍的两种方法只是在客户端调用服务端的方法,然后服务端有返回值返回客户端;相互调用是指客户端调用服务端的方法,同时服务端也可以调用客户端的方法。

          基于双工MEP (信息交换模式,Message Exchange Pattern,下同)消息交换可以看成是多个基本模式下 (比如请求-回复模式和单项模式)消息交换的组合。双工MEP又具有一些变体,比如典型的订阅-发布模式就可以看成是双工模式的一种表现形式。

        一) 两种典型的双工MEP

         1.请求过程中的回调

          这是一种比较典型的双工消息交换模式的表现形式,客户端在进行服务调用的时候,附加上一个回调对象;服务在对处理该处理中,通过客户端附加的回调对 象(实际上是调用回调服务的代理对象)回调客户端的操作(该操作在客户端执行)。整个消息交换的过程实际上由两个基本的消息交换构成,其一是客户端正常的服务请求,其二则是服务端对客户端的回调。两者可以采用请求-回复模式,也可以采用单向(One-way)的MEP进行消息交换。图1描述了这样的过程,服务调用和回调都采用请求-回复。


    图1 请求过程中的回调

         2.订阅-发布

         订阅-发布模式是双工模式的一个典型的变体。在这个模式下,消息交换的双方变成了订阅者和发布者,若干订阅者就某个主题向发布者申请订阅,发布者将 所有的订阅者保存在一个订阅者列表中,在某个时刻将主题发送给该主题的所有订阅者。实际上基于订阅-发布模式的消息交换也可以看成是两个基本模式下消息交 换的组合,申请订阅是一个单向模式的消息交换(如果订阅者行为得到订阅的回馈,该消息交换也可以采用请求-回复模式);而主题发布也是一个基于单向模式的消息交换过程。订阅-发布消息交换模式如图2所示。

     

     

    图2 订阅-发布

           二) 实例演示:创建基于双工通信的WCF应用

          接下来我们通过一个的实例来学习基于双工通信的WCF应用。为简单起见,我们沿用上文( WCF学习之旅—请求与答复模式和单向模式(十九))的示例。在上文的示例中,我们都是调用 BookService直接显示书籍名称,并将结果通过Winform应用程序输出。在本例中我们将采用另外一种截然不同的方式调用服务并进行结果的输出:我们通过单向(One-way)的模式调用BookService(也就是客户端不可能通过回复消息得到结果),服务端在完成书籍名称显示之后,通过回调(Callback)的方式在客户端将书籍名称显示出来。

          步骤一:定义服务契约和回调契约

          首先进行服务契约的定义,我们照例通过接口(IBookService)的方式定义服务契约,作用于指定显示书籍名称的方法DisplayName操作,我们通过OperationContractAttribute特性的IsOneway属性将操作定义成单向的操作,这意味着客户端仅仅是向服务端发送一个请求,并不会通过回复消息得到任何结果。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text; 
    
    namespace WcfServiceLib
    {
        // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IBookService”。
        [ServiceContract(CallbackContract = typeof(ICallback))]
        public interface IBookService
        { 
    
            /// <summary>
            /// 单工模式,显示名称
            /// </summary>
            /// <param name="name">书籍名称</param>
            [OperationContract(IsOneWay = true)]
            void DisplayName(string name); 
    
        }
    
    }
    
     

     

          我们要实现的功能是通过在服务端回调客户端的操作方式实现结果的输出。客户端正常调用BookService的服务方法,那么在服务 执行过程中借助于客户端在服务调用时提供的回调对象对客户端的操作进行回调,从本质上讲是另外一种形式的服务调用。WCF采用基于服务契约的调用形式,客户端正常的服务调用需要服务契约,同理服务端回调客户端依然需要通过描述回调操作的服务契约,我们把这种服务契约称为回调契约。回调契约的类型通过ServiceContractAttribute特性的CallbackContract属性进行指定。

          上面代码中服务契约IBookService的回调契约ICallback定义如下。由于回调契约本质也是一个服务契约,所以定义方式和一般意义上的 服务契约基本一样。有一点不同的是,由于定义IBookService的时候已经通过 [ServiceContract(CallbackContract=typeof(ICallback))]指明ICallback是一个服务契约 了,所以ICallback不再需要添加ServiceContractAttribute特性。ICallback定义了一个服务操作DisplayResult用于显示书籍名称与日期,由于服务端不需要回调的返回值,所以将回调操作也设为单向方法。ICallback接口代码如下。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text; 
    
    namespace WcfServiceLib
    {
    
        public interface ICallback
        {
    
            [OperationContract(IsOneWay = true)]
            void DisplayResult(string result);
        }
    
    }

     

         步骤二:实现服务

         在BookService实现了上面定义的服务契约IBookService中的方法,实现了DisplayName操作,完成书籍名称和日期的显示工作。同时把书籍名称与日期的显示通过回调的方式在客户端显示出来,所以需要借助于客户端提供的回调对象(该对象在客户端调用BookService的时候指定,在介绍客户端代码的实现的时候会讲到)。在WCF中,回调对象通过当前OperationContext的GetCallback<T>方法获得(T代表回调契约的类型)。代码如下。

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text; 
    
    namespace WcfServiceLib
    {
        // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“BookService”。
        // 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 BookService.svc 或 BookService.svc.cs,然后开始调试。
        public class BookService : IBookService
        {
       
    
            /// <summary>
            /// 双工模式,回调显示结果
            /// </summary>
            /// <param name="name">名称</param>
            public void DisplayName(string name)
            {
    
                string result=string.Format("书籍名称:{0},日期时间{1}", name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                Console.WriteLine("\r\n" + result);
                ICallback call = OperationContext.Current.GetCallbackChannel<ICallback>();
                call.DisplayResult("回调---"+result);
            }
        }
    
    }

     

          注: OperationContext在WCF中是一个非常重要、也是一个十分有用的对象,它代表服务操作执行的上下文。我们可以通过静态属性 Current(OperationContext.Current)得到当前的OperationContext。借助 OperationContext,我们可以在服务端或者客户端获取或设置一些上下文,比如在客户端可以通过它为出栈消息(outgoing message)添加SOAP报头,以及HTTP报头(比如Cookie)等。在服务端,则可以通过OperationContex获取在客户端设置的 SOAP报头和HTTP报头。关于OperationContext的详细信息,可以参阅MSDN在线文档。

         步骤三:服务寄宿

         我们通过一个控制台应用程序完成对BookService的寄宿工作,并将所有的服务寄宿的参数定义在代码中。由于双工通信依赖于一个双工的信道栈,即依赖于一个能够支持双工通信的绑定,在此我们选用了WSDualHttpBinding。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.Text;
    using System.Threading.Tasks;
    using WcfServiceLib;
     
    
    namespace ConsoleHosting
    {
    
        class Program
        {
            static void Main(string[] args)
            {
                //创建宿主的基地址
                Uri baseAddress = new Uri("http://localhost:8080/BookService");
                //创建宿主
                using (ServiceHost host = new ServiceHost(typeof(BookService), baseAddress))
                {
                    //向宿主中添加终结点
                    host.AddServiceEndpoint(typeof(IBookService), new WSDualHttpBinding (), baseAddress);
                    if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
                    {
                        //将HttpGetEnabled属性设置为true
                        ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                        behavior.HttpGetEnabled = true;
                        behavior.HttpGetUrl = baseAddress;
                        //将行为添加到Behaviors中
                        host.Description.Behaviors.Add(behavior);
                        //打开宿主
                        host.Opened += delegate
          {
    
              Console.WriteLine("BookService控制台程序寄宿已经启动,HTTP监听已启动....,按任意键终止服务!");
          }; 
    
                        host.Open();
                        //print endpoint information                 
    
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        foreach (ServiceEndpoint se in host.Description.Endpoints)
                        {
                            Console.WriteLine("[终结点]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-绑定]: {2} \r\n\t [C-协定]: {3}",
                         se.Name, se.Address, se.Binding.Name, se.Contract.Name); 
                        } 
    
                        Console.Read();
                    }
                }
            }
        }
    }

         注: 在WCF预定义绑定类型中,WSDualHttpBindingNetTcpBinding均提供了对双工通信的支持,但是两者在对双工通信的实现机制上却有本质的区别。WSDualHttpBinding是基于HTTP传输协议的;而HTTP协议本身是基于请求-回复的传输协议,基于HTTP的通道本质上都是单向的。WSDualHttpBinding实际上创建了两个通道,一个用于客户端向服务端的通信,而另一个则用于服务端到客户端的通信,从而间接地提供了双工通信的实现。而NetTcpBinding完全基于支持双工通信的TCP协议。

     

         步骤四:实现回调契约

         在客户端程序为回调契约提供实现,在下面的代码中BookCallBack实现了回调契约ICallback,在DisplayResult方法中对书籍名称与日期的显示的输出。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks; 
    
    namespace WinClient
    {
    
        class BookCallBack : BookServiceReference.IBookServiceCallback
        {
    
            //声明一个delegate(委托)类型:delegateDispalyResult,该类型可以搭载返回值为空,参数只有一个(string型)的方法。 
            public delegate void delegateDispalyResult(string result); 
    
            //声明一个delegateDispalyResult类型的对象。该对象代表了返回值为空,参数只有一个(string型)的方法。它可以搭载N个方法。 
            public delegateDispalyResult mainThread; 
    
            public void DisplayResult(string result)
            {
                mainThread(result);//通过委托的方法在主界面中显示书籍名称与日期信息
                Console.WriteLine( result);
    
            }
        }
    }

     

         步骤五:服务调用

         接下来实现对双工服务的调用,这是一个通过Web 服务引用,添加相应的WCF服务调用对象(关于如何添加Web服务引用请参考前面的文章)。在服务调用程序中,先创建回调对象,并通过InstanceContext对回调对象进行包装,然后把InstanceContext对象做为参数传递给服务调用对象。

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.ServiceModel;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
     
    
    namespace WinClient
    {  
    
        public partial class Form1 : Form
        {
    
            BookCallBack call;
    
            public Form1()
            {
    
                InitializeComponent();
                //创建BookCallBack类的对象 
                BookCallBack call = new BookCallBack();
                call.mainThread += new BookCallBack.delegateDispalyResult(DisplayResult);
                instanceContext = new InstanceContext(call);
            }
    
            InstanceContext instanceContext;
    
            private void buttonTwoWay_Click(object sender, EventArgs e)
            {           
    
                BookServiceReference.BookServiceClient client = new BookServiceReference.BookServiceClient(instanceContext);
    
                //在BookCallBack对象的mainThread(委托)对象上搭载两个方法,在线程中调用mainThread对象时相当于调用了这两个方法。      
    
                textBox1.Text += string.Format("开始调用wcf服务:{0}\r\n\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                client.DisplayName("科学可以这样看丛书");
                textBox1.Text += string.Format("\r\n\r\n调用结束:{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            } 
    
            /// <summary> 
            /// 在界面上显示回调结果
            /// </summary> 
            /// <param name="result"></param> 
            private void DisplayResult(string result)
            {
    
                //判断该方法是否被主线程调用,也就是创建labMessage1控件的线程,当控件的InvokeRequired属性为ture时,说明是被主线程以外的线程调用。如果不加判断,会造成异常 
                if (this.textBox1.InvokeRequired)
                {
    
                    //再次创建一个BookCallBack类的对象 
                    call = new BookCallBack();
                    //为新对象的mainThread对象搭载方法 
                    call.mainThread += new BookCallBack.delegateDispalyResult(DisplayResult);
                    //this指窗体,在这调用窗体的Invoke方法,也就是用窗体的创建线程来执行mainThread对象委托的方法,再加上需要的参数(i) 
                    this.Invoke(call.mainThread, new object[] { result });
                }
                else
                {
                    textBox1.Text +="\r\n"+ result;
                }
            }
        }
    }

     

         在服务寄宿程序启用的情况下,运行客户端程序后,通过服务端执行的运算结果会通过回调客户端的操作显示出来,下面是最终输出的结果。

        

    转载于:https://www.cnblogs.com/chillsrc/p/5749211.html

    展开全文
  • WebSocket 是一个建立在 TCP 之上进行全双工通讯的应用层协议

    WebSocket 是一个建立在 TCP 之上进行全双工通讯的应用层协议

    我们知道,HTTP 协议采用的是请求/响应模式,服务端是不能主动向客户端推送数据的

    若要实现推送功能,一般都是采用 Ajax 轮询,但这样频繁的请求会给服务端带来极大的压力

    WebSocket 协议就是为了解决这种类似的场景而出现的,它允许服务端可以主动向客户端推送数据

    浏览器和服务器通过一次握手,就能在两者之间创建持续性的连接,进行双向数据传输,直至任意一方将其关闭

    目前,基于 SHA 加密方式的握手协议是使用最为广泛的,具体的过程如下:

    • 浏览器向服务器发起一个 HTTP 请求,但是这个请求与普通的请求不同,它会附加一些头部信息

    • 服务器解析头部信息,返回一个响应,这样就能建立起 WebSocket 连接

    一个典型的客户端请求如下:

    GET ws://localhost:5000 HTTP/1.1
    Host: localhost
    Origin: http://localhost:5000
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: 随机字符串
    Sec-WebSocket-Version: 13
    
    • Connection:必须设置为 Upgrade,表示客户端希望升级协议
    • Upgrade:必须设置为 websocket,表示希望将协议升级为 Websocket
    • Sec-WebSocket-Key:随机字符串,与响应中 Sec-WebSocket-Accept 的值做对比验证

    一个典型的服务端响应如下:

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: 根据 Sec-WebSocket-Key 计算得到
    Sec-WebSocket-Location: ws://localhost:5000
    
    • Connection:如果值为 Upgrade,表示服务端同意升级协议

    • Upgrade:如果值为 websocket,表示同意将协议升级为 Websocket

    • Sec-WebSocket-Accept:经过服务器确认并加密的 Sec-WebSocket-Key

      Sec-WebSocket-Key 的值拼上一段特殊的字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11

      然后使用 SHA1 对这个字符串做哈希,再进行 Base64 编码后,才能得到 Sec-WebSocket-Accept 的值

    在理解了原理之后,我们下面再来看一下具体要怎么使用 WebSocket

    在客户端浏览器中,可以通过构造函数 WebSocket() 创建一个对象,该对象能提供使用 WebSocket 的相关 API

    其中,参数 url 用于指定连接地址,参数 protocol 用于指定可接受的子协议

    WebSocket(url, [protocol])
    

    构造函数返回一个 WebSocket 对象,该对象常用的属性和方法如下:

    • readyState:只读属性,表示连接状态

      0 表示连接尚未建立,1 表示连接已经建立,可以进行通信

      2 表示连接正在关闭,3 表示连接已经关闭,或者不能打开

    • bufferedAmount:只读属性,在缓存中等待传输的字节数

    • onopen:指定 open 事件的监听函数,在连接建立时触发

    • onmessage:指定 message 事件的监听函数,在收到数据时触发

    • onerror:指定 error 事件的监听函数,在发生错误时触发

    • onclose:指定 close 事件的监听函数,在连接关闭时触发

    • send():发送数据

    • close():关闭连接

    var ws = new WebSocket('ws://localhost:5000')
    
    ws.onopen = function(event) { 
        console.log('open')
    }
    
    ws.onmessage = function(event) {
        console.log('message', event.data)
        ws.send('Goodbye')
        ws.close()
    }
    
    ws.onclose = function(event) {
    	console.log('close')
    }; 
    
    ws.onerror = function(event) {
    	console.log('error')
    }
    

    而对于服务器而言,WebSocket 只是一种协议而已,不同的语言和不同的框架会有不同的实现

    下面我们以 Node.js 为例,写一个简单的 Demo

    var express = require('express')
    var expressWs = require('express-ws')
    
    var app = express()
    
    expressWs(app)
    
    app.ws('/', function (ws, req){
        // ws:  WebSocket 实例
        // req:Request 实例
        ws.send('Hello')
        ws.on('message', function (msg) {
            console.log(msg)
        })
    })
    
    var server = app.listen(5000, '127.0.0.1', function() {
        console.log('server running at http://127.0.0.1:5000')
    })
    

    【 阅读更多 HTML 系列文章,请看 HTML学习笔记

    展开全文
  • 1、基本概念 常用于数据通信的传输方式有单工、半双工、全双工和多工方式。单工方式:数据仅按一个固定方向传送。...全双工方式:允许双方同时进行数据双向传送,但一般全双工传输方式的线路和设备较复杂。 ...
  • 图解HTTP读书笔记()

    2016-10-31 10:40:30
    使用浏览器进行全双工通信的WebSocket HTTP/2.0 Web服务器管理文件的WebDAV 消除HTTP瓶颈的SPDYGoogle在2010年发布了SPDY,其开发目标旨在解决HTTP的性能瓶颈,缩短Web页面的加载时间(50%)。 SPDY SPDY以会话层的...
  • 串口通信是单片机一个重要的部分,单片机和PC,...单片机的串口是全双工异步串口通信方式。通过TXD(P3.1)发送,RXD(P3.0)引脚接收输入。串口传送数据是一帧一帧发送的,它有四种工作方式,同时也可以做并行I/O的扩展。
  • WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只...
  • WebSockets是一种支持全双工通信的套接字。现代的html5通过js api使得浏览器天生支持webSocket。但是Websockets在移动端以及服务器之间的通信也非常有用,在这些情况下可以复用一个已经存在的TCP连接。 1.处理...
  • 链路捆绑 链路聚合的应用场景 链路聚合一般部署在核心结点,以便提升整个网络的数据吞吐量。...Eth-Trunk链路两端相连的物理接口的数量、速率、双工反射光、流控方式必须一致。 二层链路聚合配置 1.创建聚合口et...
  • 、Socket之TCP编程

    2014-06-03 16:46:00
     TCP是Transmission Control Protocol(传输控制协议)的简称,是TCP/IP体系中面向连接的运输层协议,在网络中提供全双工的和可靠的服务。  TCP最主要的特点:  (1)是面向连接的传输层协议;  (2)每个TCP...
  • spring boot 开发—第篇集成websocket

    千次阅读 2018-05-30 00:09:15
    WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 1.2、为什么需要WebSocket? HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端...
  • 篇 消息通信模式(上) 请求应答与单向 通过前两篇的学习,我们了解了服务模型的一些特性如会话和实例化,今天我们来进一步学习服务模型的另一个重要特性:消息通信模式。 WCF的服务端与客户端在通信时有三...
  • SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚。SPI 是一个环形总线结构,由 ss(cs)、sck、sdi、sdo 构成,时序主要是在 sck 的控制下,两个双向移位寄存器进行...
  • 篇 消息通信模式(上) 请求应答与单向 通过前两篇的学习,我们了解了服务模型的一些特性如会话和实例化,今天我们来进一步学习服务模型的另一个重要特性:消息通信模式。 WCF的服务端与客户端在通信时有三...

空空如也

空空如也

1 2 3 4
收藏数 66
精华内容 26
关键字:

双工九