2016-09-22 2 views
2

Ich bin ein C# Telegramm-Client von TLSharp benutzerdefinierte Codierung starten und modifiziert sie, um Schicht 54Telegramm Client-Updates und API-Anfragen über derselben Sitzung

ich beide Empfangs Updates behandeln die von Telegramm-Servern und mit unterstützen wollen API ohne dafür eine separate Sitzung zu öffnen.

Das Problem ist im Grunde ein Multithread-Zugriff auf den Socket, der mit dem Telegram Server verbunden ist.

Hier ist es das Schema:

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

Das Problem ist, dass, um zu erhalten ständig aktualisiert von Telegramm-Server ich eine Weile (true) Zyklus verwenden, die im Grunde ist schematisiert als:

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 
} 

Nun, wenn ich will, zum Beispiel Abfrage-Telegramm-Server für die Liste aller Chats, in denen bin ich registriert, ich habe um auf die socket_1 zuzugreifen, um diese Anfrage zu senden und auf die Antwort zu warten, aber socket_1 hört zu und ich kann natürlich nicht darauf zugreifen.

Eine Lösung könnte sein, einen Vektor der Anfrage zu verwenden, die verarbeitet werden, nachdem eine Aktualisierung empfangen wurde, die Idee, so etwas wie diese:

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) 
       } 
      } 
    } 

Diese Lösung ist ziemlich schrecklich, da wir einen Prozess Anfrage nur nach einem Update, und so kein Update = keine Anfrage bearbeitet ...

eine andere Möglichkeit, eine Art von Verriegelungsmechanismus nach einem Schema wie folgt verwenden könnte:

//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) 
} 

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

Das große Problem dabei ist, dass sobald der Thread_updater die Sperre übernimmt, diese erst wieder freigegeben wird, wenn ein Update empfangen wurde ... Dies ist im Grunde das gleiche Problem wie zuvor. Ich habe versucht, auch mit CancellationTasks oder Socket Timeout zu spielen, aber ich fühlte mich, als würde ich einen falschen Weg gehen.

Gibt es eine elegante Lösung/Muster, um dies auf eine saubere Art zu handhaben? Wie gesagt, ich möchte nicht 2 Sitzungen öffnen, da es logisch falsch ist (es wäre wie zwei Clients zu haben, um den Empfang von Updates und das Senden von Nachrichten zu behandeln).

Antwort

0

Es gibt einen einfacheren Weg, dies zu tun.

Dies ist, was ich in VB.net getan habe, sollte aber auch für Sie in C# arbeiten.

  • Richten Sie Ihre Buchse und an Telegramm-Server verbinden, um Daten hören
  • Datenpuffer wie es
  • Prozess empfangenen Daten empfangen werden - dekodieren TL-Typen, die von Telegramm und entweder speichern/reagieren i.e Senden von Nachrichten als Antwort auf das, was Sie
  • beim Hören erhalten haben, können Sie auch
  • Nachrichten über die gleiche Buchse senden

Beispielcode

Region „Variablen“

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 
Verwandte Themen