Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

[.NETV2] CLASSCOM - CLASSE DE COMMUNICATION EN RÉSEAU SIMPLIFIÉE GÉRANT LE MULTICLIENT, 2 EN 1 CLIENT ET SERVEUR - CLASS SOCKET


Information sur la source

Catégorie :Réseau & Internet Source .NET ( DotNet ) Classé sous : socket, reseau, net, class, communication Niveau : Débutant Date de création : 06/04/2007 Date de mise à jour : 11/04/2007 15:48:24 Vu : 8 732

Note :
8,6 / 10 - par 5 personnes
8,60 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (37)
Ajouter un commentaire et/ou une note


Description

Bonjour,

Je vous présente ici une class de communication réseau, simple et d'utilisation claire, pour tout ceux qui rencontre (ou non) des problèmes avec les sockets.

Fonctionnement :

} Connexion
- on définit grâce au constructeur si c'est un serveur qui écoute ou bien un client qui va se connecter à un serveur
- si instanciée en serveur, un évenement prévient lorsqu'une connexion arrive, et donne le socket client (qu'il suffit de passer à un constructeur de cette class)
- si instanciée en client (avec les infos de connexion en paramètre), un évenement prévient si la connexion s'est effectuée ou non

} Communication
# les messages sont des classcomm.commMessage (structure perso pour simplifier la comm réseau)
- pour envoyer un message, on utilise Send qui prend en paramètre un Ordre (string) et un Message (string)
- pour recevoir des messages, il suffit de tester de temps en temps MsgCount : si y'en a 0 c'est qu'il n'y a pas de CommMessage à consommer, sinon c'est qu'il y en a le nombre indiqué en attente d'être lu, grâce à ReadNextMsg qui renvoie le CommMessage

} L'event ProblemDetected
- renvoit ErrorType.connection_failed si un client n'a pas su se connecter à un serveur donné
- renvoit ErrorType.seems_disconnected si on est vraisemblablement déconnecté (erreur pendant une transmission par exemple)
- renvoit ErrorType.failed_to_listen si un serveur n'arrive pas à écouter sur un port donné ou tout autre erreur à l'ouverture d'un serveur

} Fermeture
- pour fermer un serveur ou déconnecter un client, utilisez la méthode Dispose
#en gros l'objet ne vit QUE connecté. Simple.




Ci-dessous la class à coller directement dans un module de class.net :
 

Source

  • Imports System.Net.Sockets
  • Imports System.Net
  • Imports System.Text
  • Public Class ClassComm
  • Implements IDisposable
  • #Region "IDisposable Support"
  • Private disposedValue As Boolean = False
  • ' IDisposable
  • Protected Overridable Sub Dispose(ByVal disposing As Boolean)
  • If Not Me.disposedValue Then
  • If disposing Then
  • If Not TimerConnectionTimeOut Is Nothing Then
  • TimerConnectionTimeOut.Enabled = False
  • TimerConnectionTimeOut.Close()
  • TimerConnectionTimeOut.Dispose()
  • TimerConnectionTimeOut = Nothing
  • End If
  • If Not TimerSendTimeOut Is Nothing Then
  • TimerSendTimeOut.Enabled = False
  • TimerSendTimeOut.Close()
  • TimerSendTimeOut.Dispose()
  • TimerSendTimeOut = Nothing
  • End If
  • If Not TimerSend Is Nothing Then
  • TimerSend.Enabled = False
  • TimerSend.Close()
  • TimerSend.Dispose()
  • TimerSend = Nothing
  • End If
  • _bytes = Nothing
  • _qrcv.Clear()
  • _qrcv = Nothing
  • _qsnd.Clear()
  • _qsnd = Nothing
  • On Error Resume Next
  • _sck.Shutdown(SocketShutdown.Both)
  • _sck.Close()
  • On Error GoTo 0
  • _sck = Nothing
  • End If
  • 'free shared unmanaged resources
  • End If
  • Me.disposedValue = True
  • End Sub
  • Public Sub Dispose() Implements IDisposable.Dispose
  • Dispose(True)
  • GC.SuppressFinalize(Me)
  • End Sub
  • #End Region
  • #Region "Enumeration"
  • Public Enum ErrorType
  • seems_disconnected
  • connection_failed
  • failed_to_listen
  • End Enum
  • #End Region
  • #Region "Structure"
  • Public Structure CommMessage
  • Public Order As String
  • Public Message As String
  • Private _idCode As Long
  • Public IsComplete As Boolean
  • Public Property idCode() As Long
  • Get
  • If _idCode = 0 Then _idCode = Now.Ticks / ((Rnd() * 1000) + 1)
  • Return _idCode
  • End Get
  • Set(ByVal value As Long)
  • If _idCode = 0 Then _idCode = value
  • _idCode = value
  • End Set
  • End Property
  • Public ReadOnly Property MyBytes() As Byte()
  • Get
  • If _idCode = 0 Then _idCode = Now.Ticks / ((Rnd() * 1000) + 1)
  • If Order Is Nothing Then Order = ""
  • If Message Is Nothing Then Message = ""
  • Dim ms As New IO.MemoryStream
  • Dim bw As New IO.BinaryWriter(ms, Encoding.Unicode)
  • With bw
  • .Write(Order)
  • .Write(Message)
  • .Write(IsComplete)
  • .Write(_idCode)
  • .Close()
  • End With
  • ms.Close()
  • Return ms.ToArray
  • bw = Nothing
  • ms.Dispose()
  • ms = Nothing
  • End Get
  • End Property
  • Public Sub SetBytes(ByRef ByteCommMessage As Byte())
  • Dim ms As New IO.MemoryStream(ByteCommMessage, False)
  • Dim br As New IO.BinaryReader(ms, Encoding.Unicode)
  • With br
  • Order = .ReadString
  • Message = .ReadString
  • IsComplete = .ReadBoolean
  • _idCode = .ReadUInt64
  • .Close()
  • End With
  • br = Nothing
  • ms.Close()
  • ms.Dispose()
  • ms = Nothing
  • End Sub
  • Public Function ConfirmSequence() As Byte()
  • Dim ms As New IO.MemoryStream
  • Dim bw As New IO.BinaryWriter(ms, Encoding.Unicode)
  • With bw
  • .Write("")
  • .Write("")
  • .Write(True)
  • .Write(_idCode)
  • .Close()
  • End With
  • ms.Close()
  • Return ms.ToArray
  • bw = Nothing
  • ms.Dispose()
  • ms = Nothing
  • End Function
  • End Structure
  • #End Region
  • #Region "Private objects"
  • Private _bytes() As Byte
  • Private _qrcv As New Queue(Of CommMessage)
  • Private _qsnd As New Queue(Of CommMessage)
  • Private _last_send As CommMessage
  • Private _last_rcv As CommMessage
  • Private _sck As Socket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP)
  • Private WithEvents TimerConnectionTimeOut As Timers.Timer
  • Private WithEvents TimerSendTimeOut As Timers.Timer
  • Private WithEvents TimerSend As New Timers.Timer(10)
  • Private _sending As Boolean = False
  • #End Region
  • #Region "Events"
  • Public Event ConnectionRequestAccepted(ByRef sck As Socket)
  • Public Event Connected()
  • Public Event ProblemDetected(ByVal source As ErrorType)
  • Public Event NewMessage()
  • #End Region
  • #Region "Constructor New"
  • Public Sub New(ByVal Port_To_Listen_If_Server As Integer)
  • _last_rcv.idCode = -1
  • _last_rcv.IsComplete = True
  • Try
  • With _sck
  • .Bind(New IPEndPoint(IPAddress.Any, Port_To_Listen_If_Server))
  • .Listen(10)
  • .BeginAccept(AddressOf CallBackAccept, _sck)
  • End With
  • Catch
  • RaiseEvent ProblemDetected(ErrorType.failed_to_listen)
  • End Try
  • End Sub
  • Public Sub New(ByVal Host_To_Connect As String, ByVal Port_To_Connect As Integer, Optional ByVal Timeout_ms As Integer = 5000)
  • _last_rcv.idCode = -1
  • _last_rcv.IsComplete = True
  • If Timeout_ms >= 0 Then
  • TimerConnectionTimeOut = New Timers.Timer
  • TimerConnectionTimeOut.Interval = Timeout_ms
  • TimerConnectionTimeOut.Enabled = True
  • TimerSendTimeOut = New Timers.Timer
  • TimerSendTimeOut.Interval = Timeout_ms
  • TimerSendTimeOut.Enabled = False
  • End If
  • Try
  • _sck.BeginConnect(Host_To_Connect, Port_To_Connect, AddressOf CallBackConnect, _sck)
  • Catch
  • RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
  • End Try
  • End Sub
  • Public Sub New(ByRef Sck As Socket, Optional ByVal Timeout_ms As Integer = 5000)
  • _last_rcv.idCode = -1
  • _last_rcv.IsComplete = True
  • If Sck.Connected Then
  • If Timeout_ms >= 0 Then
  • TimerSendTimeOut = New Timers.Timer
  • TimerSendTimeOut.Interval = Timeout_ms
  • TimerSendTimeOut.Enabled = False
  • End If
  • Try
  • _sck = Sck
  • TimerSend.Enabled = True
  • _bytes = New Byte(_sck.ReceiveBufferSize) {}
  • _sck.BeginReceive(_bytes, 0, _sck.ReceiveBufferSize, SocketFlags.None, AddressOf CallBackReceive, _sck)
  • Catch
  • RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
  • End Try
  • Else
  • RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
  • End If
  • End Sub
  • #End Region
  • #Region "Timers events"
  • Private Sub TimerConnectionTimeOut_Elapsed(ByVal sender As Object, ByVal e As system.Timers.ElapsedEventArgs) Handles TimerConnectionTimeOut.Elapsed
  • TimerConnectionTimeOut.Enabled = False
  • RaiseEvent ProblemDetected(ErrorType.connection_failed)
  • End Sub
  • Private Sub TimerSendTimeOut_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles TimerSendTimeOut.Elapsed
  • If _sending Then Sendcm(_last_send.MyBytes) Else TimerSendTimeOut.Enabled = False
  • End Sub
  • Private Sub TimerSend_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles TimerSend.Elapsed
  • If Not _sending Then
  • If _qsnd.Count > 0 Then
  • Sending(True)
  • _last_send = _qsnd.Dequeue
  • Sendcm(_last_send.MyBytes)
  • End If
  • End If
  • End Sub
  • #End Region
  • #Region "Public properties"
  • Public ReadOnly Property ReadNextMsg() As CommMessage
  • Get
  • If _qrcv.Count > 0 Then Return _qrcv.Dequeue Else Return Nothing
  • End Get
  • End Property
  • Public ReadOnly Property MsgCount() As Integer
  • Get
  • Return _qrcv.Count
  • End Get
  • End Property
  • Public ReadOnly Property IsSending() As Boolean
  • Get
  • Return _sending
  • End Get
  • End Property
  • #End Region
  • #Region "Public functions"
  • Public Sub Send(ByVal Order As String, ByVal Message As String)
  • Dim cm As CommMessage
  • Dim LenTT As Integer = (Order & Message).Length
  • If LenTT > 4000 Then
  • 'Si supérieur à 4000 alors découpe
  • 'Les paquets sont basés sur 4000 car le tout sera encodé en Unicode, donc doublé,
  • 'la limite des paquets des sockets étant 8192, j'ai arrondi la découpe à 4000
  • 'pour prendre en compte les autres paramètres transmis avec les messages
  • Dim i As Integer = 0
  • Dim j As Integer = 0
  • Dim ti As Integer
  • Dim tj As Integer
  • Do
  • tj = 0
  • ti = 0
  • cm = New CommMessage
  • ti = IIf(Order.Substring(i).Length > 4000, 4000, Order.Substring(i).Length)
  • cm.Order = Order.Substring(i, ti)
  • i += ti
  • If ti < 4000 Then
  • tj = IIf(Message.Substring(j).Length > (4000 - ti), (4000 - ti), Message.Substring(j).Length)
  • cm.Message = Message.Substring(j, tj)
  • j += tj
  • End If
  • cm.IsComplete = (LenTT = i + j)
  • _qsnd.Enqueue(cm)
  • Loop Until cm.IsComplete
  • Else
  • 'Sinon envoie tel quel
  • cm = New CommMessage
  • cm.Order = Order
  • cm.Message = Message
  • cm.IsComplete = True
  • _qsnd.Enqueue(cm)
  • End If
  • End Sub
  • #End Region
  • #Region "Private functions"
  • Private Sub Sendcm(ByRef _b() As Byte)
  • Try
  • _sck.BeginSend(_b, 0, _b.Length, SocketFlags.None, AddressOf CallBackSend, _sck)
  • Catch
  • RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
  • End Try
  • End Sub
  • Private Sub Sending(ByVal Flag As Boolean)
  • _sending = Flag
  • TimerSendTimeOut.Enabled = Flag
  • End Sub
  • #End Region
  • #Region "Socket delegate CallBack"
  • 'en mode serveur, renvoit un socket connecté à un client
  • 'puis repasse en attente
  • Private Sub CallBackAccept(ByVal async As IAsyncResult)
  • Try
  • RaiseEvent ConnectionRequestAccepted(_sck.EndAccept(async))
  • _sck.BeginAccept(AddressOf CallBackAccept, _sck)
  • Catch
  • RaiseEvent ProblemDetected(ErrorType.failed_to_listen)
  • End Try
  • End Sub
  • 'en mode client, établit la connexion
  • Private Sub CallBackConnect(ByVal async As IAsyncResult)
  • If Not TimerConnectionTimeOut Is Nothing Then TimerConnectionTimeOut.Enabled = False
  • Try
  • _sck.EndConnect(async)
  • RaiseEvent Connected()
  • TimerSend.Enabled = True
  • _bytes = New Byte(_sck.ReceiveBufferSize) {}
  • _sck.BeginReceive(_bytes, 0, _sck.ReceiveBufferSize, SocketFlags.None, AddressOf CallBackReceive, _sck)
  • Catch
  • RaiseEvent ProblemDetected(ErrorType.connection_failed)
  • End Try
  • End Sub
  • 'réception de données, puis se remet en attente
  • Private Sub CallBackReceive(ByVal async As IAsyncResult)
  • Dim size As Integer
  • Try
  • size = _sck.EndReceive(async)
  • If size > 0 Then
  • Dim cm As New CommMessage
  • cm.SetBytes(_bytes)
  • If cm.Order = "" AndAlso cm.Message = "" Then
  • If cm.idCode = _last_send.idCode Then Sending(False)
  • Else
  • If cm.idCode <> _last_rcv.idCode Then
  • If Not _last_rcv.IsComplete Then
  • cm.Order = cm.Order.Insert(0, _last_rcv.Order)
  • cm.Message = cm.Message.Insert(0, _last_rcv.Message)
  • End If
  • _last_rcv = cm
  • If cm.IsComplete Then
  • _qrcv.Enqueue(cm)
  • RaiseEvent NewMessage()
  • End If
  • End If
  • Sendcm(cm.ConfirmSequence)
  • End If
  • Else
  • RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
  • End If
  • _bytes = New Byte(_sck.ReceiveBufferSize) {}
  • _sck.BeginReceive(_bytes, 0, _sck.ReceiveBufferSize, SocketFlags.None, AddressOf CallBackReceive, _sck)
  • Catch
  • RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
  • End Try
  • End Sub
  • 'envoi des données terminées
  • Private Sub CallBackSend(ByVal async As IAsyncResult)
  • Try
  • _sck.EndSend(async)
  • Catch
  • RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
  • End Try
  • End Sub
  • #End Region
  • End Class
Imports System.Net.Sockets
Imports System.Net
Imports System.Text

Public Class ClassComm
  Implements IDisposable

#Region "IDisposable Support"
  Private disposedValue As Boolean = False

  ' IDisposable
  Protected Overridable Sub Dispose(ByVal disposing As Boolean)
    If Not Me.disposedValue Then
      If disposing Then
        If Not TimerConnectionTimeOut Is Nothing Then
          TimerConnectionTimeOut.Enabled = False
          TimerConnectionTimeOut.Close()
          TimerConnectionTimeOut.Dispose()
          TimerConnectionTimeOut = Nothing
        End If
        If Not TimerSendTimeOut Is Nothing Then
          TimerSendTimeOut.Enabled = False
          TimerSendTimeOut.Close()
          TimerSendTimeOut.Dispose()
          TimerSendTimeOut = Nothing
        End If
        If Not TimerSend Is Nothing Then
          TimerSend.Enabled = False
          TimerSend.Close()
          TimerSend.Dispose()
          TimerSend = Nothing
        End If
        _bytes = Nothing
        _qrcv.Clear()
        _qrcv = Nothing
        _qsnd.Clear()
        _qsnd = Nothing
        On Error Resume Next
        _sck.Shutdown(SocketShutdown.Both)
        _sck.Close()
        On Error GoTo 0
        _sck = Nothing
      End If
      'free shared unmanaged resources
    End If
    Me.disposedValue = True
  End Sub

  Public Sub Dispose() Implements IDisposable.Dispose
    Dispose(True)
    GC.SuppressFinalize(Me)
  End Sub
#End Region

#Region "Enumeration"
  Public Enum ErrorType
    seems_disconnected
    connection_failed
    failed_to_listen
  End Enum
#End Region

#Region "Structure"
  Public Structure CommMessage
    Public Order As String
    Public Message As String
    Private _idCode As Long
    Public IsComplete As Boolean
    Public Property idCode() As Long
      Get
        If _idCode = 0 Then _idCode = Now.Ticks / ((Rnd() * 1000) + 1)
        Return _idCode
      End Get
      Set(ByVal value As Long)
        If _idCode = 0 Then _idCode = value
        _idCode = value
      End Set
    End Property
    Public ReadOnly Property MyBytes() As Byte()
      Get
        If _idCode = 0 Then _idCode = Now.Ticks / ((Rnd() * 1000) + 1)
        If Order Is Nothing Then Order = ""
        If Message Is Nothing Then Message = ""
        Dim ms As New IO.MemoryStream
        Dim bw As New IO.BinaryWriter(ms, Encoding.Unicode)
        With bw
          .Write(Order)
          .Write(Message)
          .Write(IsComplete)
          .Write(_idCode)
          .Close()
        End With
        ms.Close()
        Return ms.ToArray
        bw = Nothing
        ms.Dispose()
        ms = Nothing
      End Get
    End Property
    Public Sub SetBytes(ByRef ByteCommMessage As Byte())
      Dim ms As New IO.MemoryStream(ByteCommMessage, False)
      Dim br As New IO.BinaryReader(ms, Encoding.Unicode)
      With br
        Order = .ReadString
        Message = .ReadString
        IsComplete = .ReadBoolean
        _idCode = .ReadUInt64
        .Close()
      End With
      br = Nothing
      ms.Close()
      ms.Dispose()
      ms = Nothing
    End Sub
    Public Function ConfirmSequence() As Byte()
      Dim ms As New IO.MemoryStream
      Dim bw As New IO.BinaryWriter(ms, Encoding.Unicode)
      With bw
        .Write("")
        .Write("")
        .Write(True)
        .Write(_idCode)
        .Close()
      End With
      ms.Close()
      Return ms.ToArray
      bw = Nothing
      ms.Dispose()
      ms = Nothing
    End Function
  End Structure
#End Region

#Region "Private objects"
  Private _bytes() As Byte
  Private _qrcv As New Queue(Of CommMessage)
  Private _qsnd As New Queue(Of CommMessage)
  Private _last_send As CommMessage
  Private _last_rcv As CommMessage
  Private _sck As Socket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP)
  Private WithEvents TimerConnectionTimeOut As Timers.Timer
  Private WithEvents TimerSendTimeOut As Timers.Timer
  Private WithEvents TimerSend As New Timers.Timer(10)
  Private _sending As Boolean = False
#End Region

#Region "Events"
  Public Event ConnectionRequestAccepted(ByRef sck As Socket)
  Public Event Connected()
  Public Event ProblemDetected(ByVal source As ErrorType)
  Public Event NewMessage()
#End Region

#Region "Constructor New"

  Public Sub New(ByVal Port_To_Listen_If_Server As Integer)
    _last_rcv.idCode = -1
    _last_rcv.IsComplete = True
    Try
      With _sck
        .Bind(New IPEndPoint(IPAddress.Any, Port_To_Listen_If_Server))
        .Listen(10)
        .BeginAccept(AddressOf CallBackAccept, _sck)
      End With
    Catch
      RaiseEvent ProblemDetected(ErrorType.failed_to_listen)
    End Try
  End Sub
  Public Sub New(ByVal Host_To_Connect As String, ByVal Port_To_Connect As Integer, Optional ByVal Timeout_ms As Integer = 5000)
    _last_rcv.idCode = -1
    _last_rcv.IsComplete = True
    If Timeout_ms >= 0 Then
      TimerConnectionTimeOut = New Timers.Timer
      TimerConnectionTimeOut.Interval = Timeout_ms
      TimerConnectionTimeOut.Enabled = True
      TimerSendTimeOut = New Timers.Timer
      TimerSendTimeOut.Interval = Timeout_ms
      TimerSendTimeOut.Enabled = False
    End If
    Try
      _sck.BeginConnect(Host_To_Connect, Port_To_Connect, AddressOf CallBackConnect, _sck)
    Catch
      RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
    End Try
  End Sub
  Public Sub New(ByRef Sck As Socket, Optional ByVal Timeout_ms As Integer = 5000)
    _last_rcv.idCode = -1
    _last_rcv.IsComplete = True
    If Sck.Connected Then
      If Timeout_ms >= 0 Then
        TimerSendTimeOut = New Timers.Timer
        TimerSendTimeOut.Interval = Timeout_ms
        TimerSendTimeOut.Enabled = False
      End If
      Try
        _sck = Sck
        TimerSend.Enabled = True
        _bytes = New Byte(_sck.ReceiveBufferSize) {}
        _sck.BeginReceive(_bytes, 0, _sck.ReceiveBufferSize, SocketFlags.None, AddressOf CallBackReceive, _sck)
      Catch
        RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
      End Try
    Else
      RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
    End If
  End Sub

#End Region

#Region "Timers events"
  Private Sub TimerConnectionTimeOut_Elapsed(ByVal sender As Object, ByVal e As system.Timers.ElapsedEventArgs) Handles TimerConnectionTimeOut.Elapsed
    TimerConnectionTimeOut.Enabled = False
    RaiseEvent ProblemDetected(ErrorType.connection_failed)
  End Sub

  Private Sub TimerSendTimeOut_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles TimerSendTimeOut.Elapsed
    If _sending Then Sendcm(_last_send.MyBytes) Else TimerSendTimeOut.Enabled = False
  End Sub

  Private Sub TimerSend_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles TimerSend.Elapsed
    If Not _sending Then
      If _qsnd.Count > 0 Then
        Sending(True)
        _last_send = _qsnd.Dequeue
        Sendcm(_last_send.MyBytes)
      End If
    End If
  End Sub

#End Region

#Region "Public properties"
  Public ReadOnly Property ReadNextMsg() As CommMessage
    Get
      If _qrcv.Count > 0 Then Return _qrcv.Dequeue Else Return Nothing
    End Get
  End Property

  Public ReadOnly Property MsgCount() As Integer
    Get
      Return _qrcv.Count
    End Get
  End Property

  Public ReadOnly Property IsSending() As Boolean
    Get
      Return _sending
    End Get
  End Property

#End Region

#Region "Public functions"

  Public Sub Send(ByVal Order As String, ByVal Message As String)
    Dim cm As CommMessage
    Dim LenTT As Integer = (Order & Message).Length
    If LenTT > 4000 Then
      'Si supérieur à 4000 alors découpe

      'Les paquets sont basés sur 4000 car le tout sera encodé en Unicode, donc doublé,
      'la limite des paquets des sockets étant 8192, j'ai arrondi la découpe à 4000
      'pour prendre en compte les autres paramètres transmis avec les messages
      Dim i As Integer = 0
      Dim j As Integer = 0
      Dim ti As Integer
      Dim tj As Integer
      Do
        tj = 0
        ti = 0
        cm = New CommMessage
        ti = IIf(Order.Substring(i).Length > 4000, 4000, Order.Substring(i).Length)
        cm.Order = Order.Substring(i, ti)
        i += ti
        If ti < 4000 Then
          tj = IIf(Message.Substring(j).Length > (4000 - ti), (4000 - ti), Message.Substring(j).Length)
          cm.Message = Message.Substring(j, tj)
          j += tj
        End If
        cm.IsComplete = (LenTT = i + j)
        _qsnd.Enqueue(cm)
      Loop Until cm.IsComplete

    Else
      'Sinon envoie tel quel
      cm = New CommMessage
      cm.Order = Order
      cm.Message = Message
      cm.IsComplete = True
      _qsnd.Enqueue(cm)
    End If
  End Sub

#End Region

#Region "Private functions"
  Private Sub Sendcm(ByRef _b() As Byte)
    Try
      _sck.BeginSend(_b, 0, _b.Length, SocketFlags.None, AddressOf CallBackSend, _sck)
    Catch
      RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
    End Try
  End Sub
  Private Sub Sending(ByVal Flag As Boolean)
    _sending = Flag
    TimerSendTimeOut.Enabled = Flag
  End Sub
#End Region

#Region "Socket delegate CallBack"

  'en mode serveur, renvoit un socket connecté à un client
  'puis repasse en attente
  Private Sub CallBackAccept(ByVal async As IAsyncResult)
    Try
      RaiseEvent ConnectionRequestAccepted(_sck.EndAccept(async))
      _sck.BeginAccept(AddressOf CallBackAccept, _sck)
    Catch
      RaiseEvent ProblemDetected(ErrorType.failed_to_listen)
    End Try
  End Sub

  'en mode client, établit la connexion
  Private Sub CallBackConnect(ByVal async As IAsyncResult)
    If Not TimerConnectionTimeOut Is Nothing Then TimerConnectionTimeOut.Enabled = False
    Try
      _sck.EndConnect(async)
      RaiseEvent Connected()
      TimerSend.Enabled = True
      _bytes = New Byte(_sck.ReceiveBufferSize) {}
      _sck.BeginReceive(_bytes, 0, _sck.ReceiveBufferSize, SocketFlags.None, AddressOf CallBackReceive, _sck)
    Catch
      RaiseEvent ProblemDetected(ErrorType.connection_failed)
    End Try
  End Sub

  'réception de données, puis se remet en attente
  Private Sub CallBackReceive(ByVal async As IAsyncResult)
    Dim size As Integer
    Try
      size = _sck.EndReceive(async)
      If size > 0 Then
        Dim cm As New CommMessage
        cm.SetBytes(_bytes)
        If cm.Order = "" AndAlso cm.Message = "" Then
          If cm.idCode = _last_send.idCode Then Sending(False)
        Else
          If cm.idCode <> _last_rcv.idCode Then
            If Not _last_rcv.IsComplete Then
              cm.Order = cm.Order.Insert(0, _last_rcv.Order)
              cm.Message = cm.Message.Insert(0, _last_rcv.Message)
            End If
            _last_rcv = cm
            If cm.IsComplete Then
              _qrcv.Enqueue(cm)
              RaiseEvent NewMessage()
            End If
          End If
          Sendcm(cm.ConfirmSequence)
        End If
      Else
        RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
      End If
      _bytes = New Byte(_sck.ReceiveBufferSize) {}
      _sck.BeginReceive(_bytes, 0, _sck.ReceiveBufferSize, SocketFlags.None, AddressOf CallBackReceive, _sck)
    Catch
      RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
    End Try
  End Sub

  'envoi des données terminées
  Private Sub CallBackSend(ByVal async As IAsyncResult)
    Try
      _sck.EndSend(async)
    Catch
      RaiseEvent ProblemDetected(ErrorType.seems_disconnected)
    End Try
  End Sub

#End Region

End Class

Conclusion

>>>>> Pourquoi avoir utilisé une structure perso (commMessage) pour l'envoi et la réception ?
L'utilisateur de cette classe simplifiée n'a pas à s'inquiéter de ce qu'il envoit et comment il l'envoit. Il envoit juste.
A la réception on reçoit donc un commMessage, on regarde l'ordre (par exemple avec un select case) et on interprete donc le message correctement, l'ordre nous renseignant sur ce qu'on doit faire du message (exemple : est-ce que c'est l'âge du gars, son nom ou bien son prénom ?)
Démo :

Dim cm As ClassComm.CommMessage = _comm.ReadNextMsg
Select Case cm.Order
  Case "nom"
    Msgbox("La personne connectée s'appelle " & cm.Message & ".")
  Case "age"
    Msgbox("La personne connectée a " & cm.Message & " ans.")
  Case "passion"
    Msgbox("La personne connectée est passionnée par " & cm.Message & ".")
End Select



>>>>> Pourquoi ne pas avoir mis en event un message arrivant ?
Parceque les sockets sont multithreads et c'est pas propre du tout : les events sont renvoyés dans un thread à part à cause des callbacks des sockets, donc on ne peut pas agir sur une interface à partir des events renvoyés par la class.
Alors j'ai jugé plus souple de mettre les messages arrivant dans une file d'attente FIFO (first in first out), qui sera par exemple checké par l'utilisateur à l'aide d'un Timer 100ms, ce qui est parfait pour de la comm réseau.
Démo :

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
  If Not _traitement Then
    If _comm.MsgCount > 0 Then
      _traitement = True
      Dim cm As ClassComm.CommMessage = _comm.ReadNextMsg
      'traitement du message ici ou renvoie sur une sub qui traitera
      _traitement = False
    End If
  End If
End Sub



>>>>> C'est quoi idCode dans le commMessage ?
La classe est consciencieuse : quand elle envoit un message, avant de pouvoir en envoyer un nouveau, elle attend d'avoir la confirmation que le message précédent a bien été reçu (bien que le protocole TCP/IP est censé s'en occupé... mais j'ai déjà subit trop de perte en réseau pour des causes inconnues donc avec ça, c'est plus un problème). Chaque message a un idCode unique, et ce code sert d'accusé de réception. Quand la class reçoit un message, elle renvoie l'idCode à l'expéditeur pour dire OK, sinon le dit expéditeur renverra sans cesse le message au bout d'un laps de temps (timeout définit au constructeur) jusqu'à avoir la confirmation que c'est bien reçu.
Pas d'inquiétude, les messages n'arrivent pas en double, le mécanisme est bien géré, tout est vérifié.
Pas d'inquiétude non plus, les nouveaux messages demandés à être envoyé entre un envoi précédent et son propre accusé de réception sont mis en file d'attente pour être envoyé après, donc rien n'est perdu.
Au final on a une class bien aboutie qui gère bien ses comm réseau à la place de l'user.
Bref, ne pas prendre garde à l'idcode, c'est pour info seulement, au cas où on en aurait besoin pour autre chose... sinon pas touche !!
De toute façon c'est une property codée et la valeur de l'IdCode n'est redéfinissable que si elle n'a pas encore été attribuée (valeur 0).







Exemple d'utilisation complet à lire :
Public Class Form1

  Private WithEvents Srv As New ClassComm(25000) 'ici on a déjà un serveur en écoute sur le port 25000
  Private WithEvents Clt1 As ClassComm
  Private WithEvents Clt2 As ClassComm

'en cliquant sur le Button1, on créé le client classComm CLT1 qui va tenter de se connecter au serveur (déjà ouvert juste au dessus)
  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Clt1 = New ClassComm("localhost", 25000)
  End Sub

'ici le serveur reçoit la demande de connexion et l'accepte en créant le client ClassComm CLT2
  Private Sub Srv_ConnectionRequestAccepted(ByRef sck As System.Net.Sockets.Socket) Handles Srv.ConnectionRequestAccepted
    Clt2 = New ClassComm(sck)
  End Sub

'ceci est l'évenement du client ClassComm CLT1 qui confirme que la connexion a bien eu lieu
'a ce stade on a donc une communication prête et ouverte entre CLT1 et CLT2, avec en plus un serveur qui écoute toujours
'normalement c'est fait pour du multiclient donc on devrait pas avoir juste CLT1 et CLT2 mais une collection de client
'et là à ce stade de cette petite appli, vu que ses 2 clients sont pris, on devrait fermer le serveur avec Srv.dispose, mais bref....
  Private Sub Clt1_Connected() Handles Clt1.Connected
    MsgBox("connecté" )
  End Sub

'en cliquand sur le Button2, on fait envoyer un commMessage (ordre + message) par CLT1
  Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    Clt1.Send(InputBox("ordre" ), InputBox("mon message" ))
  End Sub

'ici j'ai mis un bouton qui permet de dire si la file d'attente du CLT2 contient des messages
  Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
    MsgBox(Clt2.MsgCount)
  End Sub

'et ici un bouton qui lit les messages en attente dans CLT2
  Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
    Dim cm As CommMessage = Clt2.ReadNextMsg
    MsgBox(cm.Order & vbCrLf & cm.Message)

'ici il faudrait (exemple) :
'SELECT CASE cm.Order
'CASE "age" : msgbox ("le type a " & cm.Message & " ans" )
'CASE "nom" : msgbox ("le type s'appelle " & cm.Message)
'CASE "profession" : msgbox ("le type travaille dans " & cm.Message)
'END SELECT

  End Sub


'Les 2 subs précédentes devraient être dans un Timer avec un test sur MsgCount

'ici un exemple de l'event ProblemDetected chez CLT1
  Private Sub Clt1_ProblemDetected(ByVal source As ClassComm.ErrorType) Handles Clt1.ProblemDetected
    Select Case source
      Case ErrorType.connection_failed : MsgBox("échec connexion" )
      Case ErrorType.seems_disconnected : MsgBox("semble déconnecté" )
    End Select
    Clt1.Dispose()
    Clt1 = Nothing
  End Sub

End Class
 

Historique

11 avril 2007 15:48:24 :
Correction du << bug des longueurs >>, voir parmi les premiers commentaires pour l'explication. Dorénavant donc, la procédure Send découpe les ordres/messages trop long en plusieurs sous-messages, le tout étant reconstitué automatiquement à la fin. La propriété MsgCount n'indiquera un nouveau message reçu que lorsque tout les paquets auront été reçus et recollés en interne. Ajout d'une propriété en lecture seule << Sending >>, qui renvoie vrai ou faux selon qu'un envoi est actuellement en cours. Ajout d'un évènement << NewMessage >> qui se déclenche à l'enqueuing d'un message arrivant, uniquement pour informer de son arrivée.

Commentaires et avis

signaler à un administrateur
Commentaire de celiphane le 06/04/2007 14:56:25

Je n'ai pas encore pleinement exploité cette class et bien que je ne vois pas encore lesquels (car j'ai vraiment travaillé cette class pour qu'elle soit exploitable), j'attends vos déclarations de bug.

Cordialement,
Celiphane

signaler à un administrateur
Commentaire de Dnx le 07/04/2007 14:14:47

Hello,

ta source a l'air vachement bien faite, bien expliquée, je vais la tester un peu pour voir... mais avant, dans quel cas devrais-je utiliser ta classe? dans quand quel type d'application?

merci d'avance.

signaler à un administrateur
Commentaire de celiphane le 07/04/2007 16:29:07

Merci pour ton commentaire.

Pour répondre à ta question, cette class peut s'utiliser à chaque fois que tu auras besoin de faire un application qui fonctionnera en réseau. Les usages sont donc multiples, du plus simple chat / dialogue en direct, jusqu'à la plus complexe des applications clients / serveur en entreprise par exemple.

Chaque fois que tu auras besoin de faire des applications qui devront communiquer en réseau, poste à poste ou bien architecture clients / serveur, tu peux sortir cette class de ta manche et elle t'épargnera un fastidieux codage de sockets, mais aussi le développement d'un protocole de communication perso ainsi que des tests de bonnes réceptions de tes messages réseaux. Elle gère tout cela.

Instancié en serveur, la class est dès le début orienté multiclients pour l'architecture clients / serveur. Si on ne souhaite pas faire du multiclients mais juste du poste à poste, à l'évènement ConnectionRequestAccepted de la classcomm serveur il suffit de lui faire << .Dispose >> puis de la réinstancier en passant à son constructeur le socket fourni par l'évènement. Et voilà, c'est devenu un simple poste à poste.

Je pense que tous ceux qui ont déjà l'habitude de coder en réseau appréciront ces fonctionnalités très simples.


Cordialement,
Celiphane

signaler à un administrateur
Commentaire de hvb le 07/04/2007 20:45:39

salut, j'ai survolé un peu ton code. Je suis actuellement en train de finliser un projet semblable pour une de mes pti de fin d'année, et j'utilise la même ruse pour le multiclient.
Par contre perso j'ai opté pour une gestion d'évenement, avec la possibilité d'effectuer une synchronisation entre les threads. Je posterais d'ici quelques jours le resultat ici (d'ou ma deception quand j'ai vu ton post ^^)

signaler à un administrateur
Commentaire de celiphane le 07/04/2007 22:45:16

Salut l'ami,
je suis désolé pour toi d'avoir posté cette source avant la tienne !

Est-ce que c'est le fait de renvoyer le socket fraichement connecté à un serveur dans un event pour pouvoir le passer au constructeur d'une nouvelle class (pour << transformer >> le socket en ClassComm) que tu appelles une ruse ? Car je ne dirais pas que j'ai employé une quelconque feinte dans ce code source. Tu m'expliqueras quand tu pourras ! :-)

Concernant la gestion d'évènement, c'est effectivement un choix. Comme je l'ai expliqué, je n'ai pas souhaité obligé l'utilisateur de la class à devoir se synchroniser avec son interface à chaque réception de données. Voilà pourquoi j'ai opté pour une queue FIFO, ce qui permet en outre à l'utilisateur de traiter les messages dans l'ordre, de prendre tout le temps qu'il faut avec un message et de passer au second ensuite. A l'inverse en mettant les réceptions en évènement, le risque (et c'est fréquent) est que pendant la gestion d'un message fraichement reçu, un autre message arrive et redéclenche l'évenement ce qui fait que tout se déroule en même temps, je trouve que c'est confu et selon les cas ça peut faire des conflits (si les traitements des deux messages manipulent ne serait-ce qu'une même variable ou bien un même contrôle, les résultats deviennent inattendu).

Bref c'est pour ça que j'ai garder le concept du FIFO : les messages s'enqueuent les uns à la suite des autres, et même pendant qu'un message est en cours de traitement, la classcomm continu d'enqueuer les nouveaux messages, qui seront donc traités seulement à la suite, un par un, sans conflit ni résultat inattendu.

Je ne critique absolument pas, au contraire j'ai hâte devoir comment d'autres que moi gère tout ces inévitables conflits réseaux. Je présente juste ma vision des choses, avec mon expérience.

A très bientôt,
Celiphane

signaler à un administrateur
Commentaire de hvb le 08/04/2007 14:57:31

salut ^^
oui, moi j'ai consideré cela comme une ruse, car j'ai mis pas mal de temps à trouver un moyen d'en arriver là. D'ailleurs, ce n'est pas exactement la même methode, moi je fais ça dans un constructeur privé (est ce dans les normes? :/), à partir de la classe elle même et je n'ai d'ailleurs pas préciser que j'ai fait deux classes distinctes client et serveur. (alors qu'a l'origine je voulais refaire Winsock de vb6, mais j'ai trouvé ça plus pratique... et plus lisible)
Enfin bref, JE JE JE, on verra quand j'aurais posté, parlons de ton projet:
Il faut avouer que ton petit speach sur la méthode FIFO m'a plus ou moins convaincu et je commence à me demander si je ne devrais pas ajouter la possibilité de passer par autre chose que des évenements, même si mes nombreux tests ont pourtant été satisfaisant depuis la synchronisation(zut j'en reviens à moi).
Mais par contre, de ton coté, cela pourrait tout de même être pratique de renvoyer un envénement, sans données en arguments, mais pour prévenir qu'un "message" est arrivé. Apres celui ci, tu utilise la même méthode que lorsque c'est ton timer qui voit qu'un nouveau msg est là. Cela reste dans ton idée, non?

signaler à un administrateur
Commentaire de celiphane le 08/04/2007 19:16:20

Tout à fait, excellente remarque.
Annoncer l'arrivée de données via un évènement n'engage effectivement à rien d'autre que prévenir, et c'est en effet dans l'intérêt de l'utilisateur de la class.

J'intégrerais cet évènement en plus dans ma prochaine mise à jour que je compte finir après les fêtes de Pâques, car j'ai fait un oubli majeur sur cette class : les messages très longs sont actuellement scindé par le protcole TCP/IP (en paquet de 8192) et à la réception, la class pense que le message est complet alors qu'il ne l'est pas ! Bref quand elle tente de lire les bytes reçus alors qu'il s'agit d'un long message (ça se passe dans la sub << SetBytes >> de ma structure CommMessage), ça retourne une exception puisque le tableau de bytes est incomplet (a été scindé par le protocole TCP/IP).

Je n'avais pas réalisé dans mes petits tests de développeur genre << Coucou >> ou encore << Le chat boit du lait >> qu'en application réelle, on serait amené à envoyer des chaînes de plus 40Ko parfois lol ;-) !!!

Bref la solution est simple et je suis en train de la développer, c'est ma class qui va scinder les paquets pour reconstruire correctement à l'arrivé, et n'enqueuer QUE si la totalité des paquets sont parvenus. L'accusé de réception continuera de se faire paquet par paquet, et ça sera entièrement géré en interne.

Pardon pour cette erreur de parcours, quand j'y pense c'est si con de ma part de l'avoir oublié !

Celiphane

signaler à un administrateur
Commentaire de caolila le 10/04/2007 15:54:37

Salut les gars.

Sympa ta source j'vais voir ca de plus pres, moi aussi je bosse sur un truc similaire afin de gerer une application Client Serveur avec des clients du genre "chiant", cad de Clients WCE avec WLAN, ce sont des scanners de codebare. Avec des engins pareils il n'est pas rare pour ne pas dire tres tres regulier que la connection soit perdu pour des raisons diverses (perte de réseau, reset utilisateur etc...).Le serveur doit garder trace de tout puisqu il s'identifie a une DB au nom du Client WCE (User + Pass)
Dans la version actuelle j'ai choisi la meme solution que Celiphane, cad de faire une file d'attente ainsi qu'un AR. Il est en effet tres important de pouvoir traiter les paquets dans l ordre afin d'eviter les bugs du genre update  avant create...hehehe

bref je vous ferais un ptit coucou apres avoir regarde ce post en détail.

Sinon a priori bon boulot .
@+
Hugues

signaler à un administrateur
Commentaire de caolila le 10/04/2007 15:57:29

Oups un ptit oubli.

As tu pensé a la question de la sécurité, les ptits paquets peuvent etre "sniffer" et la... tout est possible pour un gars sachant un tout petit peu bidouiller....

signaler à un administrateur
Commentaire de celiphane le 10/04/2007 17:11:52

Salut CAOLILA,

J'y ai bien pensé mais je ne l'ai pas intégré, car je n'en ai pas le besoin. J'avais aussi pensé à la compression des messages.

Dans les deux cas, il suffit de rajouter la routine qui transforme le message (compression ou cryptage) dans la structure CommMessage au niveau de
<< Public ReadOnly Property MyBytes() As Byte() >> pour la transformation
et de
<< Public Sub SetBytes(ByRef ByteCommMessage As Byte()) >> pour le retour au message d'origine.

Je commence à travailler sur << mon bug de longueur >> expliqué dans le commentaire plus haut.

Cordialement,
Celiphane

signaler à un administrateur
Commentaire de celiphane le 10/04/2007 17:12:47

Et merci pour ton intérêt.

signaler à un administrateur
Commentaire de hvb le 10/04/2007 17:19:22

je ne comprend pas la remarque sur le fait de pouvoir sniffer, tout echange réseau peut être sniffé, non? comment pourrait on palier à ça?
Si la solution que vous sous-entendiez était le cryptage, je pense fortement que c'est à l'utilisateur de la classe de gerer ça et non le contraire
peut-être que je n'ai rien compris à la remarque, et dans ce cas je m'en excuse et veut en savoir plus :D

(désolé celiphane de prendre de la place sur ton espace ^^)

signaler à un administrateur
Commentaire de hvb le 10/04/2007 17:21:29

celiphane : pareil pour la compression, je ne pense pas que tu devrais ajouter cette fonctionalité directement dans ta classe, car celle ci ne servirait donc plus qu'a communiquer entre un client ET un serveur codés à partir de ta classe. là n'est pas l'interet... je pense.

signaler à un administrateur
Commentaire de celiphane le 10/04/2007 17:25:02

Il n'y a pas à être désolé, j'estime que l'espace commentaires d'une source est fait pour ça !

Non rassure toi tu as parfaitement compris la question. Et tu as raison également sur le fait que dans l'absolu, c'est plutôt à l'utilisateur de transformer ses messages comme il l'entend.

Maintenant si on se place dans l'optique du développement d'une class parfaite et simple, si elle intégrait un cryptage et/ou une compression de données automatique et transparente pour l'utilisateur et ce sur simple demande dans le constructeur de la-dite class, ce serait un plus non négligeable voilà tout. Mais comme je l'ai dit plus haut, je n'en ai pas eu le besoin. Et comme j'ai initialement développé cela pour un projet bien conci, ça n'y figure pas voilà tout.  ;-)

Cordialement,
Celiphane

signaler à un administrateur
Commentaire de celiphane le 10/04/2007 17:28:55

Message croisé, je reprends la parole rapidement pour rebondir sur ton dernier message : << car celle ci ne servirait donc plus qu'a communiquer entre un client ET un serveur codés à partir de ta classe >>.

Je suis désolé pour toi que tu ne l'ai pas compris plus tôt, mais c'est déjà le cas : ma classcomm ne communique qu'avec une autre classcomm.

ClassComm <----> ClassComm = oui
ClassComm <----> Socket = non

Elle ne peut servir en l'état qu'à simplifier la communication réseau dans une solution logicielle complète, mais ne peut pas s'intégrer dans une solution avec un protocle existant (genre navigateur web <----> ClassComm). Elle n'a pas pour but de simplifier l'utilisation des Sockets, elle EST à elle seule une forme de communication hyper simplifiée (à l'utilisation) qui REPOSE sur les sockets.

Cordialement,
Celiphane

signaler à un administrateur
Commentaire de caolila le 10/04/2007 17:32:36

Salut HBV

Le but de cette classe n'est il pas de rentre la vie plus facile a l'utilisateur finale ?
C'est dans ce contexte et uniquement dans celui-ci que je parlais de "securite" de de "sniffage". Si tu prends le FTP ou le POP3  tout est en clair et donc il est tres tres facile de lire un mot de passe. apres boujour les dégats.

Imagine le travail dans un team, chacun a sa specialité (pour ne pas dire ses problemes) et si ce genre d'integration (securité, compression etc) est transparente c'est le pied et un gain de productivité..

N'oublions pas que les clients (les gars qui payent mon salaire) sont soucieux de leur sécurité...donc autant l'integrer en standard...

Hugues

signaler à un administrateur
Commentaire de hvb le 10/04/2007 17:50:55

Celiphane : ouf, heuresement que j'ai verifier avant de poster, je n'avais pas lu ton dernier message et allait poster tout un roman sur "pourquoi ne pas l'intégrer".
Grosse erreur de ma part, je n'avais donc pas totalement saisi le but de ta classe, je m'excuse et reviens sur ma remarque: oui il serait donc très interessant d'ajouter cryptage et compression directement à celle-ci :)
Caolila : cf le message pour celiphane, betise de ma part, à oublier :)

bonne continuation à vous deux

signaler à un administrateur
Commentaire de PWM63 le 11/04/2007 10:18:02

Ta classe m'a l'air d'être un super boulot, et je crois que je vais d'abord la tester pour son travail premier à savoir la communication, et ensuite, peut-être que je l'intègrerai dans une de mes applications multipostes afin de la transformer en client/serveur.

En tout cas, j'attends avec impatience la mise à jour pour le bug de longueur.

Félicitations !
Stéphane

signaler à un administrateur
Commentaire de celiphane le 11/04/2007 15:51:52

Nouvelle mise à jour appliquée.
Voir dans l'historique des MAJ pour plus d'informations.

Celiphane

signaler à un administrateur
Commentaire de celiphane le 12/04/2007 13:42:17

Bonjour, suite à une question en messagerie privés, je fais partager à tous ma réponse, qui illustre 3 utilisations possibles :

___________________________________
Communications de Poste A à Poste B
___________________________________

But } Faire de la comm' d'un poste à un autre

- Instancier sur A une ClassComm avec un port en écoute
- lorsque la connexion est souhaitée, instancier sur B une ClassComm en passant l'IP du poste A, et le port choisi
- ClassComm A renvoit l'évènement << ConnectionRequestAccepted >>
- A n'a plus besoin d'écouter, donc on appelle la méthode << Dispose >> de ClassComm A et on la réinstancie en lui passant << Sck >>, le socket donné en argument

= Ici ClassComm A et ClassComm B peuvent communiquer
= Pour fermer l'une des deux, il faut lui faire appeler << Dispose >>, et l'autre renverra une erreur d'origine << seems_disconnected >>



________________________________
Communications ClientS / Serveur
________________________________

But } Par exemple une base de données nourrie à distance par des applis clientes

- une appli SRV instancie une ClassComm avec un port en écoute
- le client qui veut se connecter instancie sa ClassComm en passant l'ip du SRV et le port choisi
- ClassComm SRV lève l'event << ConnectionRequestAccepted >>
- il instancie une nouvelle ClassAPPLI (exemple) qui héberge une ClassComm en public
- la ClassAPPLI nouvellement créée instancie sa ClassComm avec << Sck >> qui a été fourni par l'event de SRV
- la ClassAPPLI est ajoutée dans une collection afin de continuer à vivre
- chaque ClassAPPLI gère donc ainsi sa communication avec une appli Cliente distante, dans notre exemple elle peut recevoir des requêtes SQL et les exécuter sur la base de données.

= Ici il peut donc y avoir des centaines de clients distants connectés sur une appli SRV



________________________________________________________
Communications Multi-clients, avec un serveur qui relaye
________________________________________________________

But } Des applis clientes qui passent par un serveur qui centralise les demandes pour les dispatcher aux autres applis clients

- exactement la même construction qu'en Clients / Serveur (ci-dessus)
- la collection d'appli clients sur SRV est public
- quand une appli cliente reçoit un message, il lui suffit de parcourir la collection pour faire demander à chaque ClassComm hébergée de faire passer un message.
- c'est au développeur de gérer ce qu'il faut faire (relayer ou non, à tous, à une appli cliente en particulier etc... la ClassComm se prête très bien à ce type de construction grâce à la réception séparée d'un ordre et d'un message : l'ordre renseigne sur ce qu'il faut faire du message)

= On peut tout faire en faite... enfin personnellement le DVP réseau m'a toujours ouvert l'esprit sur les possiblités infinies des applications, mais là avec cette ClassComm (et je me vante pas), c'est vraiment servi sur un plateau. Il ne reste plus qu'à y ajouter vos compétences, votre imagination et votre savoir-faire, car bien évidement, ce n'est qu'une class réseau, pas un wizard universelle !!! ;-)


Celiphane

signaler à un administrateur
Commentaire de celiphane le 20/04/2007 09:42:49

Salut à ceux qui passent,

je n'ai eu de nouvelles des éventuels testeurs de ma source depuis la mise à jour...
Pourrais-je avoir un feedback et une note correspondante svp, j'ai besoin de retours pour estimer le travail et pouvoir le modifier en conséquence !

Cordialement,
Celiphane

signaler à un administrateur
Commentaire de willou62 le 20/04/2007 21:26:18

Bonjour, avez vous une class similaire pour le framework 1.1, est-il possible de l'adapter pour celui-ci? j'utilise VS 2003 et Je n'ai pas assez de connaissances sur le sujet, en vb6 ça passait mais là je séche :-(, j avoue que cela me faciliterai la vie car je suis sur une programmation de serveur de données (cas du "Communications Multi-clients, avec un serveur qui relaye")
. L'idée de cette class est vraiment excellente et félicitation pour cette prog.

signaler à un administrateur
Commentaire de celiphane le 20/04/2007 23:42:06

Salutations Willou62,

concernant la conversion vers le framework 1.1, il y a justement PWM63 (qui a posté quelques commentaires juste au dessus) qui m'a annoncé avoir effectué cette conversion en messagerie privée... je n'ai toutefois plus eu de nouvelles et je ne sais pas si sa conversion fonctionne à 100%, mais c'est une des meilleurs pistes que je puisse te proposer pour le moment, à part bien sûr de migrer directement sous Visual Studio 2005 (un bonheur... franchement, il faut y aller ! VS2003 se fait vraiment vieux).

Très cordialement,
Celiphane

signaler à un administrateur
Commentaire de PWM63 le 25/04/2007 11:37:59

Willou62, je te contacte pour voir comment te passer la classe de Celiphane que j'ai convertit.
Par contre, elle ne fonctionne pas à 100%, et je n'ai toujours pas pris le temps de me pencher dessus pour résoudre un problème mineur.

Stéphane.

signaler à un administrateur
Commentaire de willou62 le 25/04/2007 13:39:49

Bonjour messieurs, merci Celiphane et PWM63 pour votre éclairage suite à ma question. Je vais tenter de mettre tout ça en application et amener si possible une correction si cela fait partie de mes capacités. Je tiens à dire que la source me parait super fiable et très bien expliquée d'où mon intêret pour celle-ci. Je ferai un retour dès que ce sera opérationnel de mon côté. Merci Celiphane pour ton encouragement à passer sur VS 2005, c'est pas l'envie qui manque, le problème c'est que beaucoup de parcs informatiques en entreprise sont toujours avec le FRAME WK 1.1 :-(

Bonne continuation et surtout bonne prog!  

signaler à un administrateur
Commentaire de kmw le 12/05/2007 20:55:58

Bonjour Celiphane, j'ai testé ta source mais sans succès. J'ai tjs un echec de connexion du client... N'étant pas expert j'ai quand même cherché à comprendre pourquoi mais je n'y arrive pas. Comment fait-on pour connaître l'adresse ip du serveur lors de sa construction? Parce que quand je regarde ce que renvoie IPAddress.any j'obtiens 0.0.0.0

signaler à un administrateur
Commentaire de kmw le 13/05/2007 00:43:16

C'est bon j'ai trouvé mon pb...

signaler à un administrateur
Commentaire de tampo80 le 13/06/2007 22:06:57

Salut man
ta classe est super cool il tombe à pic pour moi car je suis un projet de gestion de cybercafé , mais le problême est que comment puis-je identifier chaque client qui se connecte et pouvoir luis répondre chaque fois qu'il emet une demande ou lorsque le serveur à un message propre à un client specifique je cfais comment?
merci d'avant pour ta réponse  

signaler à un administrateur
Commentaire de goyotaty le 18/06/2007 07:46:57

Est-ce qu'il n'est pas possible rendre cette classe universelle en y ajoutant des options qui feront qu'il soit possible de l'utiliser en mode "socket" normal; Que l'utilisateur ait le choix de la configuration Classcomm <-> Classcomm et/ou Classcomm <-> Socket? Cela en ferait une classe universelle très pertinente ! Merci encore pour le travail effectué !

signaler à un administrateur
Commentaire de kmw le 19/06/2007 20:54:13

Pour TAMPO80, voici ce que j'ai fait sur une de mes applis

   Friend WithEvents Srv As New ClassCom(11000) 'ici en écoute sur le port 11000
   Friend SDList As New ArrayList() ' Liste des sockets entrants
   Friend SDSock As New ArrayList() ' Liste des IP des sockets entrants

    'ici le serveur reçoit la demande de connexion et l'accepte en créant le client dans la liste associée des sockets
    Private Sub Srv_ConnectionRequestAccepted(ByRef sck As System.Net.Sockets.Socket) Handles Srv.ConnectionRequestAccepted
        Dim AcceptSock As New ClassCom(sck)
        Dim WR As New StreamWriter("D:\xxx\Cnx.log", True) 'Fichier log
        If SDList.Count > 9 Then  'Si l'on veut limiter le nombre de connexions
            WR.Write(Now.ToString & " : Client -> connexion rejetée de " & sck.RemoteEndPoint.ToString & vbNewLine)
            WR.Close()
            WR.Dispose()
            AcceptSock.Dispose()
        Else
            SDList.Add(AcceptSock)
            SDSock.Add(sck.RemoteEndPoint.ToString)
            AddHandler AcceptSock.ProblemDetected, AddressOf SD_ProblemDetected
            WR.Write(Now.ToString & " : Client -> connexion de " & sck.RemoteEndPoint.ToString & vbNewLine)
            WR.Close()
            WR.Dispose()
            AcceptSock.Send("Login", "Log")
        End If
        
    End Sub

Ensuite pour les supprimer de la liste suite à une déconnection j'ai modifié une ligne de la classe, à savoir
Public Event ProblemDetected(ByVal source As ErrorType, ByVal Cliente As Socket)

Puis dans ton programme...

Private Sub SD_ProblemDetected(ByVal source As ClassCom.ErrorType, ByVal Cliente As Socket)
        Dim AcceptSock As New ClassCom(Cliente)
        Dim WR As New StreamWriter("D:\xxx\Cnx.log", True)

        For i as Integer = 0 To SDSock.Count - 1
            If SDSock(i) = Cliente.RemoteEndPoint.ToString Then
                WR.Write(Now.ToString & " : Client-> deconnexion de " & SDSock(i).ToString & vbNewLine)
                AcceptSock.Dispose()
                SDList.RemoveAt(i)
                SDSock.RemoveAt(i)
            End If
        Next
        WR.Close()
        WR.Dispose()
end sub

signaler à un administrateur
Commentaire de newbieflag le 23/11/2007 15:26:05 10/10

Excellente source, du travail de pro, ça m'a bien aidé pour mon projet ;-) bonne continuation

signaler à un administrateur
Commentaire de Yaurthek le 24/11/2007 14:32:26 10/10

Vraiment très utile et bien fait! Merci

signaler à un administrateur
Commentaire de COlive le 01/02/2008 10:16:13 9/10

Bonjour, très bon code.
Bien structuré, lisible, commentaires et explications détaillées.

Merci.

signaler à un administrateur
Commentaire de dimitriusai le 11/02/2008 10:27:08

Merci pour la classe.
Je suis à la recherche d'une facon pour créer des threads afin de créer un nouveau client, chaque fois que le serveur recoit une demande de connection.

    'ici le serveur reçoit la demande de connexion et l'accepte en créant le client ClassComm SrvReceive
    Private Sub Srv_ConnectionRequestAccepted(ByRef sck As System.Net.Sockets.Socket) Handles Srv.ConnectionRequestAccepted

        SrvReceive = New ClassComm(sck)
        While SrvReceive.MsgCount < 1
        End While
        Dim cm As ClassComm.CommMessage = SrvReceive.ReadNextMsg
        MySQL_SELECT(cm.Message)

    End Sub

Comment faire pour créer un thread avec seulement ceci

SrvReceive = New ClassComm(sck)
        While SrvReceive.MsgCount < 1
        End While
        Dim cm As ClassComm.CommMessage = SrvReceive.ReadNextMsg
        MySQL_SELECT(cm.Message)

? Merci d'avance

signaler à un administrateur
Commentaire de dimitriusai le 11/02/2008 14:37:42

Voilà la solution:

Private Sub Srv_ConnectionRequestAccepted(ByRef SocketLocal As Socket) Handles Srv.ConnectionRequestAccepted
        Try
            'Création du thread
            Dim myNewThread As New Thread(AddressOf Me.test)

            ' Dim myNewThread As New System.Threading.Thread(AddressOf test)
            myNewThread.Name = "Thread ID:" & ThreadID
            ThreadID += 1
            'Invoke(New testDeleg(AddressOf test), New Object() {socket})
            'Démarrage du Thread
            myNewThread.Start(SocketLocal)
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try

    End Sub

signaler à un administrateur
Commentaire de Yaurthek le 18/04/2008 16:01:06

Bonjour,
Cette classe m'est très utile, comme je l'ai déjà dit, mais je commence à atteindre ses limites : serait il possible d'inclure l'envoi de fichier ?(donc d'un tableau de byte directement plutôt qu'une string)
J'ai cherché longtemps comment faire, et la seule façon que j'ai trouvée est de transformer un fichier en string : System.Text.Encoding.Default.GetString(IO.File.ReadAllBytes(cheminDuFichier))
Cette méthode fonctionne mais je pense que ce serait bien plus rapide d'envoyer directement le fichier (j'ai essayé de modifier la classe, mais étant donné que j'ai déjà du mal à comprendre comment elle fonctionne...)
Alors Celiphane, si tu repasse par là, et que tu a du temps libre, je pense que ce serait une bonne idée...^^
Merci d'avance si quelqu'un répond

signaler à un administrateur
Commentaire de Bobdesbois le 10/10/2008 14:28:09 8/10

Bonjour,
Merci pour cette class. Je voulais savoir ce qu'il faudrai modifier pour l'utiliser sur windows mobile 5. Parce que j'ai essayé mais ca ne marche pas directement.
En tout cas elle marche tres bien pour du PC a PC.
Encore merci.

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

.NET & compress method [ par joker ] je cherche en .Net comment accéder à la méthode Compress ou CompressEx de la class Win32_CodecFile.L'aide parle de WMI class, bref ... je sais pas tro menu navigation class vb.net [ par ErB ] bonjourje cherche un modele de class pour un menu de type windows (fichier, edition, affichage, aide...) pour winform SDI en VB NETplus pour l'idee ge Class sous VB.NET [ par steph95 ] Je m'initie à VB.net (ASP) et j'utilise un script dont certains nom ne sont pas déclaré comme FileStream, FileInfo ... , dans le tutorial, il m'est in WINSOCK 6 à .NET => serveur multiclient [ par franckies ] WINSOCK 6 à .NET =&gt; serveur multiclientSalut à tous...Voici mon probleme:J'ai créer un serveur multi-client (chat) et un client sous VB6.Le petit p voir les pc du reseau [ par laurent180 ] Bonjour à vous,aimerai listé dans un combobox tout les nom des pc du reseau: j'ai essayé avec les api mais j'y arrive pas suis debutant.maintenant j'e VB.NET Class ControlDesigner [ par labout ] laboutAu secours les spécialistes de VB.NETJe veux un Inherits de la classe ControlDesignerparInherits System.Windows.Forms.Design.ControlDesignerJ'ai connexion lecteur reseau [ par phil-y2k ] Bonjour/soir,je suis encore moins que debutant en programmation.je recherche un script vbs qui me permette de deconnecter un lecteur reseau s'il exist Net send - detection des ip sur le reseau [ par franckpeer2p ] salut @tous,bon, je vous explique la situation pour que vous compreniez ce que je veux:imaginez vous dans une salle reseau, tout le monde envoie des m Socket en vb.net dotnet [ par Spe6men ] Voila je voudrai envoyer une constante = a ÿÿÿÿgetstatusa un serveur Et je veu ensuie traiter sa reponse commen faire svpG essayer ca mai ca marche pa Timeout pour socket en .NET [ par mastercatz ] Bonjour.J'ai fait un petit prog qui doit envoyer des données à une application cliente à l'aide de Socket.NetJ'aimerais savoir s'il y a un moyen d'avo


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,421 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.