Télécharger la documentation au format Word
Winsock
Les bases
- Winsock est un composant intégré à windows, donc on peut pas trop lui en demander
- Winsock est un socket synchrone, il est donc impossible d'émettre et de recevoir sur un même composant si il n'a pas été réinitialisé entre-temps
- Winsock n'intégre pas de timeout ( ou alors j'ai mal vu ) donc c'est à soi de créer un timer pour gérer cela
L'écoute
On va se dire « bah je le met en listen point final » , d'un côté c'est pas faux mais on peut quand même allez voir plus loin
A savoir :
Voici le langage tenu par une connection entrante : « Bonjour je suis madame A venant de B et je viens voir madame C chez D
- A c'est le port de la machine distante
- B c'est l'adresse de la machine distante
- C c'est le port de la machine locale
- D c'est l'adresse de la machine locale
On ne peut forcer l'addresse ip ou résolue de la machine locale avec winsock
Propriété :
- localip : un des adresse ip de la machine locale, le gros probléme vient des machines multi-ip ( plusieurs cartes réseaux ou ADSL/cable oui VPN ), ne peut être modifiée
- localhostname : l'addresse ip de localip résolue, ne peut être modifiée
- localport : Le port locale qui servira à l'écoute, il est préférable de ne pas choisir une valeur en dessous de 1024 ( compris ) car ces ports sont réservés au systéme d'exploitation
- protocol : euh vous voulez vraiment choisir ?? TCP ou UDP ( mattez le net pour les différences mais l'UDP est conseillé pour des connections où la perte ou l'ordre des données n'a pas d'importance ... enfin je crois )
- remotehost : pour restreindre l'écoute à une seule addresse
- remoteport : pour restreindre l'écoute à un seul port
- sockethandle : en général c'est à utiliser en conjonction avec une api
Exemple :
La base :
' Pudence est mère de sureté
WinSock.Close
' Pas en dessous de 1024
WinSock.LocalPort = 1025
' Go !
WinSock.Listen
Pareil mais avec un timeout de connection
' Ouais bon hein, on sait jamais
Timer.Enabled = False
' Dix secondes pour se connecter, couuuuuurs forest
Timer.Interval = 10000
' Pudence est mère de sureté
WinSock.Close
' Pas en dessous de 1024
WinSock.LocalPort = 1025
' Au dernier moment on active
Timer.Enabled = True
' Go !
WinSock.Listen
Private Sub Timer_Timer()
' Commençons par juguler cet excés de zéle
Timer.Enabled = False
' C'est fini pour le pauvre winsock
WinSock.Close
End Sub
Connection entrante
Je sais c'est facile de faire Winsock.accept, mais on peut toujours se planter :p
A savoir :
- Un socket windows peut accepter plusieurs connections simultanées sur un même port même si ce n'est pas gérable avec VB, de ce fait il faut savoir distinguer les connection, c'est le rôle de requestID
- De même une connection entrante sera identifiée par son adresse et son port
- Pourquoi le port distant et local serait différent ?? Et bien si quelqu'un utilise un proxy, il va alors transposer les ports, pour en savoir plus il suffit d'allez mater le fonctionnement des proxy/socket5/socket4
Propriété :
- requestID : je viens de le dire :p
- remotehost : adresse de la machine distante
- remotehostip : adresse ip de la machine distante
- remoteport : port de la machine distante
- sockethandle : en général c'est à utiliser en conjonction avec une api
Exemple :
La base :
' Premiére étape, fermer le winsock car on passe de écoute à connection
WinSock.Close
' Accepter la connection
WinSock.Accept requestID
Redirection de la connection sur un autre winsock
' Premiére étape, fermer le winsock pour pas être emmerdé
WinSock.Close
' Premiére étape, fermer l'autre winsock pour pas être emmerdé
WinSockReceive.Close
' Accepter la connection sur l'autre winsock
WinSockReceive.Accept requestID
' Relancer l'écoute
WinSock.Close
Connection sortante
Le principe est grossiérement le même que la connection entrante
A savoir :
- Gardez toujours à l'esprit le probléme des timeouts
Propriété :
- remotehost : adresse de la machine distante
- remotehostip : adresse ip de la machine distante
- remoteport : port de la machine distante
- sockethandle : en général c'est à utiliser en conjonction avec une api
Exemple :
La base :
' Prudence prudence
WinSock.Close
' Machine distante IP et Port ( dans notre cas on va se connecter chez nous )
WinSock.RemoteHost = "127.0.0.1"
WinSock.RemotePort = 1025
' Go ! Goldorak go ! euh non winsock
WinSock.Connect
Pareil mais avec un timeout de connection
' Ouais bon hein, on sait jamais
Timer.Enabled = False
' Dix secondes pour se connecter, couuuuuurs forest
Timer.Interval = 10000
' Prudence prudence
WinSock.Close
' Machine distante IP et Port ( dans notre cas on va se connecter chez nous )
WinSock.RemoteHost = "127.0.0.1"
WinSock.RemotePort = 1025
' Au dernier moment on active
Timer.Enabled = True
' Go ! Goldorak go ! euh non winsock
WinSock.Connect
Private Sub Timer_Timer()
' Commençons par juguler cet excés de zéle
Timer.Enabled = False
' C'est fini pour le pauvre winsock
WinSock.Close
End Sub
Envoi de données
On pars du principe que la connection a été effectuée
A savoir :
- On se contrefout du timeout
- Evitons quand même d'envoyer trop de données en même temps
- Je ne sais plus si c'est d'actualité mais il est préférable d'envoyer VBctrl à la fin des données, ce bug avait été rencontré lors d'une connection en telnet, du coup je préfère être prudent et l'utiliser en permanence
Propriété :
Où ça ?
Exemple :
La base :
' Définition du texte ( oui bon je simule quand même )
Texte$ = "Hello Network !"
' Prudence
Texte$ = Texte$ + vbCrLf
' Goooooooooo !
WinSock.SendData Texte$
Réception de données
Pareil, on pars du principe que tou a fonctionné jusqu'à présent
A savoir :
- On se contrefout du timeout
Propriété :
Où ça ?
Exemple :
La base :
Private Sub WinSock_DataArrival(ByVal bytesTotal As Long)
' On va éviter les pbs en utilisant la taille des données entrantes
Data$ = Space$(bytesTotal)
' Et on réceptionne
WinSock.GetData Data$, , bytesTotal
' On a fini, alors on range on passe un coup de balai et on se casse
WinSock.Close
End Sub
Mega exemple de la mort qui tue ( ok j'éxagére un peu, mais juste un peu )
Voilà le principe, nous allons initialiser un socket de connection, ainsi qu'un certain nombre de socket de réception et d'envoi de manière à faire un pseudo asynchrone.
Ensuite on va attendre que des gars se connectent chez nous.
Il est important de savoir que tel quel ce programme ne sert à rien et ne marchera pas car personne ne peut se connecter chez nous vu que personne ne sait qu'on existe.
En gros il pourrait fonctionner conjointement à un serveur et en augmentant le protocol qui se résume à l'heure actuelle à : envoi/réception de text et initialisation de la connection.
Dans la feuille on a :
- un timer nommé Timer et indexé à 0
- 1 winsock nommé Winsock
- 2 winsocks nommés WinsockReceive et WinsockSend tout les deux indexés à 0
- 2 zones de textes nommées TextGlobal et TextEnvoi
' Nombre maxi de connection simultanées
Const NombreSockets = 20
' Timeout lors d'une connection, exprimé en millisecondes
Const TimeOut = 5000
' Port local d'écoute
Const LocalPort = 1030
Private Sub Form_Load()
' Définition des propriété de la zone d'affichage
With TextGlobal
.MultiLine = True
.Text = ""
End With
' Pré-paramétrage des timers
With Timer(0)
.Enabled = False
.Interval = TimeOut
End With
' Chargement et initialisation des Sockets et de Timers
For x% = 1 To NombreSockets - 1
Load WinSockReceive(x%)
Load WinsockSend(x%)
Load Timer(x%)
WinsockSend(x%).Tag = ""
WinSockReceive(x%).Tag = ""
Next x%
' Initialisation et lancement de l'écoute
With WinSock
.LocalPort = LocalPort
.Listen
End With
End Sub
Private Sub TextEnvoi_KeyPress(KeyAscii As Integer)
' Si l'utilisateur a appuyé sur entrée, c'est qu'il veut envoyer le
' texte ou alors il a rien compris
If KeyAscii = 13 Then
' On va tester tout les sockets d'envoi
For x% = 0 To NombreSockets - 1
' On vérifie si le socket d'envoi est paré
If WinsockSend(x%).Tag = "OK" Then
' On balance le texte saisi
WinsockSend(x%).SendData TextEnvoi.Text + vbCrLf
End If
Next x%
End If
End Sub
' Timeout lors d'une connection
Private Sub Timer_Timer(Index As Integer)
' On va déjà arrêter le timer
Timer(Index).Enabled = False
' Puis fermer les Sockets de réception et d'émission
WinsockSend(Index).Close
WinSockReceive(Index).Close
' Et réinitialiser leurs valeurs
WinsockSend(Index).Tag = ""
WinSockReceive(Index).Tag = ""
End Sub
' Lorsqu'un péquin veut se connecter chez nous
Private Sub WinSock_ConnectionRequest(ByVal requestID As Long)
' On ferme le Socket d'écoute par prudence je dirai
WinSock.Close
' On va chercher un Socket libre
For x% = 0 To NombreSockets - 1
If WinSockReceive(x%).Tag = "" Then Exit For
Next x%
' Si x% = NombredeSockets alors on a tout essayer et rien n'est libre
If x% = NombreSockets Then
' Donc on abandonne, le gars finira en ping timeout
WinSock.Listen
Exit Sub
End If
' Sinon ...
With WinSockReceive(x%)
' Initialisation + prudence
.Tag = ""
.Close
' On accepte la connection
.Accept requestID
' Et on le dit clairement
.Tag = "OK"
End With
' Initialisation + prudence du socket d'émission
With WinsockSend(x%)
.Tag = ""
.Close
End With
End Sub
' Bah qu'est-ce que j'ai fait, pourquoi suis-je ainsi rejeté ? :'(
Private Sub WinSockReceive_Close(Index As Integer)
' Bon pas du coup on va tout fermer et réinitialiser
WinSockReceive(Index).Close
WinsockSend(Index).Close
WinSockReceive(Index).Tag = ""
WinsockSend(Index).Tag = ""
Timer(Index).Enabled = False
End Sub
' Ouais m'man j'ai du courrier !!! :p
Private Sub WinSockReceive_DataArrival(Index As Integer, ByVal bytesTotal As Long)
' Préparation de la variable recevant les données
Dim Data As String
Data = Space$(bytesTotal)
' Réception des données
WinSockReceive(Index).GetData Data
' Petit test, car lors d'une première connection on aimerait
' recevoir le port sur lequel on doit se connecter chez le gars
' Par défaut ces données commencent par le caractére de code 0
If Left$(Data, 1) = Chr$(0) Then
' Première connection, ok chuis prêt
With WinsockSend(Index)
' Assignation de l'adresse et du port de la machine distante
' au socket d'émission correpondant
.RemotePort = Str2int(Mid$(Data, 2, 2))
.RemoteHost = WinSockReceive(Index).RemoteHost
' Lancement du timer de timeout ( comme c'est bien dit :D )
Timer(Index).Enabled = True
' Lancement de la connection
.Connect
End With
Else
' Sinon ce n'est qu'une banale réception de texte, alors on l'affiche
TextGlobal.Text = Str$(Index) + "> " + TextGlobal.Text + Data
End If
End Sub
' Bah qu'est-ce que j'ai fait, pourquoi suis-je ainsi rejeté ? :'(
Private Sub WinsockSend_Close(Index As Integer)
' Bon pas du coup on va tout fermer et réinitialiser
WinSockReceive(Index).Close
WinsockSend(Index).Close
WinSockReceive(Index).Tag = ""
WinsockSend(Index).Tag = ""
Timer(Index).Enabled = False
End Sub
' On est connecté chez notre client
Private Sub WinsockSend_Connect(Index As Integer)
' On oubli pas le timer sinon on pourrait partir en timeout alors que c'est même pas vrai
Timer(Index).Enabled = False
' On prépare les données à envoyer
Dim Data As String
' Caractére de code 0 -> première connection
Data = Chr$(0)
' Suivi de la représentation sur 2 octets de notre port d'écoute
Data = Data + Int2str(LocalPort) + vbCrLf
' Et on envoi ça
WinsockSend(Index).SendData Data
' On finit en indiquant notre socket d'émission comme prêt
WinsockSend(Index).Tag = "OK"
End Sub
' Petite fonction utile pour transformer un integer en chaîne de 2 caractéres
Private Function Int2str(Source As Integer) As String
Int2str = Space$(2)
Mid$(Int2str, 1, 1) = Source \ 256
Mid$(Int2str, 2, 1) = Source Mod 256
End Function
' Petite fonction utile pour transformer une chaîne de 2 caractéres en un integer
Private Function Str2int(Source As String) As Integer
Str2int = Asc(Mid$(Source, 1, 1)) * 256
Str2int = Str2int + Asc(Mid$(Source, 2, 1))
End Function