2016-09-22 31 views
2

我编码一个自定义的C#电报客户从TLSharp开始,修改了它以支持层54电报客户端更新和在同一会话API请求

我要处理来自服务器的电报都接收更新和使用API而无需打开单独的会话来执行此操作。

问题基本上是多线程访问连接到电报服务器的套接字。

这方案:

TelegramClient < ------- socket_1 [(session_1)] --------> TelegramServer

的问题是,为了收到我用了一段时间(真)周期电报服务器 基本上都是模式化的不断更新:

while(true){ 
    data_cipher = await socket_1.receive() // listen for new stuff from the socket_1 
    data_plain = decrypt(data_cipher) // decrypt 
    processUpdate(data_plain) // process the update 
} 

现在,如果我想要的,例如,对于所有的聊天记录列表中查询电报服务器中,我是注册,我有 访问socket_1以发送此请求,并等待答案,但socket_1正在侦听,我显然无法访问它。

一种解决方案可能是使用的要求更新已被接收后,将被处理的载体, 的想法是这样的:

List<Request> pending_requests = new List<Request>() // list of requests added by another thread 

    while(true){ 

     data_cipher = await socket_1.receive() // listen for new stuff from the socket_1 
     data_plain = decrypt(data_cipher) // decrypt 
      processUpdate(data_plain) // process the update 

     if(pending_requests.Count != 0){ 
      foreach(Request r in pending_requests){ 
        processRequest(r) 
       } 
      } 
    } 

该解决方案是非常可怕的,因为我们处理只有更新后的要求,因此没有更新=没有请求处理......

另一种可能是使用某种类型的锁机构下面这样一个方案:

//Thread_updater 
//-------------------------------------------- 
while(true){ 

     lock(socket_1){ 
    data_cipher = await socket_1.receive() // listen for new stuff from the socket_1 
    } 

    data_plain = decrypt(data_cipher) // decrypt 
     handleUpdate(data_plain) // process the update 

} 
-------------------------------------------- 


//Thread_requests 
//-------------------------------------------- 
Request r = new Request(<stuff>); 

lock(socket_1){ 
    await sendRequest(r,socket_1) 
} 

-------------------------------------------- 

这个问题的最大问题是,一旦Thread_updater接受锁定,它将永远不会释放它,直到接收到更新为止......这与以前基本相同。 我也尝试玩取消任务或套接字超时,但我觉得我走错了路。

有没有一个优雅的解决方案/模式,以便以一种整洁的方式处理这个问题? 如上所述,我不想打开2个会话,因为它在逻辑上是错误的(这就像有两个客户端来处理接收更新和发送消息)。

回答

0

有更简单的方法来做到这一点。

这就是我在VB.net中所做的,但也应该在C#中为你工作。

  • 设置您的插座,并连接到电报的服务器,因为它是收到
  • 过程中接收到的数据监听数据
  • 缓存数据 - 从收到的电报解码TL类型,要么存储/反应我。E在响应消息发送到所收到的内容
  • 一边听,你也可以通过相同的插座发送消息

示例代码

地区的“变量”

Const BUFFER_SIZE = 1024 * 64 

    Private soc As Socket 
    Private ep As EndPoint 
    Private connected As Boolean 
    Private efSent As Boolean 
    Private are As New AutoResetEvent(False) 
#End Region 

#Region "Network" 
    Public Sub Connect(Optional ip As String = "149.154.167.40", Optional port As Integer = 443) 
     soc = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) With {.SendBufferSize = BUFFER_SIZE, .SendTimeout = 3000} 

     Try 
      ep = GetIPEndPointFromHostName(ip, port) 

      Dim arg = New SocketAsyncEventArgs With {.RemoteEndPoint = ep, .UserToken = "Connect_ARGS"} 
      AddHandler arg.Completed, AddressOf IO_Handler 

      While Not connected 
       Try 
        If Not soc.ConnectAsync(arg) Then 
         IO_Handler(soc, arg) 
        End If 

        are.WaitOne(4000) 
       Catch ex As Exception 
        Thread.Sleep(1000) 
       End Try 
      End While 

     Catch ex As Exception 
      Log("Connect: " & ex.ToString, ConsoleColor.Red, True) 
     End Try 

     ReadData() 
    End Sub 

    Public Sub Disconnect() 
     connected = False 
     loggedin = False 

     soc.Disconnect(False) 
     soc = Nothing 

     Log("Disconnect", ConsoleColor.DarkYellow, True, True, True) 
    End Sub 

    Private Sub Send(m As PlainMessage) 
     SendData(m.data, True) 
    End Sub 

    Private Sub Send(m As EncryptedMessage) 
     SendData(m.data, True) 
    End Sub 

    Private Sub SendData(b() As Byte, Optional read As Boolean = False) 
     b = TCPPack(b) 

     Dim arg = New SocketAsyncEventArgs With {.RemoteEndPoint = ep, .UserToken = "Send_ARGS"} 
     AddHandler arg.Completed, AddressOf IO_Handler 
     arg.SetBuffer(b, 0, b.Length) 

     Try 
      If Not soc.SendAsync(arg) Then 
       IO_Handler(soc, arg) 
      End If 
     Catch ex As Exception 
      Log("SendData: " & ex.ToString, ConsoleColor.Red) 
     End Try 
    End Sub 

    Private Sub IO_Handler(sender As Object, e As SocketAsyncEventArgs) 
     Select Case e.SocketError 
      Case SocketError.Success 
       Select Case e.LastOperation 
        Case SocketAsyncOperation.Connect 'A socket Connect operation. 
         connected = True 
         Log("Connected to " & e.ConnectSocket.RemoteEndPoint.ToString, ConsoleColor.Green) 
         are.Set() 
        Case SocketAsyncOperation.Disconnect 
         connected = False 
         RaiseEvent Disconneted() 
        Case SocketAsyncOperation.Receive 'A socket Receive operation. 
         If e.BytesTransferred = 0 Then 'no pending data 
          Log("The remote end has closed the connection.") 
          If connected Then 
           ReadData() 
          End If 

          connected = False 
          loggedin = False 

          Exit Sub 
         End If 
         HandleData(e) 
       End Select 
      Case SocketError.ConnectionAborted 
       RaiseEvent Disconneted() 
     End Select 
    End Sub 

    Private Function GetIPEndPointFromHostName(hostName As String, port As Integer) As IPEndPoint 
     Dim addresses = System.Net.Dns.GetHostAddresses(hostName) 
     If addresses.Length = 0 Then 
      Log("Unable to retrieve address from specified host name: " & hostName, ConsoleColor.Red) 
      Return Nothing 
     End If 
     Return New IPEndPoint(addresses(0), port) 
    End Function 

    Private Function TCPPack(b As Byte()) As Byte() 
     Dim a = New List(Of Byte) 
     Dim len = CByte(b.Length/4) 

     If efSent = False Then 'TCP abridged version 
      efSent = True 
      a.Add(&HEF) 
     End If 

     If len >= &H7F Then 
      a.Add(&H7F) 
      a.AddRange(BitConverter.GetBytes(len)) ' 
     Else 
      a.Add(len) 
     End If 

     a.AddRange(b) 'data, no sequence number, no CRC32 

     Return a.ToArray 
    End Function 

    Private Sub ReadData() 
     Dim arg = New SocketAsyncEventArgs With {.RemoteEndPoint = ep, .UserToken = "Read_ARGS"} 
     AddHandler arg.Completed, AddressOf IO_Handler 

     Dim b(BUFFER_SIZE - 1) As Byte 
     arg.SetBuffer(b, 0, BUFFER_SIZE) 

     Try 
      If Not soc.ReceiveAsync(arg) Then 
       IO_Handler(soc, arg) 
      End If 
     Catch ex As Exception 
      Log("ReadMessages: " & ex.ToString, ConsoleColor.Red) 
     End Try 
    End Sub 

    Private Sub HandleData(e As SocketAsyncEventArgs) 
     Log("<< " & B2H(e.Buffer, 0, e.BytesTransferred), ConsoleColor.DarkGray, True, logTime:=False) 
     Try 
      Dim len As Integer = e.Buffer(0) 
      Dim start = 1 

      If len = &H7F Then 
       len = e.Buffer(1) 
       len += e.Buffer(2) << 8 
       len += e.Buffer(3) << 16 
       start = 4 
      End If 

      len = 4 * len 

      Dim d(len - 1) As Byte 
      Array.Copy(e.Buffer, start, d, 0, len) 

      ProcessResponse(d) 
     Catch ex As Exception 

     End Try 

     ReadData() 
    End Sub 

    Private Sub ProcessResponse(data As Byte()) 
     'process the data received - identify the TL types returned from Telegram, then store/handle each as required 
    End Sub 
#End Region