none
Клиент-серверное приложение некорректно передает данные RRS feed

  • Вопрос

  • Разрабатываю клиент-север приложение. Как-то не правильно реализовал прием и, наверно, передачу данных, потому что если сообщение от клиента будет больше буфера (1024 байт) есть шанс приема неправильного символа.

    Код..

    Private Sub DoRead(ByVal ar As IAsyncResult)
            Dim BytesRead As Integer
            Try
                'Заканчиваем асинхронное чтение и считаем кол-во байт
                SyncLock GetDataLock
                    BytesRead = myClient.GetStream.EndRead(ar)
                End SyncLock
    
                If BytesRead < 1 Then
                    'Если нет байт для чтения
                    ping_FSLogMessage("Нет байт для чтения")
                    GC.Collect() 'Чистим за собой
                    Exit Sub
                End If
    
                'Кодировка UTF8 
                sMessage &= System.Text.Encoding.UTF8.GetString(readBuffer, 0, BytesRead)
                If sMessage.Contains(Chr(4)) Then
                    Dim tmpMessage As String = Split(sMessage, Chr(4))(0)
                    RaiseEvent MessageReceived(Me, tmpMessage)
                    sMessage = Split(sMessage, Chr(4))(1)
                End If
    
                'И начинаем новое асинхронное чтение
                SyncLock GetDataLock
                    myClient.GetStream.BeginRead(readBuffer, 0, READ_BUFFER_SIZE, AddressOf DoRead, Nothing)
                End SyncLock
            Catch e As Exception
                GC.Collect() 'Чистим за собой
            End Try
        End Sub

    Отправка сообщения следующая

    SyncLock SendDataLock
                    Dim writer As New IO.StreamWriter(myClient.GetStream)
                    writer.Write(data & Chr(4))
                    writer.Flush()
                End SyncLock

    Похоже нужно как-то сообщить какое количество байт информации нужно принять, прежде чем обработать строку... но как это сделать? Подскажите пожалуйста.  

    17 ноября 2013 г. 14:35

Ответы


  • Dim Streem As Net.Sockets.NetworkStream = myClient.GetStream
    Dim r As New IO.BinaryReader(Streem)
    Dim s As String = r.ReadString
    Так можно считать данные из потока не заморачиваясь о числе байт в нем. Соответственно считается что в потоке текстовая строка, а не набор прочих байт.

    Yes, yes - am back!

    • Помечено в качестве ответа Siompc 18 ноября 2013 г. 14:32
    18 ноября 2013 г. 6:45
    Отвечающий

Все ответы


  • Dim Streem As Net.Sockets.NetworkStream = myClient.GetStream
    Dim r As New IO.BinaryReader(Streem)
    Dim s As String = r.ReadString
    Так можно считать данные из потока не заморачиваясь о числе байт в нем. Соответственно считается что в потоке текстовая строка, а не набор прочих байт.

    Yes, yes - am back!

    • Помечено в качестве ответа Siompc 18 ноября 2013 г. 14:32
    18 ноября 2013 г. 6:45
    Отвечающий
  • Попробовал вставить в DoRead, зависает на строке 

    Dim s As String = r.ReadString

    Если вставить в процедуру New

     'Начало чтения
        Public Sub New(ByVal Client As TcpClient)
            myClient = Client
            sMessage = ""
    
            Dim Stream As Net.Sockets.NetworkStream = myClient.GetStream
            Dim r As New BinaryReader(Stream)
            Do
                If Stream.DataAvailable Then
                    ping_FSLogMessage(r.ReadString)
                End If
            Loop
    
            'Начинаем асинхронное чтение с записью в readBuffer
            myClient.GetStream.BeginRead(readBuffer, 0, READ_BUFFER_SIZE, AddressOf DoRead, Nothing)
        End Sub

    Все равно зависает на чтении строки...

    18 ноября 2013 г. 9:21
  • Клиент и сервер это разные процессы или один? Просто если Do...Loop в том же потоке, что и отправка сообщения, то отправить вы ничего не сможете, поток будет висеть.

    Ожидание сообщения r.ReadString нужно производить в отдельном потоке


    Yes, yes - am back!

    18 ноября 2013 г. 9:25
    Отвечающий
  • Это разные. Просто в сервере тоже создается класс приема сообщений как и в клиенте. Т.е. Сервер прослушивает и создает класс TcpClient в котором читает все данные от клиента. А клиент создает тот же класс, в котором читает сообщения от сервера. На каждый такой класс выделяется отдельный поток в котором и обрабатывается процедура DoRead.

    18 ноября 2013 г. 9:33
  • Так а что не работает конкретно?

    Yes, yes - am back!

    18 ноября 2013 г. 9:34
    Отвечающий
  •    'Начало чтения
        Public Sub New(ByVal Client As TcpClient)
            myClient = Client
            sMessage = ""
            AddHandler MessageReceived, AddressOf onMessageReceived 'Обработчик сам на себя
    
            Dim Thread As New Thread(New ParameterizedThreadStart(AddressOf sss))
            Thread.Start()
        End Sub
    
    
    
    
      Private Sub sss(ByVal obj As Object)
            Dim Stream As Net.Sockets.NetworkStream = myClient.GetStream
            Dim r As New BinaryReader(Stream)
            Do
                If Stream.DataAvailable Then
                    ping_FSLogMessage(r.ReadString)
                End If
            Loop
        End Sub
    Даже если выношу в отдельный поток принятие сообщений он все равно при приходе сообщения останавливается на строке  
    r.ReadString
    И все... все зависает

    18 ноября 2013 г. 9:41
  • Теоретически на этом месте идет ожидание сообщения от клиента, то есть это зависание вполне нормально, поэтому я не пойму что именно не получается

    Yes, yes - am back!

    18 ноября 2013 г. 9:43
    Отвечающий
  • Он ожидает, но не принимает :) Посылаю сообщение "test", но при отправке начинает висеть как клиентское приложение, так и серверное. Дальнейшая отправка невозможна. 
    18 ноября 2013 г. 9:47
  • ОК. В данный момент как раз работаю на клиент-серверным приложением, поэтому щас очищу свой код от ненужного и выложу... 

    Yes, yes - am back!

    18 ноября 2013 г. 9:49
    Отвечающий
  • Спасибо большое! :)
    18 ноября 2013 г. 9:54
  • Ну во первых к серверу будет подключаться не один клиент это нужно учесть.

    Вот набросок Сервера

    Class MainWindow 
        Private Delegate Sub ThreadD()
        Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
            Dim GN As New ThreadD(AddressOf Listen)
            GN.BeginInvoke(Nothing, Nothing)
        End Sub
        Private Sub Listen()
            Dim tcplist As New Net.Sockets.TcpListener(Net.IPAddress.Any, 0000)
            tcplist.Start()
            Do
                Dim curcli As Net.Sockets.TcpClient = tcplist.AcceptTcpClient
                Dim newclient As New ClientHandler(curcli)
                'Здесь можно добавить newclient в некий глобальный список, что бы потом с ним взаимодействовать
                Dim GN As New ThreadD(AddressOf newclient.Listener)
                GN.BeginInvoke(Nothing, Nothing)
            Loop
        End Sub
    End Class
    'Класc управления взаимодействия с конкретным клиентом
    Public Class ClientHandler
        Private Delegate Sub ThreadD()
        Dim Client As Net.Sockets.TcpClient
        Dim Streem As Net.Sockets.NetworkStream
        Public Sub New(ByVal client As Net.Sockets.TcpClient)
            Me.Client = client
            Streem = client.GetStream
        End Sub
        Public Sub Listener()
            Dim r As New IO.BinaryReader(Streem)
            Do
                MsgBox(r.ReadString)
            Loop
        End Sub
    End Class

    Приложение клиент (набросок)

    Class MainWindow 
        Dim SerWork As New ServerWorkerClass
    
        Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
            SerWork.Start()
        End Sub
    End Class
    
    Public Class ServerWorkerClass
        Const Adress As String = "localhost"
        Const Port As Integer = 0
        Dim client As Net.Sockets.TcpClient
        Private Delegate Sub ThreadD()
        Public Property Streem As Net.Sockets.NetworkStream
        Public Sub New()
        End Sub
        Public Sub Start()
            Dim GN As New ThreadD(AddressOf Connect)
            GN.BeginInvoke(Nothing, Nothing)
        End Sub
        Private Sub Connect()
            Try
                client = New Net.Sockets.TcpClient(Adress, Port)
                Streem = client.GetStream
                Dim NewAsyncCom As New AsyncSendCommandClass("бла бла", Me)
                NewAsyncCom.SendCommand()
                'Запускаем слушаетеля, для прослушки сообщений от сервера
                Listener()
            Catch ex As Exception
            End Try
        End Sub
        Private Sub Listener()
            Dim r As New IO.BinaryReader(Streem)
            'Ждет сообщений от сервера
            Do
                If Streem.CanRead Then
                    MsgBox(r.ReadString)
                End If
            Loop
        End Sub
    
    End Class
    ''' <summary>
    ''' Класс для отправки комманд серверу
    ''' </summary>
    ''' <remarks></remarks>
    Public Class AsyncSendCommandClass
        Public Delegate Sub CallbackSub()
        Private Delegate Sub ThreadD()
        Dim Body As String
        Dim Parent As ServerWorkerClass
        Public Sub New(_body As String, ByRef _parent As ServerWorkerClass)
            Body = _body
            Parent = _parent
        End Sub
        Public Sub SendCommand()
            Dim GN As New ThreadD(AddressOf CreateWrite)
            GN.BeginInvoke(Nothing, Nothing)
        End Sub
        Private Sub CreateWrite()
            Try
                Dim BinWriter = New IO.BinaryWriter(Parent.Streem)
                BinWriter.Write(Body)
                BinWriter.Flush()
            Catch ex As Exception
            End Try
        End Sub
    End Class

    Здесь два класса. Один в отдельном потоке держит постоянную связь с сервером и слушает от него сообщения. Второй создается в приложение при необходимости что то отправить серверу

    Я не проверял работоспособность в таком виде, просто покрамсал мой код, но в теории должно сработать.


    Yes, yes - am back!

    18 ноября 2013 г. 11:20
    Отвечающий
  • Все :) Причина в зависании была в том, что сообщение посылалось не через IO.BinaryWriter а через IO.StreamWriter :) Спасибо огромное, LXGDARK ))

    18 ноября 2013 г. 14:32