Winsock Tutorial 2



Giving the user options ...
In our previous *winsock tutorial*, we hard-coded into the application
the RemoteHost and RemotePort that we'll be using.
But what if we want to give the user the choice ?
Well we can just use text boxes and command buttons for this.

Add to a new form a winsock control, 2 textboxes, a listbox,
and a command button.
Name them as follows ;

Code:
Winsock Control; sock
TextBoxes      ; txtPort and txtHost
ListBox        ; lstInfo
CommandButton  ; cmdConnect
You may also want to stick a few labels onto your form to tell the user
what the textboxes and listbox are for.
Change cmdConnect's caption to "Connect"

Now, everything is going to happen when the user hits cmdConnect,
so lets start coding its Event Sub ;

VB Code:
  1. Private Sub cmdConnect_Click()
  2.    
  3. End Sub

We want to have the winsock control (called sock) connect to some host&port.
The host and port are contained in txtHost and txtPort.
So we just grab their .Text properties ;

VB Code:
  1. Private Sub cmdConnect_Click()
  2.     sock.Connect txtHost.Text, txtPort.Text
  3. End Sub

Now as before we're going to need other Event Subs ;

VB Code:
  1. Private Sub sock_Connect()
  2.     lstInfo.AddItem "Connected to : " & sock.RemoteHost
  3. End Sub
  4.  
  5. Private Sub sock_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)
  6.     lstInfo.Additem Scode & ":" & Description
  7. End Sub
  8.  
  9. Private Sub sock_DataArrival(ByVal bytesTotal As Long)
  10.     Dim strBuff As String
  11.     sock.GetData strBuff, vbString
  12.     lstInfo.Additem strBuff
  13. End Sub

So, what does the above code do ?
Well, when we connect (Connect()), we're adding a line to a listbox saying
that we have connected to the RemoteHost.
If there's an error (Error()), we're adding it again to the listbox.
And when Data Arrives (DataArrival()), we're putting it in the listbox.

So we've given the user the choice to connect to any arbitrary host
on any port.
Lets also give them the ability to disconnect from that host.
Add to your form a command button called cmdDisconnect.
We can use the .Close() method of a Winsock Control to close the connection
if one has been established, so code the cmdDisconnect Click Sub as follows ;

VB Code:
  1. Private Sub cmdDisconnect_Click()
  2.     sock.Close
  3.     lstInfo.AddItem "Disconnected !"
  4. End Sub


In our above code samples, we're assuming the user is going to enter a host
and a port that is okay (ie. host : port = mail!!.?.#tcd.ie : -7)
It is generally not a good idea to completely trust the user when giving
you input, because the user does have a tendency to enter wrong information.

So how can we check the information they entered into our textboxes ?
Well there are two ways I would think of doing it.
We could make sure that they only enter numbers, periods and characters into
the textbox by checking the contents of the textbox in its Change() Sub.

Or, we could do the checking in one go in the cmdConnect() Sub.
If we're doing the checking in the cmdConnect(), there is a major advantage.
Though it may be a little advanced or this tutorial, there is a Windows API that
will tell you if a hostname as supplied is valid or not.
So why not use that to tell us ?

So as to not confuse those of you not used to dealing with API calls, or networking
related code, I have wrapped everything up into one simple Function call.
Now, there are a few ugly looking declarations.
Those are everything before "Private Function ...", and they go at the top
of your Form's code just below "Option Explicit" (if its written up there).
Then the Function itself can go anywhere in your form's code

VB Code:
  1. Option Explicit
  2.  
  3. 'Declarations
  4. Private Declare Function gethostbyname Lib "WSOCK32.DLL" (ByVal szHost As String) As Long
  5. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, ByVal hpvSource As Long, ByVal cbCopy As Long)
  6. Private Declare Function WSAStartup Lib "WSOCK32.DLL" (ByVal VersionReq As Long, WSADataReturn As WSADATA) As Long
  7. Private Declare Function inet_addr Lib "WSOCK32.DLL" (ByVal s As String) As Long
  8. Private Type WSADATA
  9.    wVersion As Integer
  10.    wHighVersion As Integer
  11.    szDescription(0 To 256) As Byte
  12.    szSystemStatus(0 To 128) As Byte
  13.    wMaxSockets As Long
  14.    wMaxUDPDG As Long
  15.    dwVendorInfo As Long
  16. End Type
  17.  
  18. 'Function itself
  19. Private Function isHostNameValid(ByVal strHost As String) As Boolean
  20.     Dim WSAD As WSADATA, ipFromHostname As String
  21.     Dim nbytes As Long, ptrHosent As Long, ptrName As Long
  22.     Dim ptrAddress As Long, ptrIPAddress As Long, sAddress As String
  23.     If WSAStartup(&H101, WSAD) = 0 Then
  24.         sAddress = Space$(4)
  25.         ptrHosent = gethostbyname(strHost & vbNullChar)
  26.         If ptrHosent <> 0 Then
  27.             ptrName = ptrHosent
  28.             ptrAddress = ptrHosent + 12
  29.             CopyMemory ptrName, ByVal ptrName, 4
  30.             CopyMemory ptrAddress, ByVal ptrAddress, 4
  31.             CopyMemory ptrIPAddress, ByVal ptrAddress, 4
  32.             CopyMemory ByVal sAddress, ByVal ptrIPAddress, 4
  33.             ipFromHostname = CStr(Asc(sAddress)) & _
  34.                                 "." & CStr(Asc(Mid$(sAddress, 2, 1))) & _
  35.                                 "." & CStr(Asc(Mid$(sAddress, 3, 1))) & _
  36.                                 "." & CStr(Asc(Mid$(sAddress, 4, 1)))
  37.             If (inet_addr(ipFromHostname) <> &HFFFFFFFF) Then
  38.                 isHostNameValid = True
  39.             End If
  40.         End If
  41.     End If
  42. End Function

So how do we use the function ?
Well below is some error checking code that you can see in action ;

VB Code:
  1. Private Sub cmdConnect_Click()
  2.     If (CLng(txtPort.Text) > 0) And (CLng(txtPort.Text) < 65535) Then
  3.         ' so the port number is ok.
  4.         ' but what about the host they've entered ?        
  5.         If isHostNameValid(txtHost.Text) Then
  6.             ' The HostName in txtHost.Text is ok!
  7.             ' So lets connect then        
  8.             sock.Connect txtHost.Text, txtPort.Text        
  9.         Else            
  10.             MsgBox "Invalid hostname !", vbCritical        
  11.         End If    
  12.     Else        
  13.         MsgBox "Port should be in the rang 0 - 65535", vbCritical        
  14.     End If
  15. End Sub

So we've now just written an application that will allow a user to connect
to any given host on any given port, and we've added in code to make sure
that the HostName and Port Number are ok.

In further tutorials, I will not bother adding in the code to check if the
host and port are ok.
You should do that yourself in all of your applications that utilize winsock,
but for these tutorials I'll just cut to the chase as it were.

Jamie Plenderleith
[email protected]
09 / 02 / 02