.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 Class
Imports 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
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д