none
Проблемой в программе клиент-сервер. RRS feed

  • Вопрос

  • Здравствуйте! Пытаюсь создать программы клиент и сервер. Собрал, можно сказать по кусочкам с интернета. Сначала сервер не видел сообщения клиента, но мог отправить сообщение всем подключенным клиентам, но потом вовсе перестало все работать. Помогите пожалуйста с этим вопросом. Исходники клиента и сервера здесь:

    http://filestored.narod.ru/ClientServer.rar

    В работе с клиентскими и серверными программмами я еще новичек, поэтому не судите строго :)

    24 декабря 2010 г. 13:56

Ответы

  • Не, программа запускает асинхронное чтение (создает новый поток) только когда подключился новый пользователь к серверу. Метод AcceptTcpClient блокирующий, т.е. программа висит на этом месте и ждет пока кто-то подключится, потом создает для клиента поток новый, в котором будет выполнять работу с клиентом, в тоже время основной поток в цикле начинает опять ожидать нового клиента, и так все время..

    У вас вообще не появляется окно сервера, т.к. в загрузке формы стоит бесконечный цикл, вам по-хорошему запуск сервера нужно в отдельный поток, который будет принимать входящих клиентов, или же использовать асинхронный прием listener.BeginAcceptTcpClient

    Для теста, можете сделать выход из цикла, при первом удачном подключении:

            LB1.Items.Add("Новое подключение: ожидание авторизации")
            Exit Do
          Loop Until False
    

    И еще обрабатывайте исключения на сервере:

            LB1.Items.Add("Новое подключение: ожидание авторизации")
            Exit Do
          Loop Until False
        Catch ex As SocketException
          MessageBox.Show("SocketException:" & ex.ToString())
        End Try
    


    Для связи [mail]
    • Помечено в качестве ответа Abolmasov Dmitry 27 декабря 2010 г. 20:47
    25 декабря 2010 г. 9:37

Все ответы

  • Упростил совсем код. Вот исходники:

     

    СЕРВЕР:

    Imports System.Net.Sockets
    Imports System.Text

    Public Class Form1
    Dim Client As TcpClient
    Dim listener As TcpListener 'Ожидание подключений
    Dim ReadSize As Short = 255
    Dim ReadBuffer(ReadSize) As Byte
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Try
    'Запускаем ожидание входящих запросов на указанный IP
    listener = New TcpListener(System.Net.IPAddress.Any, 10000) 'ЗДЕСЬ УКАЗЫВАЕТСЯ IP
    listener.Start()
    Do 'Если кто то пытается подключится

    Dim Client As Object = listener.AcceptTcpClient() 'Клиент ожидает подключения...
    StartLisen(Client) 'Запускаем асинхронное чтение

    'Сообщаем что кто то подключается...
    LB1.Items.Add("Новое подключение: ожидание авторизации")
    Loop Until False
    Catch
    End Try
    End Sub

    Private Sub StartLisen(ByVal NewClient As TcpClient)
    Me.Client = NewClient

    'Начинаем асинхронное чтение, с сохранением в readBuffer
    Me.Client.GetStream.BeginRead(ReadBuffer, 0, ReadSize, AddressOf StreamReceiver, Nothing)
    End Sub
    Private Sub StreamReceiver(ByVal ar As IAsyncResult)
    Dim BytesRead As Integer
    Dim UserMessage As String

    Try
    'Делаем так, что бы никто другой не мог использовать этот поток в то же время.
    SyncLock Me.Client.GetStream
    'Заканчиваем чтение и считаем кол-во байтов
    BytesRead = Me.Client.GetStream.EndRead(ar)
    End SyncLock

    'Преобразовываем байтовый массив (длинна - 1)
    UserMessage = Encoding.GetEncoding(1251).GetString(ReadBuffer, 0, BytesRead - 1)

    'Добавляем запись
    LB1.Items.Add(UserMessage)

    'Делаем так, что бы никто другой не мог использовать этот поток в то же время.
    SyncLock Me.Client.GetStream
    'Начинаем новое асинхронное чтение, с сохранением в readBuffer
    Me.Client.GetStream.BeginRead(ReadBuffer, 0, ReadSize, AddressOf StreamReceiver, Nothing)
    End SyncLock
    Catch e As Exception
    End Try
    End Sub
    End Class

     

    КЛИЕНТ:

    Imports System.Net.Sockets 'Добавляем пространство имен
    Imports System.Text
    Public Class Form1
    Dim Client As TcpClient
    Dim ReadSize As Short = 255
    Dim ReadBuffer(ReadSize) As Byte
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Try
    'Пытаемся подключится к IP
    Me.Client = New TcpClient("localhost", 10000)

    'Начинаем асинхронное чтение, с сохранением в readBuffer
    Me.Client.GetStream.BeginRead(ReadBuffer, 0, ReadSize, AddressOf DoRead, Nothing)

    'показываем окно чата
    Me.Show()
    Catch Ex As Exception 'Если подключение не удалось...
    MsgBox("Сервер не активный. Возможно проводятся технические работы.", _
    MsgBoxStyle.Exclamation, Me.Text)
    Me.Dispose()
    End Try
    End Sub
    'Асинхронное чтение
    Private Sub DoRead(ByVal ar As IAsyncResult)
    Dim BytesRead As Integer
    Dim ServerMessage As String

    Try
    'Заканчиваем асинхронное чтение в readBuffer и получаем кол-во байт.
    BytesRead = Me.Client.GetStream.EndRead(ar)
    If BytesRead < 1 Then
    'Если нет байт для чтения, значит сервер закрыт. Деактивируем окно клиента.
    SendTXT.Enabled = False
    Exit Sub
    End If

    'Декодируем полученные байты (размер - 1)
    ServerMessage = Encoding.GetEncoding(1251).GetString(ReadBuffer, 0, BytesRead - 1)

    'Вызываем процедуру обработки команд
    MsgBox(ServerMessage)

    ' Начинаем новое асинхронное чтение в переменную readBuffer.
    Client.GetStream.BeginRead(ReadBuffer, 0, ReadSize, AddressOf DoRead, Nothing)
    Catch e As Exception
    SendTXT.Enabled = False
    End Try
    End Sub

    'Эта подпрограмма использует StreamWriter, чтобы послать сообщение серверу.
    Private Sub SendData(ByVal data As String)
    Dim Writer As New IO.StreamWriter(Me.Client.GetStream)
    Writer.Write(data & vbCr)

    'Отсылаем все данные и очищаем средство записи
    Writer.Flush()
    End Sub

    Private Sub SendTXT_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SendTXT.Click
    SendData(TextBox1.Text)
    End Sub
    End Class

     

     

    Почему не присылается сообщение от клиента к серверу? Помогите пожалуйста

    24 декабря 2010 г. 20:20
  • Все у вас вроде хорошо присылается, просто ошибка в том что в ListBox вы изменяете из другого потока, с визуальными компонентами нужно рабоать из главного потока.

    Измените 

    'Добавляем запись
    LB1.Items.Add(UserMessage)

     

    на

     

    'Добавляем запись

    LB1.Invoke(New Action(Sub() LB1.Items.Add(UserMessage)))



    Для связи [mail]
    • Предложено в качестве ответа Abolmasov Dmitry 25 декабря 2010 г. 6:08
    • Отменено предложение в качестве ответа Abolmasov Dmitry 25 декабря 2010 г. 9:40
    24 декабря 2010 г. 21:53
  • Ого, да тут надо учится учится и еще раз учится :) Спасибо большое, попробую отпишусь. А где можно прочитать про создание таких приложений и про работу с потоками? Весь день искал, никаких книг, ничего нет. :)
    24 декабря 2010 г. 22:24
  • Спасибо еще раз!!!! :) Будем читать.
    24 декабря 2010 г. 22:44
  • Abolmasov Dmitry не работает ((( Ничего не принимает. Нажимаю отправить и никакой реакции.
    25 декабря 2010 г. 8:41
  • А цикл в загрузке сервера:

     

    Do

      Dim Client As Object = listener.AcceptTcpClient()
      StartLisen(Client)

       LB1.Items.Add("Новое подключение: ожидание авторизации")
    Loop Until False

     Не совсем пойму как он работает. Возможно здесь ошибка... Если посмотреть, то при загрузке формы начинается выполнение пока кто то не подключится? Тогда программа получается открывает постоянно новые потоки асинхронного чтения...

    LB1.Items.Add("Новое подключение: ожидание авторизации") - при подключении и отключении ничего не показывает...

    25 декабря 2010 г. 9:16
  • Не, программа запускает асинхронное чтение (создает новый поток) только когда подключился новый пользователь к серверу. Метод AcceptTcpClient блокирующий, т.е. программа висит на этом месте и ждет пока кто-то подключится, потом создает для клиента поток новый, в котором будет выполнять работу с клиентом, в тоже время основной поток в цикле начинает опять ожидать нового клиента, и так все время..

    У вас вообще не появляется окно сервера, т.к. в загрузке формы стоит бесконечный цикл, вам по-хорошему запуск сервера нужно в отдельный поток, который будет принимать входящих клиентов, или же использовать асинхронный прием listener.BeginAcceptTcpClient

    Для теста, можете сделать выход из цикла, при первом удачном подключении:

            LB1.Items.Add("Новое подключение: ожидание авторизации")
            Exit Do
          Loop Until False
    

    И еще обрабатывайте исключения на сервере:

            LB1.Items.Add("Новое подключение: ожидание авторизации")
            Exit Do
          Loop Until False
        Catch ex As SocketException
          MessageBox.Show("SocketException:" & ex.ToString())
        End Try
    


    Для связи [mail]
    • Помечено в качестве ответа Abolmasov Dmitry 27 декабря 2010 г. 20:47
    25 декабря 2010 г. 9:37
  • Ух, вроде бы получилось. Незнаю как, но заработало. Заново просто все переписал. Последний вопрос, будет ли клиент работать с сервером если указать при запуске внутренний IP и порт? Через интернет смогут подключаться, или это работает только для локальной сети?
    25 декабря 2010 г. 13:49
  • Нет, локальные адреса работать не будут, нужны внешние адреса или dns имена.
    Для связи [mail]
    25 декабря 2010 г. 20:51
  • Снова проблемка...

    При запуске клиента показываем ввод логина...

    Private Sub General_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Try
    'Подключаемся...
    YouClient = New TcpClient("localhost", PORT_NUM)
    'Начинаем асинхронное чтение, без изоляции пользовательского интерфейса
    YouClient.GetStream.BeginRead(readBuffer, 0, READ_BUFFER_SIZE, AddressOf DoRead, Nothing)

       Me.Show()
       LoginConnectUser.ShowDialog(Me) 'ДИАЛОГ ЛОГИНА
    Catch Ex As Exception 'Если не смогли подключится к серверу...
    MsgBox("Сервер отключен. Возможно идут технические работы, попробуйте позже.", _
    MsgBoxStyle.Exclamation, Me.Text)
    Me.Dispose()
    End Try
    End Sub

    Дальше вводим логин и отправляем серверу. От него приходит ответ и он обрабатывается... если логин неверный, нужно снова показать диалог. При показе вся программа зависает. В чем дело?


    Private Sub DoRead(ByVal ar As IAsyncResult)
    Dim BytesRead As Integer
    Dim strMessage As String
    Try
    'Заканчиваем асинхронное чтение и считаем кол-во байт
    BytesRead = YouClient.GetStream.EndRead(ar)
    If BytesRead < 1 Then
      'Если нет байт для чтения - значит сервер закрыт. Деактивируем клиент
    MarkAsDisconnected()
    Exit Sub
    End If

    'Преобразовываем байты в сообщение с -2, так как сервер посылает Chr(13) и Chr(10)
    strMessage = Encoding.UTF8.GetString(readBuffer, 0, BytesRead - 2)

      ProcessCommands(strMessage) 'ЗДЕСЬ ОБРАБАТЫВАЕМ КОМАНДЫ ()

    'И начинаем новое асинхронное чтение
    YouClient.GetStream.BeginRead(readBuffer, 0, READ_BUFFER_SIZE, AddressOf DoRead, Nothing)
    Catch e As Exception
    MarkAsDisconnected()
    End Try
    End Sub


    'Обработчик команд сервера
    Private Sub ProcessCommands(ByVal strMessage As String)
    Dim dataArray() As String
    'Делим сообщение по "|"
    dataArray = strMessage.Split(Chr(124))
    'Смотрим на команду
    Select Case dataArray(0)
    Case "JOIN" 'Сервер принял логин и оповестил "ВОШЛИ"
    'Закрыть форму ввода логина
      DisplayText("Вы вошли" & Chr(13) & Chr(10))
       Case "REFUSE" 'Сервер не принял логин, показываем диалог снова.
    LoginConnectUser.ShowDialog(Me)
       End Select
      End Sub

    28 декабря 2010 г. 17:45
  • И еще... ввел в YouClient = New TcpClient("localhost", PORT_NUM)

    внешний IP сервера

    YouClient = New TcpClient("95.71.29.101", PORT_NUM)

    Попытался подключится и в итоге ничего... Не соединяется. Ввожу локальный адресс машины 192.168.1.2, работает. Через интернет никак не хочет подключатся... ((

    28 декабря 2010 г. 19:42
  • Проверьте доступность порта, если вы за NAT, то необходимо выполнить проброс порта на сервере. Таже посмотритрите настройки фаерволла.
    Для связи [mail]
    29 декабря 2010 г. 14:35
  • Используя Try - Catch, выводите ошибки куда-нибудь, иначе, просто пропуская их, вы делаете только себе хуже в плане отладки приложения. Незнаете, что случилось и почему программа не так работает. В данном случае при вызове LoginConnectUser.ShowDialog(Me) возникает ошибка Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on, что говорит о том, что нельза работать с элементами управления не из главного потока.

    Выше я давал ссылки на руководства по потоко-безопасной работе с элементами управления

    Сделайте так:

    Me.Invoke(New Action(Sub() LoginConnectUser.ShowDialog(Me)))
    

     


    Для связи [mail]
    29 декабря 2010 г. 15:24