.NET 4.x Технология клиент-сервер. Классы клиента и сервера. Обсуждение - Visual Basic .NET
Формулировка задачи:
В данном теме выкладываю свои классы клиента и сервера, а так же простеньких примеров реализованных на этих классах. Жду конструктивной критики, идей, предложений по улучшению, оптимизации и прочего.
Сообщение в разработке. Примеры будут чуть позже.
Так же прошу поправлять меня, если комментарии в коде не соответствуют реальной действительности. Комментировал то как я это понимаю.
P.S. с орфографией тоже туговато, так что исправляем
Imports System.Net.Sockets
Public Class Class_Server
Public Delegate Sub StatusInvoker(ByVal t As String) ' делегат для синхронизации при выводе сообщений
Private port As Integer = 13000 ' порт сервера на котором он работает
Private server_name As String ' название сервера. Например: Game, Authorization server . Просто для удобства
Private flag As Byte ' в текущий версии не используется. В будущем планируется клиентов с другим флагом сразу отключать, что бы только "правильные" клиенты могли подключаться к серверу.
Private server_Listener As TcpListener ' Сам TcpListener который и прослушивает нужный порт
Private clientsList As New Hashtable ' Список клиентов. (Уникальный индификатор ID,сам класс клиента)
Public Event OnRead(ByVal _client As handleClinet, ByVal _data As String) ' Событие вызываемое при получении данных от клиентов
Public Event Connect(ByVal _client As handleClinet) ' Событие вызываемое при подключении клиента
Public Event Disconnect(ByVal _client As handleClinet, ByVal _reson As String) ' Событие вызываемое при отключении клиента
Public Event messege(ByVal text As String) ' событие вызываемое, когда нужно передать информацию из класса
Public Sub New(ByVal _Port As Integer, ByVal _Name As String, ByVal _Flag As Byte) ' инициализация класса. Порт, название, флаг
port = _Port
server_name = _Name
flag = _Flag
End Sub
Public Sub start() ' Запуск сервера.
Dim server_Thread As Threading.Thread = New Threading.Thread(AddressOf startServ)
server_Thread.Start() ' Запуск отдельного потока на прослушивание порта и подключений к серверу
End Sub
Public Sub stop_server() ' Процедура для закрытия сервера.
Dim Item As DictionaryEntry
Dim list As Hashtable = clientsList
For Each Item In list ' Перебор всех клиентов в списке
CType(Item.Value, handleClinet).closeClient() ' Закрываем каждого клиента, что бы поток клиента завершился.
Next
server_Listener.Stop() ' Остановка прослушивания порта
End Sub
Public ReadOnly Property User_List() As Hashtable ' Возращаем список клиентов в виде хэш таблицы.
Get
Return clientsList
End Get
End Property
Private Sub Client_OnRead(ByVal client As handleClinet, ByVal data As String) ' Процедура, вызываемая при получении сервером данных от клиента
RaiseEvent OnRead(client, data) ' вызываем событие сервера, что клиент прислал данные
End Sub
Private Sub Client_Disconnect(ByVal client As handleClinet, ByVal reson As String) ' Процедура, вызываемая при отключении клиента
clientsList.Remove(client.ID) ' удаляем клиента из списка клиентов
RaiseEvent Disconnect(client, reson) ' вызываем событие сервера, что отключился клиент
End Sub
Public Sub Send_user(ByVal client As handleClinet, ByVal data As String) ' Процедура отправки данных нужному клиенту
Dim tcp As TcpClient = client.TCP() ' получаем TcpClient нужный для получения потока записи
Dim stream As NetworkStream = tcp.GetStream ' получение потока записи
Dim mess() As Byte = System.Text.Encoding.UTF8.GetBytes(data) ' преобразование сообщения в байты для потока
stream.Write(mess, 0, mess.Length) ' отсылка данных в виде байт
stream.Flush() ' честно не знаю, что это делает. Работает и с этим и без, но в большинстве примеров есть, поэтому оставил.
End Sub
Public Sub Send_all(ByVal data As String) ' Процедура посылки сообщения всем клиентам
Dim Item As DictionaryEntry
Dim list As Hashtable = clientsList
For Each Item In list ' Перебор всех клиентов и рассылка через Send_user
Send_user(CType(Item.Value, handleClinet), data)
Next
End Sub
Private Sub startServ() ' процедура служащая для прослушивания порта и подключении клиентов
Dim client As TcpClient
Dim count As Integer ' Кол-во подключений
Try
server_Listener = New TcpListener(port)
server_Listener.Start() ' Начинаем прослушивать порт
msg("=========Сервер " & Me.server_name & " запущен...=========")
While True ' Бесконечный цикл, что бы клиенты могли подключаться всегда
count += 1 ' Новое подключение, значит счетчик +1
client = server_Listener.AcceptTcpClient() ' Ждем подключения. Когда клиент подключается, запоминаем его в client
Dim temp_client As New handleClinet(client, count) ' Создаем новый класс клиента и передаем ему подключившегося клиента и его номер подключения
temp_client.startClient(Me) ' Запускаем клиента и прослушивание входящих сообщений. Ссылку на класс сервера передаем, что бы клиент мог добавить своё событие на Client_OnRead
clientsList.Add(temp_client.ID, temp_client) ' Добавляем клиента в список клиентов
RaiseEvent Connect(temp_client) ' Вызываем событие, что клиент подключился
End While
Catch ex As Exception
msg("=========Ошибка сервера " & Me.server_name & ". Причина: " & ex.Message) ' Сообщаем об ошибке
End Try
' Так как сюда попасть можно, только после ошибки, значит сервер остановлен. Сообщаем об этом.
msg("=========Сервер " & Me.server_name & " остановлен!=========")
End Sub
Public Sub msg(ByVal t As String) ' Процедура отправки сообщения
Try
RaiseEvent messege(t) ' Вызываем событие о новом сообщении
Catch ex As Exception
MsgBox(ex.Message, , "Msg Сервера")
End Try
End Sub
Public Class handleClinet ' Класс клиента в сервере. Нужен для индификации и базовых дейсвий с клиентом
Public Event OnRead(ByVal _client As handleClinet, ByVal _data As String) ' Событие, что текущий клиент получил данные
Public Event Disconnect(ByVal _client As handleClinet, ByVal _reson As String) ' Событие при отключении текущего клиента
Private mgID As Guid = Guid.NewGuid ' Получаем уникальный индификатор клиента для хэш таблицы списка клиентов
Private clientSocket As TcpClient ' Сокет клиента
Private stream As NetworkStream ' Поток данных клиента
Private clNo As String ' Номер клиента
' Private serv As Class_Server
Private myIP As String ' Храним тут ип этого клиента
Public Sub New(ByVal inClientSocket As TcpClient, ByVal count As Integer) ' Инициализация клиента
Me.clientSocket = inClientSocket
Me.stream = clientSocket.GetStream()
Me.clNo = count
End Sub
Public ReadOnly Property IP() As String ' Получение IP
Get
If myIP = "" Then myIP = clientSocket.Client.RemoteEndPoint.ToString
Return myIP
End Get
End Property
Public ReadOnly Property ID() As String ' Получение ID
Get
Return mgID.ToString
End Get
End Property
Public ReadOnly Property Number() As String ' Получение номера клиента
Get
Return clNo.ToString
End Get
End Property
Public ReadOnly Property TCP() As TcpClient ' Получение TcpClient клиента
Get
Return clientSocket
End Get
End Property
Public Sub closeClient() ' Процедура принудительного закрытия подключения клиента
stream.Close()
clientSocket.Close()
End Sub
Public Sub startClient(ByVal _serv As Class_Server) ' Процедура запуска клиента
Dim client_Thread As Threading.Thread = New Threading.Thread(AddressOf doListen)
client_Thread.Start() ' Запуск прослушивания новых данных текущим клиентом в отедьном потоке
AddHandler Me.OnRead, AddressOf _serv.Client_OnRead ' При срабатывании события OnRead в клиенте, вызываем процедуру Client_OnRead на самом сервере
AddHandler Me.Disconnect, AddressOf _serv.Client_Disconnect ' При срабатывании события Disconnect в клиенте, вызываем процедуру Client_Disconnect на самом сервере
End Sub
Private Sub doListen()
Dim bytes(1048576) As Byte ' 1мб буфер, что бы данные не дробились.
Dim data As String
Dim i As Int32
Try
i = stream.Read(bytes, 0, bytes.Length) ' Ждем новых данных от клиентского приложения. При получении заносим данные в буфер bytes и длину данных в i
While (i <> 0) ' Цикл бесконечный, пока длина считанных данных не окажется=0, что ознает, что клиент оключился.
data = System.Text.Encoding.UTF8.GetString(bytes, 0, i) ' Переводим байты в текст
RaiseEvent OnRead(Me, data) ' Вызываем событие о получении новых данных
i = stream.Read(bytes, 0, bytes.Length) ' Ждем новых данных от клиентского приложения. При получении заносим данные в буфер bytes и длину данных в i
End While
closeClient() ' Так как вышли из цикла, то клиентское приложение закрыло соединение. Следовательно закрываем и мы.
RaiseEvent Disconnect(Me, "Клиент решил отключиться от сервера.") ' Вызываем событие, что клиент отключился и передаем причину.
Catch ex As Exception
closeClient() ' Ошибка, значит отключаем клиента.
RaiseEvent Disconnect(Me, "Проблема соединения: " & ex.Message) ' Вызываем событие, что клиент отключился и передаем причину.
End Try
End Sub
End Class
End ClassImports System.Net.Sockets
Public Class Class_client
Public Delegate Sub StatusInvoker(ByVal t As String) ' делегат для синхронизации при выводе сообщений
Dim client As TcpClient ' Текущий TcpClient связывающий клиента и сервер
Dim stream As NetworkStream ' Поток данных связывающий клиента и сервер
Dim flag As Byte ' Пока не используется. Нужен для индификации сервером "правильного" клиента
Dim connection As Boolean = False ' Состояние подключения
Public Event OnRead(ByVal _data As String) ' Событие, при получении данных от сервера
Public Event Disconnected(ByVal _reson As String) ' Событие, при отключении от сервера
Public Event messege(ByVal text As String) ' Событие, при необходимости передать сообщение из класса
Dim s As Object
Public Function Connect(ByVal server As String, ByVal port As Integer, ByVal _Flag As Byte) As Boolean ' Функция подключения клиента к серверу. Вернет True в случае удачи и False при ошибке.
If connection = False Then ' Проверка не подключены ли мы уже?
Try
Me.client = New TcpClient(server, port)
Me.stream = client.GetStream()
Me.flag = _Flag
Dim client_Thread As Threading.Thread = New Threading.Thread(AddressOf Me.doListen)
client_Thread.Start() ' Запуск отдельного потока на прослушивание данных с сервера.
connection = True ' Состояние=Подключен
Return True
Catch ex As Exception
msg(ex.Message) ' Сообщаем об ошибке
Return False
End Try
Else
Return False ' Если уже подключены, тогда новое подключение не удалось
End If
End Function
Public Sub Disconnect(Optional ByVal s As String = "") ' Процедура отключения от сервера. Возможно указать коментарий.
If connection Then
connection = False ' Состояние=Отключен
stream.Close()
client.Close()
If s = "" Then
RaiseEvent Disconnected("Отключен") ' Вызываем событие отключения с коментарием "по умолчанию"
Else
RaiseEvent Disconnected(s) ' Вызываем событие отключения с коментарием указанным при вызове текущей процедуры
End If
End If
End Sub
Public Function Send(ByVal data As String) As Boolean ' Функция отправки сообщения серверу. Вернет True, если посылка удалась или False в обратном случаее
If connection Then
Try
Dim databyte() As Byte = System.Text.Encoding.UTF8.GetBytes(data) ' Создаем массив байтов для отправки из Data
stream.Write(databyte, 0, databyte.Length) ' Отправляем наш массив байт.
stream.Flush() ' честно не знаю, что это делает. Работает и с этим и без, но в большинстве примеров есть, поэтому оставил.
Return True ' Говорим, что посылка удалась
Catch ex As Exception
msg(ex.Message) ' Сообщаем об ошибке
Disconnect("Проблема при отправке.") ' Отключаемся
Return False
End Try
Else
Return False ' Если не подключены, то отправка не удалась.
End If
End Function
Private Sub doListen() ' Процедура прослушки сообщений от сервера
Dim bytes(1048576) As Byte ' 1мб буфер, чтобы данные не дробились.
Dim data As String
Dim i As Int32
Try
i = stream.Read(bytes, 0, bytes.Length) ' Ждем новых данных от сервера. При получении заносим данные в буфер bytes и длину данных в i
While (i <> 0) ' Цикл бесконечный, пока длина считанных данных не окажется=0, что ознает, что соединение с сервером прекращенно.
data = System.Text.Encoding.UTF8.GetString(bytes, 0, i) ' Переводим байты в текст
RaiseEvent OnRead(data) ' Вызываем событие о получении новых данных
i = stream.Read(bytes, 0, bytes.Length) ' Ждем новых данных от сервера. При получении заносим данные в буфер bytes и длину данных в i
End While
Disconnect() ' Так как вышли из цикла, то сервер закрыло соединение. Следовательно отключаемся.
Catch ex As Exception
Disconnect(ex.Message) ' Ошибка, значит отключаемся, указав причину.
End Try
End Sub
Public Sub msg(ByVal t As String) ' Процедура отправки сообщения
Try
RaiseEvent messege(t) ' Вызываем событие о новом сообщении
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
End ClassРешение задачи: «.NET 4.x Технология клиент-сервер. Классы клиента и сервера. Обсуждение»
textual
Листинг программы
Public Sub Send_user(ByVal client As handleClinet, ByVal data As String) ' Процедура отправки данных нужному клиенту Dim tcp As TcpClient = client.TCP() ' получаем TcpClient нужный для получения потока записи Dim stream As NetworkStream = tcp.GetStream ' получение потока записи Dim mess() As Byte = System.Text.Encoding.UTF8.GetBytes(data) ' преобразование сообщения в байты для потока stream.Write(mess, 0, mess.Length) ' отсылка данных в виде байт stream.Flush() ' честно не знаю, что это делает. Работает и с этим и без, но в большинстве примеров есть, поэтому оставил. End Sub