[sixinice 原创翻译] AsyncStreams 的说明(教程)

Discussion in 'Chinese Forum' started by 52manhua, Nov 27, 2017.

  1. 52manhua

    52manhua Member Licensed User

    译者注: AsyncStreams 在传输网络数据中是一个非常有用的对象,翻一下大家好学习.

    原始链接:https://www.b4x.com/android/forum/threads/asyncstreams-tutorial.7669/

    在 RandomAccessFile 库中提供了一个新的对象类型: AsyncStreams.
    AsyncStreams 帮助你 从 inputstream 读取数据 从 OutputStream 写入数据,不需要阻断你的程序执行. 读取和写入是在两个不同的线程进行的.

    当接收到新的数据的时候,事件通过这个 NewData 事件触发
    当你写入数据到 OutputStream 中的时候,数据添加到一个内部的序列,发送到系统后台中去

    AsyncStreams 在使用 网络数据流 或者 蓝牙数据流 是非常有用的.

    就是说,如果你的程序可能为等待一个值而不得不中断的时候, 就可以使用这样的流对象.
    我们用来处理 串口 和网络的这个方法,等同于使用一个计时器随时检测是否在 byte(数组) 正在被传输,是否存在等待传输的 byte. 但是即使如此,仍然可能有 byte 处于不可用(传输)的状态,我们需要等待直到他们可用.
    使用 AsyncStreams 有简单,安全的优点.

    这个 AsyncStreams 对象有两种模式: 普通模式 ,"前缀模式" 两种模式Regular mode and "prefix mode". 用起来就像命名描述的那样.

    这段代码演示了一个简单的程序,这个程序发送 文本到一个已经连接上的 设备或者电脑:

    Code:
    Sub Process_Globals
      
    Dim AStreams As AsyncStreams
      
    Dim Server As ServerSocket
      
    Dim Socket1 As Socket
      
    End Sub
      
    Sub Globals
      
    Dim EditText1 As EditText
      
    End Sub

      
    Sub Activity_Create(FirstTime As Boolean)
      
    If FirstTime Then
      
    Server.Initialize(5500"Server")
      
    Server.Listen
      
    Log("MyIp = " & Server.GetMyIP)
      
    End If
      EditText1.Initialize(
    "EditText1")
      EditText1.ForceDoneButton = 
    True
      
    Activity.AddView(EditText1, 10dip10dip300dip60dip)
      
    End Sub

      
    Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
      
    If Successful Then
      
    ToastMessageShow("Connected"False)
      Socket1 = NewSocket
      
    'Can only use prefix mode if both sides of the connection implement the prefix protocol!!!
      AStreams.InitializePrefix(Socket1.InputStream, False, Socket1.OutputStream, "AStreams")
      
    Else
      
    ToastMessageShow(LastException.Message, True)
      
    End If
      
    Server.Listen
      
    End Sub

      
    Sub AStreams_NewData (Buffer() As Byte)
      
    Dim msg As String
      msg = 
    BytesToString(Buffer, 0, Buffer.Length, "UTF8")
      
    ToastMessageShow(msg, False)
      
    Log(msg)
      
    End Sub

      
    Sub AStreams_Error
      
    ToastMessageShow(LastException.Message, True)
      
    Log("AStreams_Error")
      
    End Sub

      
    Sub AStreams_Terminated
      
    Log("AStreams_Terminated")
      
    End Sub

      
    'press on the Done button to send text
      Sub EditText1_EnterPressed
      
    If AStreams.IsInitialized = False Then Return
      
    If EditText1.Text.Length > 0 Then
      
    Dim buffer() As Byte
      buffer = EditText1.Text.GetBytes(
    "UTF8")
      AStreams.Write(buffer)
      EditText1.SelectAll
      
    Log("Sending: " & EditText1.Text)
      
    End If
      
    End Sub

      
    Sub Activity_Pause(UserClosed As Boolean)
      
    If UserClosed Then
      
    Log("closing")
      AStreams.Close
      Socket1.Close
      
    End If
      
    End Sub
    只要有一个连接,我们就能初始化 AsyncStreams 对象:
    Code:
    AStreams.InitializePrefix(Socket1.InputStream, False, Socket1.OutputStream, "AStreams")
    发现有数据传输的时候,我们就能转换 byte 数组到字符串显示显示:
    Code:
    Sub AStreams_NewData (Buffer() As Byte)
      
    Dim msg As String
      msg = 
    BytesToString(Buffer, 0, Buffer.Length, "UTF8")
      
    ToastMessageShow(msg, False)
      
    End Sub
    当用户输入字符串到 edittext 中的时候, 文本就被发送:

    Code:
    Sub EditText1_EnterPressed
      
    If AStreams.IsInitialized = False Then Return
      
    If EditText1.Text.Length > 0 Then
      
    Dim buffer() As Byte
      buffer = EditText1.Text.GetBytes(
    "UTF8")
      AStreams.Write(buffer)
      EditText1.SelectAll
      
    Log("Sending: " & EditText1.Text)
      
    End If
      
    End Sub
    "前缀"模式

    当对象初始化为前缀模式的时候,数据需要满足下面的条件: 每一条消息,比特数组应该给出前缀标明数组的长度 (长度为 int 类型). 因此如果其他设备发送给我们一个 100byte 的数据这条数据需要包含4个比特的容量保存 100 这个值,后面跟着 100 比特的数据
    NewData 事件会触发因为接收到这个 100 个字节事件接收的数据并不包含4个字节的前缀.
    当你发送数据,使用 Write 或者 Write2 的时候,比特数组的长度自动作为前缀添加到消息里面.
    如果你在传输的两边设备都能使用前缀的时候,那么尽量用这种模式. 因为拥有 带有长度的前缀信息 ,当 NewData 事件触发时会获得完整的数据 . 举个例子,需要100比特的数据,只有 60比特传输完毕,程序会等待 剩下的 40比特到达. 在普通模式中,这个事件会触发两次,你需要自己处理两个部分的消息
    AsyncStreams 对象同样需要处理这钟情况: 当只需要 100 比特的时候,却有多余数据传输到达了.

    注意,这个 WriteStream 方法,只适用于前缀模式,使用一个包含错误侦测内部的协议.

    错误处理

    错误事件会被触发,如果有任何错误的话. 你能使用 LastException 找到错误的原因. 在多数情况中如果发生错误,你想要关闭这个链接.

    关闭连接对象的时候, Terminated 事件会被触发.

    相关链接:
    AsyncStreamsText 类 ,提供了一个备选,使用 前缀模式来传输文本.
    新的例子,配合使用 starter Service 和 B4xSerialzator: https://www.b4x.com/android/forum/threads/network-asyncstreams-b4xserializator.72149/

    如何选择 模式 和 框架?

    有四种模式或者框架可以选择: AsyncStreams, AsyncStreams前缀模式,AsyncStreams 文本类,AsyncStreams 对象类

    当双方的连接都是用 前缀模式 , 只能使用前缀模式在很多的情况下下,你都能在传输的双方使用这种模式. 举个例子,比如建立一个聊天程序.
    你需要使用 AsyncStreamsObject. AsyncStreamsObject 不能显示进度,虽然能传输任何大小的文件. 如果你需要传输一个非常大的文件,你可以参考一下下面附件中的 FileTransfer 例子.

    如果你和一个非 b4a 的应用进行交互, 不能使用前缀模式协议,你有两个选择:
    -其一,数据发送和接收是基于文本,每个消息都以 一个 结束字符结尾,你就可以使用 AsyncStreamsText 举个例子,比如你需要连接一个第三方的 gps. 注意你可能修改这个类,适应不同的分隔符号.
    使用 AsyncStreamsText 类 的优点在于,它能正确地建立消息,你不会收到截断的消息或者多余的消息You will not receive partial messages (or multiple messages together).
    - 其二,在其他情况中,你应该使用 AsyncStreams 的普通模式. 在不能确定,每一条发送的消息都是一条单独的消息的时候. 事实上, 或多或少地保证不会发生这样的情况. 所以你的代码需要自己解决, 修正接收和 处理这些消息.

    要点

    当接受到一个新消息的时候,后台线程需要处理数据的,朝主线程队列发送一条消息. 这样的消息会触发 NewData 事件. 如果你接受很多重复的消息的时候,如果显示一个模式窗口,比如 msgbox ,事件的队列可能会发生改变.
     
    Last edited: Nov 27, 2017
Loading...