home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 5_2007-2008.ISO / data / Zips / A_Complete2049172212007.psc / CSocket.cls < prev   
Text File  |  2006-11-22  |  21KB  |  540 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "CSocket"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. '********************************************************************************
  15. 'CSocket class
  16. 'Copyright ⌐ 2002 by Oleg Gdalevich
  17. 'Visual Basic Internet Programming website (http://www.vbip.com)
  18. '********************************************************************************
  19. 'To use this class module you need:
  20. '   MSocketSupport code module
  21. '********************************************************************************
  22. 'Version: 1.0.12     Modified: 17-OCT-2002
  23. '********************************************************************************
  24. 'To get latest version of this code please visit the following web page:
  25. 'http://www.vbip.com/winsock-api/csocket-class/csocket-class-01.asp
  26. '********************************************************************************
  27. Option Explicit
  28. '
  29. 'Added: 23-AUG-2002
  30. Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lparam As Long) As Long
  31. '
  32. 'The CSocket protocol's constants as for
  33. 'the MS Winsock Control interface
  34. Public Enum ProtocolConstants
  35.     sckTCPProtocol = 0
  36.     sckUDPProtocol = 1
  37. End Enum
  38. '
  39. 'The CSocket error's constants as for
  40. 'the MS Winsock Control interface
  41. Public Enum ErrorConstants
  42.     sckAddressInUse = 10048
  43.     sckAddressNotAvailable = 10049
  44.     sckAlreadyComplete = 10037
  45.     sckAlreadyConnected = 10056
  46.     sckBadState = 40006
  47.     sckConnectAborted = 10053
  48.     sckConnectionRefused = 10061
  49.     sckConnectionReset = 10054
  50.     sckGetNotSupported = 394
  51.     sckHostNotFound = 11001
  52.     sckHostNotFoundTryAgain = 11002
  53.     sckInProgress = 10036
  54.     sckInvalidArg = 40014
  55.     sckInvalidArgument = 10014
  56.     sckInvalidOp = 40020
  57.     sckInvalidPropertyValue = 380
  58.     sckMsgTooBig = 10040
  59.     sckNetReset = 10052
  60.     sckNetworkSubsystemFailed = 10050
  61.     sckNetworkUnreachable = 10051
  62.     sckNoBufferSpace = 10055
  63.     sckNoData = 11004
  64.     sckNonRecoverableError = 11003
  65.     sckNotConnected = 10057
  66.     sckNotInitialized = 10093
  67.     sckNotSocket = 10038
  68.     sckOpCanceled = 10004
  69.     sckOutOfMemory = 7
  70.     sckOutOfRange = 40021
  71.     sckPortNotSupported = 10043
  72.     sckSetNotSupported = 383
  73.     sckSocketShutdown = 10058
  74.     sckSuccess = 40017
  75.     sckTimedout = 10060
  76.     sckUnsupported = 40018
  77.     sckWouldBlock = 10035
  78.     sckWrongProtocol = 40026
  79. End Enum
  80. '
  81. 'The CSocket state's constants as for
  82. 'the MS Winsock Control interface
  83. Public Enum StateConstants
  84.     sckClosed = 0
  85.     sckOpen
  86.     sckListening
  87.     sckConnectionPending
  88.     sckResolvingHost
  89.     sckHostResolved
  90.     sckConnecting
  91.     sckConnected
  92.     sckClosing
  93.     sckError
  94. End Enum
  95. '
  96. 'In order to resolve a host name the MSocketSupport.ResolveHost
  97. 'function can be called from the Connect and SendData methods
  98. 'of this class. The callback acceptor for that routine is the
  99. 'PostGetHostEvent procedure. This procedure determines what to
  100. 'do next with the received host's address checking a value of
  101. 'the m_varInternalState variable.
  102. Private Enum InternalStateConstants
  103.     istConnecting
  104.     istSendingDatagram
  105. End Enum
  106. '
  107. Private m_varInternalState As InternalStateConstants
  108. '
  109. 'Local (module level) variables to hold values of the
  110. 'properties of this (CSocket) class.
  111. Private mvarProtocol        As ProtocolConstants
  112. Private mvarState           As StateConstants
  113. Private m_lngBytesReceived  As Long
  114. Private m_strLocalHostName  As String
  115. Private m_strLocalIP        As String
  116. Private m_lngLocalPort      As Long
  117. Private m_strRemoteHost     As String
  118. Private m_strRemoteHostIP   As String
  119. Private m_lngRemotePort     As Long
  120. Private m_lngSocketHandle   As Long
  121. '
  122. 'Resolving host names is performed in an asynchronous mode,
  123. 'the m_lngRequestID variable just holds the value returned
  124. 'by the ResolveHost function from the MSocketSupport module.
  125. Private m_lngRequestID      As Long
  126. '
  127. 'Internal (for this class) buffers. They are the VB Strings.
  128. 'Don't trust that guy who told that the VB String data type
  129. 'cannot properly deal with binary data. Actually, it can, and
  130. 'moreover you have a lot of means to deal with that data -
  131. 'the VB string functions (such as Left, Mid, InStr and so on).
  132. 'If you need to get a byte array from a string, just call the
  133. 'StrConv function:
  134. '
  135. 'byteArray() = StrConv(strBuffer, vbFromUnicode)
  136. '
  137. Private m_strSendBuffer     As String 'The internal buffer for outgoing data
  138. Private m_strRecvBuffer     As String 'The internal buffer for incoming data
  139. '
  140. 'Lenght of the Winsock buffers. By default = 8192 bytes for TCP sockets.
  141. 'These values are initialized in the SocketExists function.
  142. 'Now, I really don't know why I was in need to get these values.
  143. Private m_lngSendBufferLen  As Long
  144. Private m_lngRecvBufferLen  As Long
  145. '
  146. 'Maximum size of a datagram that can be sent through
  147. 'a message-oriented (UDP) socket. This value is returned
  148. 'by the InitWinsock function from the MSocketSupport module.
  149. Private m_lngMaxMsgSize     As Long
  150. '
  151. 'This flag variable indicates that the socket is bound to
  152. 'some local socket address
  153. Private m_blnSocketIsBound  As Boolean  'Added: 10-MAR-2002
  154. '
  155. Private m_blnSendFlag As Boolean        'Added: 12-SEP-2002
  156. '
  157. 'This flag variable indicates that the SO_BROADCAST option
  158. 'is set on the socket
  159. Private m_blnBroadcast      As Boolean  'Added: 09-JULY-2002
  160. '
  161. 'These are those MS Winsock's events.
  162. 'Pay attention that the "On" prefix is added.
  163. Public Event OnClose()
  164. Attribute OnClose.VB_Description = "Occurs when the connection has been closed"
  165. Public Event OnConnect()
  166. Attribute OnConnect.VB_Description = "Occurs connect operation is completed"
  167. Public Event OnConnectionRequest(ByVal requestID As Long)
  168. Public Event OnDataArrival(ByVal bytesTotal As Long)
  169. Public Event OnError(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)
  170. Public Event OnSendComplete()
  171. Public Event OnSendProgress(ByVal bytesSent As Long, ByVal bytesRemaining As Long)
  172.  
  173. Public Sub SendData(varData As Variant)
  174. Attribute SendData.VB_Description = "Send data to remote computer"
  175.     '
  176.     'data to send - will be built from the varData argument
  177.     Dim arrData()       As Byte
  178.     'value returned by the send(sendto) Winsock API function
  179.     Dim lngRetValue     As Long
  180.     'length of the data to send - needed to call the send(sendto) Winsock API function
  181.     Dim lngBufferLength As Long
  182.     'this strucure just contains address of the remote socket to send data to;
  183.     'only for UDP sockets when the sendto Winsock API function is used
  184.     Dim udtSockAddr     As sockaddr_in
  185.     '
  186.     On Error Resume Next
  187.     '
  188.     'If a connection-oriented (TCP) socket was not created or connected to the
  189.     'remote host before calling the SendData method, the MS Winsock Control
  190.     'raises the sckBadState error.
  191.     If mvarProtocol = sckTCPProtocol Then
  192.         '
  193.         If m_lngSocketHandle = INVALID_SOCKET Then Exit Sub
  194.         '
  195.     Else
  196.         '
  197.         'If the socket is a message-oriented one (UDP), this is OK to create
  198.         'it with the call of the SendData method. The SocketExists function
  199.         'creates a new socket.
  200.         If Not SocketExists Then Exit Sub
  201.         '
  202.     End If
  203.     '
  204.     Select Case varType(varData)
  205.         Case vbArray + vbByte
  206.             'Modified 28-MAY-2002. Thanks to Michael Freidgeim
  207.             '--------------------------------
  208.             'Dim strArray As String
  209.             'strArray = CStr(varData)
  210.             arrData() = varData
  211.             '--------------------------------
  212.         Case vbBoolean
  213.             Dim blnData As Boolean
  214.             blnData = CBool(varData)
  215.             ReDim arrData(LenB(blnData) - 1)
  216.             CopyMemory arrData(0), blnData, LenB(blnData)
  217.         Case vbByte
  218.             Dim bytData As Byte
  219.             bytData = CByte(varData)
  220.             ReDim arrData(LenB(bytData) - 1)
  221.             CopyMemory arrData(0), bytData, LenB(bytData)
  222.         Case vbCurrency
  223.             Dim curData As Currency
  224.             curData = CCur(varData)
  225.             ReDim arrData(LenB(curData) - 1)
  226.             CopyMemory arrData(0), curData, LenB(curData)
  227.         Case vbDate
  228.             Dim datData As Date
  229.             datData = CDate(varData)
  230.             ReDim arrData(LenB(datData) - 1)
  231.             CopyMemory arrData(0), datData, LenB(datData)
  232.         Case vbDouble
  233.             Dim dblData As Double
  234.             dblData = CDbl(varData)
  235.             ReDim arrData(LenB(dblData) - 1)
  236.             CopyMemory arrData(0), dblData, LenB(dblData)
  237.         Case vbInteger
  238.             Dim intData As Integer
  239.             intData = CInt(varData)
  240.             ReDim arrData(LenB(intData) - 1)
  241.             CopyMemory arrData(0), intData, LenB(intData)
  242.         Case vbLong
  243.             Dim lngData As Long
  244.             lngData = CLng(varData)
  245.             ReDim arrData(LenB(lngData) - 1)
  246.             CopyMemory arrData(0), lngData, LenB(lngData)
  247.         Case vbSingle
  248.             Dim sngData As Single
  249.             sngData = CSng(varData)
  250.             ReDim arrData(LenB(sngData) - 1)
  251.             CopyMemory arrData(0), sngData, LenB(sngData)
  252.         Case vbString
  253.             Dim strData As String
  254.             strData = CStr(varData)
  255.             ReDim arrData(Len(strData) - 1)
  256.             arrData() = StrConv(strData, vbFromUnicode)
  257.         Case Else
  258.             '
  259.             'Unknown data type
  260.             '
  261.     End Select
  262.     '
  263.     'Store all the data to send in the module level
  264.     'variable m_strSendBuffer.
  265.     m_strSendBuffer = StrConv(arrData(), vbUnicode)
  266.     '
  267.     'Call the SendBufferedData subroutine in order to send the data.
  268.     'The SendBufferedData sub is just a common procedure that is
  269.     'called from different places in this class.
  270.     'Nothing special - just the code reuse.
  271.     m_blnSendFlag = True
  272.     Call SendBufferedData
  273.     '
  274. End Sub
  275.  
  276. Public Sub PeekData(varData As Variant, Optional varType As Variant, Optional maxLen As Variant)
  277. Attribute PeekData.VB_Description = "Look at incoming data without removing it from the buffer"
  278.     '
  279.     Dim lngBytesReceived As Long    'value returned by the RecvData function
  280.     '
  281.     On Error Resume Next
  282.     '
  283.     'The RecvData is a universal subroutine that can either to retrieve or peek
  284.     'data from the Winsock buffer. If a value of the second argument (blnPeek As Boolean)
  285.     'of the RecvData subroutine is True, it will be just peeking.
  286.     lngBytesReceived = RecvData(varData, True, IIf(IsMissing(varType), Empty, varType), _
  287.                                 IIf(IsMissing(maxLen), Empty, maxLen))
  288.     '
  289. End Sub
  290.  
  291. Public Sub Listen()
  292. Attribute Listen.VB_Description = "Listen for incoming connection requests"
  293.     '
  294.     Dim lngRetValue As Long 'value returned by the listen Winsock API function
  295.     '
  296.     On Error Resume Next
  297.     '
  298.     'SocketExists is not a variable. It is a function that can
  299.     'create a socket, if the class has no one.
  300.     If Not SocketExists Then Exit Sub
  301.     '
  302.     'The listen Winsock API function cannot be called
  303.     'without the call of the bind one.
  304.     If Not m_blnSocketIsBound Then  'Added: 10-MAR-2002
  305.         Call Bind
  306.     End If                          'Added: 10-MAR-2002
  307.     '
  308.     'Turn the socket into a listening state
  309.     lngRetValue = api_listen(m_lngSocketHandle, 5&)
  310.     '
  311.     If lngRetValue = SOCKET_ERROR Then
  312.         mvarState = sckError
  313.     Else
  314.         mvarState = sckListening
  315.     End If
  316. End Sub
  317.  
  318. Public Sub GetData(varData As Variant, Optional varType As Variant, Optional maxLen As Variant)
  319. Attribute GetData.VB_Description = "Retrieve data sent by the remote computer"
  320.     '
  321.     Dim lngBytesReceived As Long    'value returned by the RecvData function
  322.     '
  323.     On Error Resume Next
  324.     '
  325.     'A value of the second argument of the RecvData subroutine is False, so in this way
  326.     'this procedure will retrieve incoming data from the buffer.
  327.     lngBytesReceived = RecvData(varData, False, IIf(IsMissing(varType), Empty, varType), _
  328.                                 IIf(IsMissing(maxLen), Empty, maxLen))
  329.     '
  330. End Sub
  331.  
  332.  
  333. Public Sub Connect(Optional strRemoteHost As Variant, Optional lngRemotePort As Variant)
  334. Attribute Connect.VB_Description = "Connect to the remote computer"
  335.     '
  336.     Dim lngHostAddress  As Long         '32 bit host address
  337.     Dim udtAddress      As sockaddr_in  'socket address - used by the connect Winsock API function
  338.     Dim lngRetValue     As Long         'value returned by the connect Winsock API function
  339.     '
  340.     On Error Resume Next
  341.     '
  342.     'If no socket has been created before, try to create a new one
  343.     If Not SocketExists Then Exit Sub
  344.     '
  345.     'If the arguments of this function are not missing, they
  346.     'overwrite values of the RemoteHost and RemotePort properties.
  347.     '
  348.     If Not IsMissing(strRemoteHost) Then    'Added: 04-MAR-2002
  349.         If Len(strRemoteHost) > 0 Then
  350.             m_strRemoteHost = CStr(strRemoteHost)
  351.         End If
  352.     End If                                  'Added: 04-MAR-2002
  353.     '
  354.     If Not IsMissing(lngRemotePort) Then    'Added: 04-MAR-2002
  355.         If IsNumeric(lngRemotePort) Then    'Added: 04-MAR-2002
  356.             m_lngRemotePort = CLng(lngRemotePort)
  357.         End If                              'Added: 04-MAR-2002
  358.     End If                                  'Added: 04-MAR-2002
  359.     '
  360.     '----------------------------------------------------------
  361.     'Added: 31-JUL-2002
  362.     '----------------------------------------------------------
  363.     If Len(m_strRemoteHost) = 0 Then Exit Sub
  364.     '----------------------------------------------------------
  365.     '
  366.     m_varInternalState = istConnecting
  367.     '
  368.     '------------------------------------------------------------------
  369.     'Modified: 08-JULY-2002
  370.     '------------------------------------------------------------------
  371.     'Here is a major change. Since version 1.0.6 (08-JULY-2002) the
  372.     'SCocket class doesn't try to resolve the IP address into a
  373.     'domain name while connecting.
  374.     '------------------------------------------------------------------
  375.     '
  376.     'Try to get 32 bit host address from the RemoteHost property value
  377.     lngHostAddress = inet_addr(m_strRemoteHost)
  378.     '
  379.     If lngHostAddress = INADDR_NONE Then
  380.         '
  381.         'The RemoteHost property doesn't contain a valid IP address string,
  382.         'so that is perhaps a domain name string that we need to resolve
  383.         'into IP address
  384.         '
  385.         'The ResolveHost function, that can be found in the MSocketSupport
  386.         'module, will call the WSAAsyncGetHostByName Winsock API function.
  387.         'That function is an asynchronous one, so code in this class will be executing
  388.         'after the call to the PostGetHostEvent procedure from the WindowProc function
  389.         'in the MSupportSocket.
  390.         '
  391.         'Also, as you can see, the second argument is a pointer to the object, that is
  392.         'this instance of the CSocket class. We need this because the callback function
  393.         'has to know to which object send the received host infromation. See the code
  394.         'in the MSocketSupport module for more information.
  395.         '
  396.         'Change the State property value
  397.         mvarState = sckResolvingHost
  398.         'Debug.Print "mvarState = sckResolvingHost"
  399.         '
  400.         m_lngRequestID = 0
  401.         m_lngRequestID = MSocketSupport.ResolveHost(m_strRemoteHost, ObjPtr(Me))
  402.         '
  403.         '-------------------------------------------------------
  404.         'Added: 04-JUNE-2002
  405.         '-------------------------------------------------------
  406.         If m_lngRequestID = 0 Then Call DestroySocket
  407.         '-------------------------------------------------------
  408.         '
  409.     Else
  410.         '
  411.         'The RemoteHost property contains a valid IP address string,
  412.         'so we can go on connecting to the remote host.
  413.         '
  414.         'Build the sockaddr_in structure to pass it to the connect
  415.         'Winsock API function as an address of the remote host.
  416.         With udtAddress
  417.             '
  418.             .sin_addr = lngHostAddress
  419.             .sin_family = AF_INET
  420.             .sin_port = htons(UnsignedToInteger(CLng(m_lngRemotePort)))
  421.             '
  422.         End With
  423.         '
  424.         'Call the connect Winsock API function in order to establish connection.
  425.         lngRetValue = api_connect(m_lngSocketHandle, udtAddress, Len(udtAddress))
  426.         '
  427.         'Since the socket we use is a non-blocking one, the connect Winsock API
  428.         'function should return a value of SOCKET_ERROR anyway.
  429.         '
  430.         If lngRetValue = SOCKET_ERROR Then
  431.             '
  432.             'The WSAEWOULDBLOCK error is OK for such a socket
  433.             '
  434.             If Not Err.LastDllError = WSAEWOULDBLOCK Then
  435.                 '
  436.             Else
  437.                 'Change the State property value
  438.                 mvarState = sckConnecting
  439.             End If
  440.             '
  441.         End If
  442.         '
  443.     End If
  444. End Sub
  445.  
  446. Public Sub CloseSocket()
  447. Attribute CloseSocket.VB_Description = "Close current connection"
  448.     '
  449.     Dim lngRetValue As Long 'value returned by the shutdown Winsock API function
  450.     '
  451.     On Error Resume Next
  452.     '
  453.     'Why do we need to run the code that should not be running?
  454.     If m_lngSocketHandle = INVALID_SOCKET Then Exit Sub
  455.     '
  456.     If Not mvarState = sckConnected Then
  457.         '
  458.         'If the socket is not connected we can just close it
  459.         Call DestroySocket
  460.         mvarState = sckClosed
  461.         '
  462.     Else
  463.         '
  464.         'If the socket is connected, it's another story.
  465.         'In order to be sure that no data will be lost the
  466.         'graceful shutdown of the socket should be performed.
  467.         '
  468.         mvarState = sckClosing
  469.         '
  470.         'Call the shutdown Winsock API function in order to
  471.         'close the connection. That doesn't mean that the
  472.         'connection will be closed after the call of the
  473.         'shutdown function. Connection will be closed from
  474.         'the PostSocketEvent subroutine when the FD_CLOSE
  475.         'message will be received.
  476.         '
  477.         'For people who know what the FIN segment in the
  478.         'TCP header is - this function sends an empty packet
  479.         'with the FIN bit turned on.
  480.         '
  481.         lngRetValue = shutdown(m_lngSocketHandle, SD_SEND)
  482.         '
  483.     End If
  484. End Sub
  485.  
  486. Public Sub Bind(Optional lngLocalPort As Long, Optional strLocalIP As String)
  487. Attribute Bind.VB_Description = "Binds socket to specific port and adapter"
  488.     '
  489.     Dim lngRetValue     As Long         'value returned by the bind Winsock API function
  490.     Dim udtLocalAddr    As sockaddr_in  'local socket address to bind to - used by the
  491.     '                                    bind Winsock API function
  492.     Dim lngAddress      As Long         '32-bit host address - value returned by
  493.     '                                    the inet_addr Winsock API function
  494.     '
  495.     On Error Resume Next
  496.     '
  497.     'If no socket has been created before, try to create a new one
  498.     If Not SocketExists Then Exit Sub
  499.     '
  500.     'If the arguments of this function are not missing, they
  501.     'overwrites values of the RemoteHost and RemotePort properties.
  502.     '
  503.     If Len(strLocalIP) > 0 Then
  504.         m_strLocalIP = strLocalIP
  505.     End If
  506.     '
  507.     If lngLocalPort > 0 Then
  508.         m_lngLocalPort = lngLocalPort
  509.     End If
  510.     '
  511.     If Len(m_strLocalIP) > 0 Then
  512.         '
  513.         'If the local IP is known, get the address
  514.         'from it with the inet_addr Winsock API function.
  515.         lngAddress = inet_addr(m_strLocalIP)
  516.         '
  517.     Else
  518.         '
  519.         'If the IP is unknown, assign the default interface's IP.
  520.         'Actually, this line is useless in Visual Basic code,
  521.         'as INADDR_ANY = 0 (IP = 0.0.0.0).
  522.         lngAddress = INADDR_ANY
  523.         '
  524.     End If
  525.     '
  526.     If lngAddress = SOCKET_ERROR Then
  527.         '
  528.         'Bad address - go away
  529.         Exit Sub
  530.         '
  531.     End If
  532.     '
  533.     'Prepare the udtLocalAddr UDT that is a socket address structure.
  534.     With udtLocalAddr
  535.         '
  536.         'host address (32-bits value)
  537.         .sin_addr = lngAddress
  538.         'address family
  539.         .sin_family = AF_INET
  540.         'port number in the network