CSocket.cls
上传用户:bcdefg2008
上传日期:2013-02-28
资源大小:144k
文件大小:56k
源码类别:

Email服务器

开发平台:

Visual Basic

  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. Option Explicit
  15. '
  16. 'The CSocket protocol's constants as for
  17. 'the MS Winsock Control interface
  18. Public Enum ProtocolConstants
  19.     sckTCPProtocol = 0
  20.     sckUDPProtocol = 1
  21. End Enum
  22. '
  23. 'The CSocket error's constants as for
  24. 'the MS Winsock Control interface
  25. Public Enum ErrorConstants
  26.     sckAddressInUse = 10048
  27.     sckAddressNotAvailable = 10049
  28.     sckAlreadyComplete = 10037
  29.     sckAlreadyConnected = 10056
  30.     sckBadState = 40006
  31.     sckConnectAborted = 10053
  32.     sckConnectionRefused = 10061
  33.     sckConnectionReset = 10054
  34.     sckGetNotSupported = 394
  35.     sckHostNotFound = 11001
  36.     sckHostNotFoundTryAgain = 11002
  37.     sckInProgress = 10036
  38.     sckInvalidArg = 40014
  39.     sckInvalidArgument = 10014
  40.     sckInvalidOp = 40020
  41.     sckInvalidPropertyValue = 380
  42.     sckMsgTooBig = 10040
  43.     sckNetReset = 10052
  44.     sckNetworkSubsystemFailed = 10050
  45.     sckNetworkUnreachable = 10051
  46.     sckNoBufferSpace = 10055
  47.     sckNoData = 11004
  48.     sckNonRecoverableError = 11003
  49.     sckNotConnected = 10057
  50.     sckNotInitialized = 10093
  51.     sckNotSocket = 10038
  52.     sckOpCanceled = 10004
  53.     sckOutOfMemory = 7
  54.     sckOutOfRange = 40021
  55.     sckPortNotSupported = 10043
  56.     sckSetNotSupported = 383
  57.     sckSocketShutdown = 10058
  58.     sckSuccess = 40017
  59.     sckTimedout = 10060
  60.     sckUnsupported = 40018
  61.     sckWouldBlock = 10035
  62.     sckWrongProtocol = 40026
  63. End Enum
  64. '
  65. 'The CSocket state's constants as for
  66. 'the MS Winsock Control interface
  67. Public Enum StateConstants
  68.     sckClosed = 0
  69.     sckOpen
  70.     sckListening
  71.     sckConnectionPending
  72.     sckResolvingHost
  73.     sckHostResolved
  74.     sckConnecting
  75.     sckConnected
  76.     sckClosing
  77.     sckError
  78. End Enum
  79. '
  80. 'In order to resolve a host name the MSocketSupport.ResolveHost
  81. 'function can be called from the Connect and SendData methods
  82. 'of this class. The callback acceptor for that routine is the
  83. 'PostGetHostEvent procedure. This procedure determines what to
  84. 'do next with the received host's address checking a value of
  85. 'the m_varInternalState variable.
  86. Private Enum InternalStateConstants
  87.     istConnecting
  88.     istSendingDatagram
  89. End Enum
  90. '
  91. Private m_varInternalState As InternalStateConstants
  92. '
  93. 'Local (module level) variables to hold values of the
  94. 'properties of this (CSocket) class.
  95. Private mvarProtocol        As ProtocolConstants
  96. Private mvarState           As StateConstants
  97. Private m_lngBytesReceived  As Long
  98. Private m_strLocalHostName  As String
  99. Private m_strLocalIP        As String
  100. Private m_lngLocalPort      As Long
  101. Private m_strRemoteHost     As String
  102. Private m_strRemoteHostIP   As String
  103. Private m_lngRemotePort     As Long
  104. Private m_lngSocketHandle   As Long
  105. '
  106. 'Resolving host names is performed in an asynchronous mode,
  107. 'the m_lngRequestID variable just holds the value returned
  108. 'by the ResolveHost function from the MSocketSupport module.
  109. Private m_lngRequestID      As Long
  110. '
  111. 'Internal (for this class) buffers. They are the VB Strings.
  112. 'Don't trust that guy who told that the VB String data type
  113. 'cannot properly deal with binary data. Actually, it can, and
  114. 'moreover you have a lot of means to deal with that data -
  115. 'the VB string functions (such as Left, Mid, InStr and so on).
  116. 'If you need to get a byte array from a string, just call the
  117. 'StrConv function:
  118. '
  119. 'byteArray() = StrConv(strBuffer, vbFromUnicode)
  120. '
  121. Private m_strSendBuffer     As String 'The internal buffer for outgoing data
  122. Private m_strRecvBuffer     As String 'The internal buffer for incoming data
  123. '
  124. 'Lenght of the Winsock buffers. By default = 8192 bytes for TCP sockets.
  125. 'These values are initialized in the SocketExists function.
  126. 'Now, I really don't know why I was in need to get these values.
  127. Private m_lngSendBufferLen  As Long
  128. Private m_lngRecvBufferLen  As Long
  129. '
  130. 'Maximum size of a datagram that can be sent through
  131. 'a message-oriented (UDP) socket. This value is returned
  132. 'by the InitWinsock function from the MSocketSupport module.
  133. Private m_lngMaxMsgSize     As Long
  134. '
  135. 'This flag variable indicates that the socket is bound to
  136. 'some local socket address
  137. Private m_blnSocketIsBound  As Boolean  'Added: 10-MAR-2002
  138. '
  139. 'These are those MS Winsock's events.
  140. 'Pay attention that the "On" prefix is added.
  141. Public Event OnClose()
  142. Attribute OnClose.VB_Description = "Occurs when the connection has been closed"
  143. Public Event OnConnect()
  144. Attribute OnConnect.VB_Description = "Occurs connect operation is completed"
  145. Public Event OnConnectionRequest(ByVal requestID As Long)
  146. Public Event OnDataArrival(ByVal bytesTotal As Long)
  147. 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)
  148. Public Event OnSendComplete()
  149. Public Event OnSendProgress(ByVal bytesSent As Long, ByVal bytesRemaining As Long)
  150. Public Sub SendData(varData As Variant)
  151. Attribute SendData.VB_Description = "Send data to remote computer"
  152.   '
  153.   'data to send - will be built from the varData argument
  154.   Dim arrData()       As Byte
  155.     'value returned by the send(sendto) Winsock API function
  156.   Dim lngRetValue     As Long ':(燤ove line to top of current Sub
  157.     'length of the data to send - needed to call the send(sendto) Winsock API function
  158.   Dim lngBufferLength As Long ':(燤ove line to top of current Sub
  159.     'this strucure just contains address of the remote socket to send data to;
  160.     'only for UDP sockets when the sendto Winsock API function is used
  161.   Dim udtSockAddr     As sockaddr_in ':(燤ove line to top of current Sub
  162.     '
  163.     On Error GoTo SendData_Err_Handler
  164.     '
  165.     'If a connection-oriented (TCP) socket was not created or connected to the
  166.     'remote host before calling the SendData method, the MS Winsock Control
  167.     'raises the sckBadState error.
  168.     If mvarProtocol = sckTCPProtocol Then
  169.         '
  170.         If m_lngSocketHandle = INVALID_SOCKET Then
  171.             Err.Raise sckBadState, "CSocket.SendData", _
  172.                       "Wrong protocol or connection state for the requested transaction or request."
  173.             Exit Sub '>---> Bottom
  174.         End If
  175.         '
  176.       Else 'NOT MVARPROTOCOL...
  177.         '
  178.         'If the socket is a message-oriented one (UDP), this is OK to create
  179.         'it with the call of the SendData method. The SocketExists function
  180.         'creates a new socket.
  181.         If Not SocketExists Then Exit Sub ':(燛xpand Structure or consider reversing Condition
  182.         '
  183.     End If
  184.     '
  185.     Select Case varType(varData)
  186.       Case vbArray + vbByte
  187.         'Modified 28-MAY-2002. Thanks to Michael Freidgeim
  188.         '--------------------------------
  189.         'Dim strArray As String
  190.         'strArray = CStr(varData)
  191.         arrData() = varData
  192.         '--------------------------------
  193.       Case vbBoolean
  194.   Dim blnData As Boolean ':(燤ove line to top of current Sub
  195.         blnData = CBool(varData)
  196.         ReDim arrData(LenB(blnData) - 1)
  197.         CopyMemory arrData(0), blnData, LenB(blnData)
  198.       Case vbByte
  199.   Dim bytData As Byte ':(燤ove line to top of current Sub
  200.         bytData = CByte(varData)
  201.         ReDim arrData(LenB(bytData) - 1)
  202.         CopyMemory arrData(0), bytData, LenB(bytData)
  203.       Case vbCurrency
  204.   Dim curData As Currency ':(燤ove line to top of current Sub
  205.         curData = CCur(varData)
  206.         ReDim arrData(LenB(curData) - 1)
  207.         CopyMemory arrData(0), curData, LenB(curData)
  208.       Case vbDate
  209.   Dim datData As Date ':(燤ove line to top of current Sub
  210.         datData = CDate(varData)
  211.         ReDim arrData(LenB(datData) - 1)
  212.         CopyMemory arrData(0), datData, LenB(datData)
  213.       Case vbDouble
  214.   Dim dblData As Double ':(燤ove line to top of current Sub
  215.         dblData = CDbl(varData)
  216.         ReDim arrData(LenB(dblData) - 1)
  217.         CopyMemory arrData(0), dblData, LenB(dblData)
  218.       Case vbInteger
  219.   Dim intData As Integer ':(燤ove line to top of current Sub
  220.         intData = CInt(varData)
  221.         ReDim arrData(LenB(intData) - 1)
  222.         CopyMemory arrData(0), intData, LenB(intData)
  223.       Case vbLong
  224.   Dim lngData As Long ':(燤ove line to top of current Sub
  225.         lngData = CLng(varData)
  226.         ReDim arrData(LenB(lngData) - 1)
  227.         CopyMemory arrData(0), lngData, LenB(lngData)
  228.       Case vbSingle
  229.   Dim sngData As Single ':(燤ove line to top of current Sub
  230.         sngData = CSng(varData)
  231.         ReDim arrData(LenB(sngData) - 1)
  232.         CopyMemory arrData(0), sngData, LenB(sngData)
  233.       Case vbString
  234.   Dim strData As String ':(燤ove line to top of current Sub
  235.         strData = CStr(varData)
  236.         ReDim arrData(Len(strData) - 1)
  237.         arrData() = StrConv(strData, vbFromUnicode)
  238.       Case Else
  239.         '
  240.         'Unknown data type
  241.         '
  242.     End Select
  243.     '
  244.     'Store all the data to send in the module level
  245.     'variable m_strSendBuffer.
  246.     m_strSendBuffer = StrConv(arrData(), vbUnicode)
  247.     '
  248.     'Call the SendBufferedData subroutine in order to send the data.
  249.     'The SendBufferedData sub is just a common procedure that is
  250.     'called from different places in this class.
  251.     'Nothing special - just the code reuse.
  252.     Call SendBufferedData
  253.     '
  254. EXIT_LABEL:
  255.     '
  256. Exit Sub
  257.     '
  258. SendData_Err_Handler:
  259.     '
  260.     If Err.LastDllError = WSAENOTSOCK Then
  261.         Err.Raise sckBadState, "CSocket.SendData", "Wrong protocol or connection state for the requested transaction or request."
  262.       Else 'NOT ERR.LASTDLLERROR...
  263.         Err.Raise Err.Number, "CSocket.SendData", Err.Description
  264.     End If
  265.     '
  266.     GoTo EXIT_LABEL
  267.     '
  268. End Sub
  269. Public Sub PeekData(varData As Variant, Optional varType As Variant, Optional maxLen As Variant)
  270. Attribute PeekData.VB_Description = "Look at incoming data without removing it from the buffer"
  271.   '
  272.   Dim lngBytesReceived As Long    'value returned by the RecvData function
  273.     '
  274.     On Error GoTo PeekData_Err_Handler
  275.     '
  276.     'The RecvData is a universal subroutine that can either to retrieve or peek
  277.     'data from the Winsock buffer. If a value of the second argument (blnPeek As Boolean)
  278.     'of the RecvData subroutine is True, it will be just peeking.
  279.     lngBytesReceived = RecvData(varData, True, IIf(IsMissing(varType), Empty, varType), _
  280.                        IIf(IsMissing(maxLen), Empty, maxLen))
  281.     '
  282. EXIT_LABEL:
  283.     '
  284. Exit Sub
  285.     '
  286. PeekData_Err_Handler:
  287.     '
  288.     Err.Raise Err.Number, "CSocket.PeekData", Err.Description
  289.     '
  290.     GoTo EXIT_LABEL
  291.     '
  292. End Sub
  293. Public Sub Listen()
  294. Attribute Listen.VB_Description = "Listen for incoming connection requests"
  295.   '
  296.   Dim lngRetValue As Long 'value returned by the listen Winsock API function
  297.     '
  298.     On Error GoTo Listen_Err_Handler
  299.     '
  300.     'SocketExists is not a variable. It is a function that can
  301.     'create a socket, if the class has no one.
  302.     If Not SocketExists Then Exit Sub ':(燛xpand Structure or consider reversing Condition
  303.     '
  304.     'The listen Winsock API function cannot be called
  305.     'without the call of the bind one.
  306.     If Not m_blnSocketIsBound Then  'Added: 10-MAR-2002
  307.         Call Bind
  308.     End If                          'Added: 10-MAR-2002
  309.     '
  310.     'Turn the socket into a listening state
  311.     lngRetValue = api_listen(m_lngSocketHandle, 5&)
  312.     '
  313.     If lngRetValue = SOCKET_ERROR Then
  314.         mvarState = sckError
  315.         Err.Raise Err.LastDllError, "CSocket.Listen", GetErrorDescription(Err.LastDllError)
  316.       Else 'NOT LNGRETVALUE...
  317.         mvarState = sckListening
  318.     End If
  319.     '
  320. EXIT_LABEL:
  321.     '
  322. Exit Sub
  323.     '
  324. Listen_Err_Handler:
  325.     '
  326.     Err.Raise Err.Number, "CSocket.Listen", Err.Description
  327.     '
  328.     GoTo EXIT_LABEL
  329.     '
  330. End Sub
  331. Public Sub GetData(varData As Variant, Optional varType As Variant, Optional maxLen As Variant)
  332. Attribute GetData.VB_Description = "Retrieve data sent by the remote computer"
  333.   '
  334.   Dim lngBytesReceived As Long    'value returned by the RecvData function
  335.     '
  336.     On Error GoTo GetData_Err_Handler
  337.     '
  338.     'A value of the second argument of the RecvData subroutine is False, so in this way
  339.     'this procedure will retrieve incoming data from the buffer.
  340.     lngBytesReceived = RecvData(varData, False, IIf(IsMissing(varType), Empty, varType), _
  341.                        IIf(IsMissing(maxLen), Empty, maxLen))
  342.     '
  343. EXIT_LABEL:
  344.     '
  345. Exit Sub
  346.     '
  347. GetData_Err_Handler:
  348.     '
  349.     Err.Raise Err.Number, "CSocket.GetData", Err.Description
  350.     '
  351.     GoTo EXIT_LABEL
  352.     '
  353. End Sub
  354. Public Sub Connect(Optional strRemoteHost As Variant, Optional lngRemotePort As Variant)
  355. Attribute Connect.VB_Description = "Connect to the remote computer"
  356.   '
  357.     On Error GoTo Connect_Err_Handler
  358.     '
  359.     'If no socket has been created before, try to create a new one
  360.     If Not SocketExists Then Exit Sub ':(燛xpand Structure or consider reversing Condition
  361.     '
  362.     'If the arguments of this function are not missing, they
  363.     'overwrite values of the RemoteHost and RemotePort properties.
  364.     '
  365.     If Not IsMissing(strRemoteHost) Then    'Added: 04-MAR-2002
  366.         If Len(strRemoteHost) > 0 Then
  367.             m_strRemoteHost = CStr(strRemoteHost)
  368.         End If
  369.     End If                                  'Added: 04-MAR-2002
  370.     '
  371.     If Not IsMissing(lngRemotePort) Then    'Added: 04-MAR-2002
  372.         If IsNumeric(lngRemotePort) Then    'Added: 04-MAR-2002
  373.             m_lngRemotePort = CLng(lngRemotePort)
  374.         End If                              'Added: 04-MAR-2002
  375.     End If                                  'Added: 04-MAR-2002
  376.     '
  377.     mvarState = sckResolvingHost
  378.     '
  379.     'Maybe you expect to see the connect Winsock API function
  380.     'here, but instead the MScoketSupport.ResolveHost one is
  381.     'called. The connect function does its work in another place
  382.     'of this class - in the PostGetHostEvent procedure, since we
  383.     'need an address of the host in order to establish a connection.
  384.     '
  385.     'The ResolveHost function, that can be found in the MSocketSupport
  386.     'module, will call either the WSAAsyncGetHostByName or WSAAsyncGetHostByAddress
  387.     'depending on what is passed to it with the first argument. Anyway, those
  388.     'functions are asynchronous ones, so code in this class will be executing
  389.     'after the call to the PostGetHostEvent procedure from the WindowProc function
  390.     'in the MSupportSocket.
  391.     '
  392.     'Also, as you can see, the second argument is a pointer to the object, that is
  393.     'this instance of the CSocket class. We need this because the MSocketSupport
  394.     'module is supposed to serve several sockets, not a single one. So the
  395.     'MSocketSupport module should know which CSocket's instance to return info to.
  396.     '
  397.     m_lngRequestID = 0
  398.     m_varInternalState = istConnecting
  399.     m_lngRequestID = MSocketSupport.ResolveHost(m_strRemoteHost, ObjPtr(Me))
  400.     '
  401. EXIT_LABEL:
  402.     '
  403. Exit Sub
  404.     '
  405. Connect_Err_Handler:
  406.     '
  407.     Err.Raise Err.Number, "CSocket.CSocket.Connect", Err.Description
  408.     '
  409.     GoTo EXIT_LABEL
  410.     '
  411. End Sub
  412. Public Sub CloseSocket()
  413. Attribute CloseSocket.VB_Description = "Close current connection"
  414.   '
  415.   Dim lngRetValue As Long 'value returned by the shutdown Winsock API function
  416.     '
  417.     On Error GoTo Close_Err_Handler
  418.     '
  419.     'Why do we need to run the code that should not be running?
  420.     If m_lngSocketHandle = INVALID_SOCKET Then Exit Sub ':(燛xpand Structure or consider reversing Condition
  421.     '
  422.     If Not mvarState = sckConnected Then
  423.         '
  424.         'If the socket is not connected we can just close it
  425.         Call DestroySocket
  426.         mvarState = sckClosed
  427.         '
  428.       Else 'NOT NOT...
  429.         '
  430.         'If the socket is connected, it's another story.
  431.         'In order to be sure that no data will be lost the
  432.         'graceful shutdown of the socket should be performed.
  433.         '
  434.         mvarState = sckClosing
  435.         '
  436.         'Call the shutdown Winsock API function in order to
  437.         'close the connection. That doesn't mean that the
  438.         'connection will be closed after the call of the
  439.         'shutdown function. Connection will be closed from
  440.         'the PostSocketEvent subroutine when the FD_CLOSE
  441.         'message will be received.
  442.         '
  443.         'For people who know what the FIN segment in the
  444.         'TCP header is - this function sends an empty packet
  445.         'with the FIN bit turned on.
  446.         '
  447.         lngRetValue = shutdown(m_lngSocketHandle, SD_SEND)
  448.         '
  449.         '
  450.         If lngRetValue = SOCKET_ERROR Then
  451.             Err.Raise Err.LastDllError, "CSocket.CloseSocket", GetErrorDescription(Err.LastDllError)
  452.         End If
  453.         '
  454.     End If
  455. EXIT_LABEL:
  456.     '
  457. Exit Sub
  458.     '
  459. Close_Err_Handler:
  460.     '
  461.     If Err.Number <> 10038 Then
  462.         'Err.Raise Err.Number, "CSocket.Close", Err.Description
  463.     End If
  464.     '
  465.     GoTo EXIT_LABEL
  466.     '
  467. End Sub
  468. Public Sub Bind(Optional lngLocalPort As Long, Optional strLocalIP As String)
  469. Attribute Bind.VB_Description = "Binds socket to specific port and adapter"
  470.   '
  471.   Dim lngRetValue     As Long         'value returned by the bind Winsock API function
  472.   Dim udtLocalAddr    As sockaddr_in  'local socket address to bind to - used by the
  473.     '                                    bind Winsock API function
  474.   Dim lngAddress      As Long         '32-bit host address - value returned by':(燤ove line to top of current Sub
  475.     '                                    the inet_addr Winsock API function
  476.     '
  477.     On Error GoTo Bind_Err_Handler
  478.     '
  479.     'If no socket has been created before, try to create a new one
  480.     If Not SocketExists Then Exit Sub ':(燛xpand Structure or consider reversing Condition
  481.     '
  482.     'If the arguments of this function are not missing, they
  483.     'overwrites values of the RemoteHost and RemotePort properties.
  484.     '
  485.     If Len(strLocalIP) > 0 Then
  486.         m_strLocalIP = strLocalIP
  487.     End If
  488.     '
  489.     If lngLocalPort > 0 Then
  490.         m_lngLocalPort = lngLocalPort
  491.     End If
  492.     '
  493.     If Len(m_strLocalIP) > 0 Then
  494.         '
  495.         'If the local IP is known, get the address
  496.         'from it with the inet_addr Winsock API function.
  497.         lngAddress = inet_addr(m_strLocalIP)
  498.         '
  499.       Else 'NOT LEN(M_STRLOCALIP)...
  500.         '
  501.         'If the IP is unknown, assign the default interface's IP.
  502.         'Actually, this line is useless in Visual Basic code,
  503.         'as INADDR_ANY = 0 (IP = 0.0.0.0).
  504.         lngAddress = INADDR_ANY
  505.         '
  506.     End If
  507.     '
  508.     If lngAddress = SOCKET_ERROR Then
  509.         '
  510.         'Bad address - go away
  511.         Err.Raise Err.LastDllError, "CSocket.Bind", GetErrorDescription(Err.LastDllError)
  512.         Exit Sub '>---> Bottom
  513.         '
  514.     End If
  515.     '
  516.     'Prepare the udtLocalAddr UDT that is a socket address structure.
  517.     With udtLocalAddr
  518.         '
  519.         .sin_addr = lngAddress  'host address (32-bits value)
  520.         .sin_family = AF_INET   'address family
  521.         .sin_port = htons(LongToUnsigned(m_lngLocalPort))   'port number in the network byte order
  522.         '
  523.     End With 'UDTLOCALADDR
  524.     '
  525.     'Call the bind Winsock API function in order to assign local address for the socket
  526.     lngRetValue = api_bind(m_lngSocketHandle, udtLocalAddr, Len(udtLocalAddr))
  527.     '
  528.     If lngRetValue = SOCKET_ERROR Then
  529.         '
  530.         Err.Raise Err.LastDllError, "CSocket.Bind", GetErrorDescription(Err.LastDllError)
  531.         '
  532.       Else 'NOT LNGRETVALUE...
  533.         '
  534.         m_blnSocketIsBound = True   'Added: 10-MAR-2002
  535.         '
  536.     End If
  537.     '
  538. EXIT_LABEL:
  539.     '
  540. Exit Sub
  541.     '
  542. Bind_Err_Handler:
  543.     '
  544.     Err.Raise Err.Number, "CSocket.Bind", Err.Description
  545.     '
  546.     GoTo EXIT_LABEL
  547.     '
  548. End Sub
  549. Public Sub Accept(requestID As Long)
  550. Attribute Accept.VB_Description = "Accept an incoming connection request"
  551.   '
  552.   'The requestID argument is provided with the ConnectRequest
  553.   'event of another instance of the CSocket class. Actually,
  554.   'this argument is a handle of the socket already created
  555.   'calling the Accept Winsock API function by that (another)
  556.   'instance of the CSocket class.
  557.   '
  558.   Dim lngRetValue As Long         'value returned by the getsockname, getpeername, and
  559.     '                                getsockopt Winsock API functions
  560.   Dim lngBuffer   As Long         'the buffer to pass with the getsockopt Winsock API function':(燤ove line to top of current Sub
  561.   Dim udtSockAddr As sockaddr_in  'socket address - used by the getsockname and getpeername':(燤ove line to top of current Sub
  562.     '                                Winsock API functions
  563.   Dim udtHostEnt  As HostEnt      'structure to hold the host info - returned by the':(燤ove line to top of current Sub
  564.     '                                getsockname and getpeername Winsock API functions
  565.     '
  566.     On Error GoTo Accept_Err_Handler
  567.     '
  568.     'What we need to do in the body of this subroutine is to
  569.     'initialize the properties of the class that we can find
  570.     'values for. Also we need to register the socket with
  571.     'the RegisterSocket function from MSocketSupport module.
  572.     '
  573.     'Assign the socket handle
  574.     m_lngSocketHandle = requestID
  575.     '
  576.     'Retrieve the connection end-points to initialize
  577.     'the following properties of the CSocket class:
  578.     'LocalPort, LocalIP, LocalHostName
  579.     'RemotePort, RemoteHostIP, RemoteHost
  580.     '
  581.     'Local end point
  582.     '
  583.     lngRetValue = getsockname(m_lngSocketHandle, udtSockAddr, Len(udtSockAddr))
  584.     '
  585.     If lngRetValue = 0 Then
  586.         '
  587.         'LocalPort property
  588.         m_lngLocalPort = IntegerToUnsigned(ntohs(udtSockAddr.sin_port))
  589.         'LocalIP property
  590.         m_strLocalIP = StringFromPointer(inet_ntoa(udtSockAddr.sin_addr))
  591.         'LocalHostName property
  592.         lngRetValue = gethostbyaddr(udtSockAddr.sin_addr, 4&, AF_INET)
  593.         CopyMemory udtHostEnt, ByVal lngRetValue, Len(udtHostEnt)
  594.         m_strLocalHostName = StringFromPointer(udtHostEnt.hName)
  595.         '
  596.     End If
  597.     '
  598.     'Remote end point
  599.     '
  600.     lngRetValue = getpeername(m_lngSocketHandle, udtSockAddr, Len(udtSockAddr))
  601.     '
  602.     If lngRetValue = 0 Then
  603.         '
  604.         'RemotePort property
  605.         m_lngRemotePort = IntegerToUnsigned(ntohs(udtSockAddr.sin_port))
  606.         'RemoteHostIP property
  607.         m_strRemoteHostIP = StringFromPointer(inet_ntoa(udtSockAddr.sin_addr))
  608.         'RemoteHost property
  609.         lngRetValue = gethostbyaddr(udtSockAddr.sin_addr, 4&, AF_INET)
  610.         CopyMemory udtHostEnt, ByVal lngRetValue, Len(udtHostEnt)
  611.         m_strRemoteHost = StringFromPointer(udtHostEnt.hName)
  612.         '
  613.     End If
  614.     '
  615.     'Retrieve the socket type to initialize the Protocol property
  616.     lngRetValue = getsockopt(m_lngSocketHandle, SOL_SOCKET, SO_TYPE, lngBuffer, LenB(lngBuffer))
  617.     '
  618.     If lngRetValue <> SOCKET_ERROR Then
  619.         '
  620.         If lngBuffer = SOCK_STREAM Then
  621.             mvarProtocol = sckTCPProtocol
  622.           Else 'NOT LNGBUFFER...
  623.             mvarProtocol = sckUDPProtocol
  624.         End If
  625.         '
  626.     End If
  627.     '
  628.     'Get default size of the Winsock's buffers.
  629.     Call GetWinsockBuffers  'Added: 10-MAR-2002
  630.     '
  631.     If MSocketSupport.RegisterSocket(m_lngSocketHandle, ObjPtr(Me)) Then
  632.         '
  633.         'Change the State property value
  634.         mvarState = sckConnected
  635.         '
  636.     End If
  637.     '
  638. EXIT_LABEL:
  639.     '
  640. Exit Sub
  641.     '
  642. Accept_Err_Handler:
  643.     '
  644.     Err.Raise Err.Number, "CSocket.Accept", Err.Description
  645.     '
  646.     GoTo EXIT_LABEL
  647.     '
  648. End Sub
  649. Public Property Get State() As StateConstants
  650.     State = mvarState
  651. End Property
  652. Public Property Get SocketHandle() As Long
  653. Attribute SocketHandle.VB_Description = " Returns the socket handle"
  654.     SocketHandle = m_lngSocketHandle
  655. End Property
  656. Public Property Get RemotePort() As Long
  657. Attribute RemotePort.VB_Description = "Returns/Sets the port to be connected to on the remote computer"
  658.     RemotePort = m_lngRemotePort
  659. End Property
  660. Public Property Let RemotePort(NewValue As Long)
  661.     m_lngRemotePort = NewValue
  662. End Property
  663. Public Property Get RemoteHostIP() As String
  664. Attribute RemoteHostIP.VB_Description = "Returns the remote host IP address"
  665.     RemoteHostIP = m_strRemoteHostIP
  666. End Property
  667. Public Property Get RemoteHost() As String
  668. Attribute RemoteHost.VB_Description = "Returns/Sets the name used to identify the remote computer"
  669.     RemoteHost = m_strRemoteHost
  670. End Property
  671. Public Property Let RemoteHost(NewValue As String)
  672.     m_strRemoteHostIP = ""
  673.     m_strRemoteHost = NewValue
  674. End Property
  675. Public Property Get protocol() As ProtocolConstants
  676. Attribute protocol.VB_Description = "Returns/Sets the socket protocol"
  677.     protocol = mvarProtocol
  678. End Property
  679. Public Property Let protocol(NewValue As ProtocolConstants)
  680.   '
  681.     If m_lngSocketHandle = INVALID_SOCKET Then  'Modified: 10-MAR-2002
  682.         mvarProtocol = NewValue
  683.     End If
  684.     '
  685. End Property
  686. Public Property Get LocalPort() As Long
  687. Attribute LocalPort.VB_Description = "Returns/Sets the port used on the local computer"
  688.     LocalPort = m_lngLocalPort
  689. End Property
  690. Public Property Let LocalPort(NewValue As Long)
  691.     m_lngLocalPort = NewValue
  692. End Property
  693. Public Property Get LocalIP() As String
  694. Attribute LocalIP.VB_Description = "Returns the local machine IP address"
  695.     LocalIP = m_strLocalIP
  696. End Property
  697. Public Property Get LocalHostName() As String
  698. Attribute LocalHostName.VB_Description = "Returns the local machine name"
  699.     LocalHostName = m_strLocalHostName
  700. End Property
  701. Public Property Get BytesReceived() As Long
  702. Attribute BytesReceived.VB_Description = "Returns the number of bytes received on this connection"
  703.     BytesReceived = m_lngBytesReceived
  704. End Property
  705. Private Sub Class_Initialize()
  706.   '
  707.   'Socket's handle default value
  708.     m_lngSocketHandle = INVALID_SOCKET
  709.     'Initialize the Winsock service
  710.     m_lngMaxMsgSize = MSocketSupport.InitWinsockService
  711.     '
  712. End Sub
  713. Public Function vbSocket() As Long
  714.   '********************************************************************************
  715.   'Author    :Oleg Gdalevich
  716.   'Purpose   :Creates a new socket
  717.   'Returns   :The socket handle if successful, otherwise - INVALID_SOCKET
  718.   'Arguments :
  719.   '********************************************************************************
  720.   '
  721.     On Error GoTo vbSocket_Err_Handler
  722.     '
  723.   Dim lngRetValue     As Long 'value returned by the socket API function':(燤ove line to top of current Function
  724.     '
  725.     'Call the socket Winsock API function in order to create a new socket
  726.     If mvarProtocol = sckUDPProtocol Then
  727.         lngRetValue = api_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
  728.       Else 'NOT MVARPROTOCOL...
  729.         lngRetValue = api_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
  730.     End If
  731.     '
  732.     If lngRetValue = INVALID_SOCKET Then
  733.         '
  734.         Err.Raise Err.LastDllError, "CSocket.vbSocket", GetErrorDescription(Err.LastDllError)
  735.         '
  736.       Else 'NOT LNGRETVALUE...
  737.         '
  738.         '
  739.         If MSocketSupport.RegisterSocket(lngRetValue, ObjPtr(Me)) Then
  740.             '
  741.             'Assign returned value
  742.             vbSocket = lngRetValue
  743.             '
  744.           Else 'NOT MSOCKETSUPPORT.REGISTERSOCKET(LNGRETVALUE,...
  745.             '
  746.             'Err.Raise Err.LastDllError, "CSocket.vbSocket", GetErrorDescription(Err.LastDllError)
  747.             '
  748.         End If
  749.         '
  750.     End If
  751.     '
  752. EXIT_LABEL:
  753. Exit Function
  754. vbSocket_Err_Handler:
  755.     '
  756.     vbSocket = INVALID_SOCKET
  757.     '
  758. End Function
  759. Friend Sub PostSocketEvent(ByVal lngEventID As Long, Optional ByVal lngError As Long)
  760.   '
  761.   'This procedure is called by the WindowProc callback function
  762.   'from the MSocketSupport module. The lngEventID argument is an
  763.   'ID of the network event occurred for the socket. The lngError
  764.   'argument contains an error code only if an error was occurred
  765.   'during an asynchronous execution.
  766.   '
  767.   Dim lngBytesReceived    As Long         'value returned by the RecvDataToBuffer function
  768.   Dim lngRetValue         As Long         'value returned by the getsockname Winsock API function
  769.   Dim lngNewSocket        As Long         'value returned by the accept Winsock API function
  770.   Dim udtSockAddr         As sockaddr_in  'remote socket address for the accept Winscok API function
  771.   Dim udtHostEnt          As HostEnt      'structure to hold the host info - returned
  772.     '                                        by the gethostbyaddr Winsock API function
  773.     '
  774.     On Error GoTo ERROR_HANDLER
  775.     '
  776.     If lngError > 0 Then
  777.         '
  778.         'An error was occured.
  779.         '
  780.         'Change a value of the State property
  781.         mvarState = sckError
  782.         'Close the socket
  783.         Call DestroySocket
  784.         'The OnError event is just for this case
  785.         RaiseEvent OnError(CInt(lngError), GetErrorDescription(lngError), 0, "", "", 0, False)
  786.         'We have nothing to do here anymore
  787.         Exit Sub '>---> Bottom
  788.         '
  789.     End If
  790.     '
  791.     Select Case lngEventID
  792.         '
  793.       Case FD_READ
  794.         '
  795.         '
  796.         'Some data has arrived for this socket.
  797.         'Call the RecvDataToBuffer function that move arrived data
  798.         'from the Winsock buffer to the local one and returns number
  799.         'of bytes received.
  800.         lngBytesReceived = RecvDataToBuffer
  801.         '
  802.         '
  803.         'The BytesReceived property contains number of bytes in
  804.         'the local buffer of the class.
  805.         m_lngBytesReceived = m_lngBytesReceived + lngBytesReceived
  806.         '
  807.         'The OnDataArrival event is just for the case when some data
  808.         'was retieved from the Winsock buffer.
  809.         If lngBytesReceived > 0 Then
  810.             RaiseEvent OnDataArrival(Len(m_strRecvBuffer))
  811.         End If
  812.         '
  813.       Case FD_WRITE
  814.         '
  815.         'This message means that the socket in a write-able
  816.         'state, that is, buffer for outgoing data of the transport
  817.         'service is empty and ready to receive data to send through
  818.         'the network.
  819.         '
  820.         '
  821.         'If the local buffer for outgoing data (m_strSendBuffer) is
  822.         'not empty, the previous call of the send/sendto Winsock API
  823.         'function was failed. Call the SendBufferedData procedure in
  824.         'oreder to try to send that data again.
  825.         If Len(m_strSendBuffer) > 0 Then
  826.             '
  827.             Call SendBufferedData
  828.             '
  829.         End If
  830.         '
  831.       Case FD_OOB
  832.         '
  833.         'Ignored.
  834.         '
  835.       Case FD_ACCEPT
  836.         '
  837.         'When the socket is in a listening state, arrival of this message
  838.         'means that a connection request was received. Call the accept
  839.         'Winsock API function in oreder to create a new socket for the
  840.         'requested connection.
  841.         lngNewSocket = api_accept(m_lngSocketHandle, udtSockAddr, Len(udtSockAddr))
  842.         '
  843.         '
  844.         'Let the client application know that the request was received
  845.         'and pass with the event argument a handle of the recently created
  846.         'socket. The client application should create a new instance of
  847.         'the CSocket class, and then use the socket handle (lngNewSocket)
  848.         'to initialize its properties. Another way is to do not create
  849.         'one more instance of this class. We may close existing socket,
  850.         'and then accept the new handle:
  851.         '
  852.         '  Private Sub objSocket_OnConnectionRequest(ByVal requestID As Long)
  853.         '      If objSocket.State <> sckClosed Then objSocket.CloseSocket
  854.         '      objSocket.Accept (requestID)
  855.         '  End Sub
  856.         '
  857.         RaiseEvent OnConnectionRequest(lngNewSocket)
  858.         '
  859.       Case FD_CONNECT
  860.         '
  861.         'Arrival of this message means that the connection initiated by the call
  862.         'of the connect Winsock API function was successfully established.
  863.         '
  864.         'Get the connection local end-point parameters
  865.         '
  866.         lngRetValue = getsockname(m_lngSocketHandle, udtSockAddr, LenB(udtSockAddr))
  867.         '
  868.         If lngRetValue = 0 Then
  869.             '
  870.             'LocalPort property
  871.             m_lngLocalPort = IntegerToUnsigned(ntohs(udtSockAddr.sin_port))
  872.             'LocalIP property
  873.             m_strLocalIP = StringFromPointer(inet_ntoa(udtSockAddr.sin_addr))
  874.             'LocalHostName property
  875.             lngRetValue = gethostbyaddr(udtSockAddr.sin_addr, 4&, AF_INET)
  876.             CopyMemory udtHostEnt, ByVal lngRetValue, Len(udtHostEnt)
  877.             m_strLocalHostName = StringFromPointer(udtHostEnt.hName)
  878.             '
  879.         End If
  880.         '
  881.         ' -- Modified: 04-MAR-2002 --
  882.         '
  883.         'Change a value of the State property
  884.         mvarState = sckConnected
  885.         '
  886.         'Let the client app know that the connection was established.
  887.         RaiseEvent OnConnect
  888.         '
  889.         ' -- --------------------- --
  890.         '
  891.         '
  892.       Case FD_CLOSE
  893.         '
  894.         'This message means that the remote host is closing the conection
  895.         '
  896.         If mvarState = sckClosing Then
  897.             '
  898.             'If a value of the State property already is sckClosing,
  899.             'the closing of the connection was initiated by the local
  900.             'end-point (this socket) of the connection. In other words,
  901.             'the shutdown Winsock API function has been called before
  902.             '(the FIN segment is already sent by the local end-point).
  903.             '
  904.             'In this case we need wait until all the data sent by the
  905.             'remote end-point of the connection will be received.
  906.             '
  907.             Do
  908.                 '
  909.                 lngBytesReceived = RecvDataToBuffer
  910.                 '
  911.                 If lngBytesReceived > 0 Then
  912.                     RaiseEvent OnDataArrival(Len(m_strRecvBuffer))
  913.                 End If
  914.                 '
  915.             Loop Until lngBytesReceived = 0 Or lngBytesReceived = SOCKET_ERROR
  916.             '
  917.           Else 'NOT MVARSTATE...
  918.             '
  919.             mvarState = sckClosing
  920.             '
  921.             'If a value of the State property is not sckClosing, the
  922.             'connectoin is closing by the remote end-point of the
  923.             'connection (the FIN segment is sent by the remote host).
  924.             'In this case we need send all the remained data from the
  925.             'local buffer before to close the socket.
  926.             If Len(m_strSendBuffer) > 0 Then
  927.                 '
  928.                 Call SendBufferedData
  929.                 '
  930.             End If
  931.             '
  932.         End If
  933.         '
  934.         'Close the socket
  935.         Call DestroySocket
  936.         '
  937.         'Change a value of the State property
  938.         mvarState = sckClosed
  939.         '
  940.         'Let the client app that the connection is closed
  941.         RaiseEvent OnClose
  942.         '
  943.     End Select
  944.     '
  945. Exit Sub
  946.     '
  947. ERROR_HANDLER:
  948.     '
  949.     Err.Raise Err.Number, "CSocket.PostSocketEvent", Err.Description    'Modified: 15-APR-2002
  950.     '
  951. End Sub
  952. Friend Sub PostGetHostEvent(ByVal lngRequestID As Long, ByVal lngHostAddress As Long, strHostName As String, Optional lngError As Long)
  953.   '
  954.   'This procedure is called by the WindowProc callback function
  955.   'from the MSocketSupport module. Think about it as about result
  956.   'returned by the ResolveHost function called from this class.
  957.   '
  958.   Dim udtAddress      As sockaddr_in  'socket address - used by the connect Winsock API function
  959.   Dim lngRetValue     As Long         'value returned by the connect Winsock API function
  960.   Dim lngPtrToAddress As Long         'pointer to the string that contains IP address - value
  961.     'returned by the inet_ntoa Winsock API function
  962.     '
  963.     On Error GoTo ERROR_HANDLER
  964.     '
  965.     If lngError > 0 Then
  966.         '
  967.         'An error was occerred during resolving the host hame.
  968.         'For example: "Host not found"
  969.         '
  970.         '----------------------------------------------------------------
  971.         'Added: 28-APR-2002
  972.         'There is the case when a computer has a valid IP address
  973.         'but its name cannot be resolved. In this case the code should
  974.         'countinue the execution - we just don't need to change the
  975.         'RemoteHost property value.
  976.         '----------------------------------------------------------------
  977.         '
  978.         'Does the strHostName argument contain a valid IP address?
  979.         lngHostAddress = inet_addr(strHostName)
  980.         '
  981.         If lngHostAddress = INADDR_NONE Then    'Added: 28-APR-2002
  982.             '
  983.             'Change a value of the State property
  984.             mvarState = sckError
  985.             '
  986.             'Let the client app that an error was occurred.
  987.             RaiseEvent OnError(CInt(lngError), GetErrorDescription(lngError), 0, "", "", 0, False)
  988.             '
  989.             Exit Sub '>---> Bottom
  990.             '
  991.           Else    'Added: 28-APR-2002'NOT LNGHOSTADDRESS...
  992.             '
  993.             'Nothing to do here
  994.             'Both properties the RemoteHost and RemoteHostIP
  995.             'have the same value of the IP address string.
  996.             '
  997.         End If  'Added: 28-APR-2002
  998.         '
  999.     End If
  1000.     '
  1001.     'Check the id value - Do we really need this?
  1002.     If lngRequestID = 0 Then Exit Sub ':(燛xpand Structure or consider reversing Condition
  1003.     '
  1004.     If lngRequestID = m_lngRequestID Then
  1005.         '
  1006.         'Change a value of the State property
  1007.         mvarState = sckHostResolved
  1008.         '
  1009.         'Initialize the RemoteHost property
  1010.         m_strRemoteHost = strHostName
  1011.         '
  1012.         'Get pointer to the string that contains the IP address
  1013.         lngPtrToAddress = inet_ntoa(lngHostAddress)
  1014.         '
  1015.         'Retrieve that string by the pointer and init the
  1016.         'RemoteHostIP property.
  1017.         m_strRemoteHostIP = StringFromPointer(lngPtrToAddress)
  1018.         '
  1019.         'The ResolveHost function may be called from two methods
  1020.         'of the class: Connect and SendData. The m_varInternalState
  1021.         'variable tells us where the ResolveHost function called
  1022.         'from, and thus what to do here.
  1023.         '
  1024.         If m_varInternalState = istConnecting Then
  1025.             '
  1026.             'The ResolveHost was called from the Connect method, so
  1027.             'we need to continue the process of the connection establishing.
  1028.             '
  1029.             'Build the sockaddr_in structure to pass it to the connect
  1030.             'Winsock API function as an address of the remote host.
  1031.             With udtAddress
  1032.                 '
  1033.                 .sin_addr = lngHostAddress
  1034.                 .sin_family = AF_INET
  1035.                 .sin_port = htons(UnsignedToInteger(CLng(m_lngRemotePort)))
  1036.                 '
  1037.             End With 'UDTADDRESS
  1038.             '
  1039.             'Call the connect Winsock API function in order to establish connection.
  1040.             lngRetValue = api_connect(m_lngSocketHandle, udtAddress, Len(udtAddress))
  1041.             '
  1042.             'Since the socket we use is a non-blocking one, the connect Winsock API
  1043.             'function should return a value of SOCKET_ERROR anyway.
  1044.             '
  1045.             If lngRetValue = SOCKET_ERROR Then
  1046.                 '
  1047.                 'The WSAEWOULDBLOCK error is OK for such a socket
  1048.                 '
  1049.                 If Not Err.LastDllError = WSAEWOULDBLOCK Then
  1050.                     Err.Raise Err.LastDllError, "CSocket.PostGetHostEvent", GetErrorDescription(Err.LastDllError)
  1051.                   Else 'NOT NOT...
  1052.                     'Change the State property value
  1053.                     mvarState = sckConnecting
  1054.                 End If
  1055.                 '
  1056.             End If
  1057.             '
  1058.           ElseIf m_varInternalState = istSendingDatagram Then 'NOT M_VARINTERNALSTATE...
  1059.             '
  1060.             'The ResolveHost was called from the SendData method in
  1061.             'the case when a message-oriented (UDP) socket is used.
  1062.             '
  1063.             Call SendBufferedData
  1064.             '
  1065.         End If
  1066.         '
  1067.     End If
  1068.     '
  1069. Exit Sub
  1070.     '
  1071. ERROR_HANDLER:
  1072.     '
  1073.     Err.Raise Err.Number, "CSocket.PostGetHostEvent", Err.Description
  1074.     '
  1075. End Sub
  1076. Private Function SocketExists() As Boolean
  1077.   '
  1078.     If m_lngSocketHandle = INVALID_SOCKET Then
  1079.         '
  1080.         'If the m_lngSocketHandle is not a valid value, call
  1081.         'the vbSocket function in order to create a new socket
  1082.         m_lngSocketHandle = vbSocket
  1083.         '
  1084.         If m_lngSocketHandle = SOCKET_ERROR Then
  1085.             '
  1086.             'A value of SOCKET_ERROR means that the socket was not created.
  1087.             'In this case the SocketExists function must return False
  1088.             Exit Function '>---> Bottom
  1089.             '
  1090.           Else 'NOT M_LNGSOCKETHANDLE...
  1091.             '
  1092.             'Get default size of the Winsock's buffers.
  1093.             Call GetWinsockBuffers  'Modified: 10-MAR-2002
  1094.             '
  1095.         End If
  1096.         '
  1097.     End If
  1098.     '
  1099.     'The m_lngSocketHandle variable contains a valid socket
  1100.     'handle value. In this case the function returns True.
  1101.     SocketExists = True
  1102.     '
  1103. End Function
  1104. Private Sub GetWinsockBuffers()
  1105.   '
  1106.   'This subroutine is to retrieve default size of the Winsock buffers.
  1107.   'These values will be stored in the module level variables:
  1108.   'm_lngSendBufferLen and m_lngRecvBufferLen.
  1109.   'It can be called from the SocketExists and Accept functions.
  1110.   '
  1111.   'Added: 10-MAR-2002
  1112.   '
  1113.   Dim lngRetValue     As Long 'value returned by the getsockopt Winsock API function
  1114.   Dim lngBuffer       As Long 'buffer to pass with the getsockopt call
  1115.     '
  1116.     If mvarProtocol = sckTCPProtocol Then
  1117.         'Buffer for incoming data
  1118.         lngRetValue = getsockopt(m_lngSocketHandle, SOL_SOCKET, SO_RCVBUF, lngBuffer, 4&)
  1119.         m_lngRecvBufferLen = lngBuffer
  1120.         'Buffer for outgoing data
  1121.         lngRetValue = getsockopt(m_lngSocketHandle, SOL_SOCKET, SO_SNDBUF, lngBuffer, 4&)
  1122.         m_lngSendBufferLen = lngBuffer
  1123.       Else 'NOT MVARPROTOCOL...
  1124.         'the m_lngMaxMsgSize value is returned by InitWinsockService
  1125.         'function from the MSocketSupport module
  1126.         m_lngSendBufferLen = m_lngMaxMsgSize
  1127.         m_lngRecvBufferLen = m_lngMaxMsgSize
  1128.     End If
  1129.     '
  1130. End Sub
  1131. Private Function RecvDataToBuffer() As Long
  1132.   '
  1133.   'This function is to retrieve data from the Winsock buffer
  1134.   'into the class local buffer. The function returns number
  1135.   'of bytes retrieved (received).
  1136.   '
  1137.   Dim lngBytesReceived        As Long     'value returned by recv/recvfrom Winsock API function
  1138.   Dim lngRetValue             As Long     'value returned by gethostbyaddr Winsock API function
  1139.   Dim strTempBuffer           As String   'just a temporary buffer
  1140.   Dim arrBuffer()             As Byte     'buffer to pass to the recv/recvfrom Winsock API function
  1141.   Dim udtSockAddr             As sockaddr_in 'socket address of the remote peer
  1142.   Dim lngSockAddrLen          As Long     'size of the sockaddr_in structure
  1143.   Dim udtHostEnt              As HostEnt  'used to get host name with gethostbyaddr function
  1144.     '
  1145.     'Prepare the buffer to pass it to the recv/recvfrom Winsock API function.
  1146.     'The m_lngRecvBufferLen variable was initialized during creating
  1147.     'of the socket, see the vbSocket function to find out how.
  1148.     ReDim arrBuffer(m_lngRecvBufferLen - 1)
  1149.     '
  1150.     If mvarProtocol = sckTCPProtocol Then
  1151.         '
  1152.         'If the socket is a connection-oriented one, just call the recv function
  1153.         'to retrieve all the arrived data from the Winsock buffer.
  1154.         lngBytesReceived = recv(m_lngSocketHandle, arrBuffer(0), m_lngRecvBufferLen, 0&)
  1155.         '
  1156.       Else 'NOT MVARPROTOCOL...
  1157.         '
  1158.         'If the socket uses UDP, it's another story. As stated in the MS Winsock Control
  1159.         'documentation after receiving data the RemoteHost, RemoteHostIP, and RemotePort
  1160.         'properties contains parameters of the machine sending the UDP data. To achive
  1161.         'such a behavior we must use the recvfrom Winsock API function.
  1162.         '
  1163.         lngSockAddrLen = Len(udtSockAddr)
  1164.         '
  1165.         lngBytesReceived = recvfrom(m_lngSocketHandle, arrBuffer(0), m_lngRecvBufferLen, _
  1166.                            0&, udtSockAddr, lngSockAddrLen)
  1167.         '
  1168.         If Not lngBytesReceived = SOCKET_ERROR Then
  1169.             '
  1170.             'Now the udtSockAddr contains a socket address of the remote host.
  1171.             'Initialize the RemoteHost, RemoteHostIP, and RemotePort properties.
  1172.             '
  1173.             With udtSockAddr
  1174.                 '
  1175.                 'RemotePort property
  1176.                 m_lngRemotePort = IntegerToUnsigned(ntohs(.sin_port))
  1177.                 'RemoteHostIP property
  1178.                 m_strRemoteHostIP = StringFromPointer(inet_ntoa(.sin_addr))
  1179.                 'RemoteHost property
  1180.                 lngRetValue = gethostbyaddr(.sin_addr, 4&, AF_INET)
  1181.                 CopyMemory udtHostEnt, ByVal lngRetValue, Len(udtHostEnt)
  1182.                 m_strRemoteHost = StringFromPointer(udtHostEnt.hName)
  1183.                 '
  1184.             End With 'UDTSOCKADDR
  1185.             '
  1186.         End If
  1187.         '
  1188.     End If
  1189.     '
  1190.     If lngBytesReceived > 0 Then
  1191.         '
  1192.         'Convert a byte array into the VB string
  1193.         strTempBuffer = StrConv(arrBuffer(), vbUnicode)
  1194.         'Store received data in the local buffer for incoming data - m_strRecvBuffer
  1195.         m_strRecvBuffer = m_strRecvBuffer & Left$(strTempBuffer, lngBytesReceived)
  1196.         'Return number of received bytes.
  1197.         RecvDataToBuffer = lngBytesReceived
  1198.         '
  1199.       ElseIf lngBytesReceived = SOCKET_ERROR Then 'NOT LNGBYTESRECEIVED...
  1200.         '
  1201.         Err.Raise Err.LastDllError, "CSocket.RecvToBuffer", GetErrorDescription(Err.LastDllError)
  1202.         '
  1203.     End If
  1204.     '
  1205. End Function
  1206. Private Function RecvData(varData As Variant, blnPeek As Boolean, Optional varType As Variant, Optional maxLen As Variant) As Long
  1207.   '
  1208.   'This function is to retrieve data from the local buffer (m_strRecvBuffer).
  1209.   'It can be called by two public methods of the class - GetData and PeekData.
  1210.   'Behavior of the function is defined by the blnPeek argument. If a value of
  1211.   'that argument is True, the function returns number of bytes in the
  1212.   'local buffer, and copy data from that buffer into the varData argument.
  1213.   'If a value of the blnPeek is False, then this function returns number of
  1214.   'bytes received, and move data from the local buffer into the varData
  1215.   'argument. MOVE means that data will be removed from the local buffer.
  1216.   '
  1217.   Dim strRecvData As String   'temporary string buffer
  1218.   Dim arrBuffer() As Byte     'temporary byte array buffer
  1219.     '
  1220.     'If the local buffer is empty, go away - we have nothing to do here.
  1221.     If Len(m_strRecvBuffer) = 0 Then Exit Function ':(燛xpand Structure or consider reversing Condition
  1222.     '
  1223.     If IsEmpty(maxLen) Then
  1224.         maxLen = 0
  1225.     End If
  1226.     '
  1227.     If (Not maxLen > Len(m_strRecvBuffer)) And (maxLen > 0) Then
  1228.         '
  1229.         strRecvData = Left$(m_strRecvBuffer, CLng(maxLen))
  1230.         '
  1231.         If Not blnPeek Then
  1232.             m_strRecvBuffer = Mid$(m_strRecvBuffer, CLng(maxLen + 1))
  1233.         End If
  1234.         '
  1235.         arrBuffer() = StrConv(strRecvData, vbFromUnicode)
  1236.         '
  1237.       Else 'NOT (NOT...
  1238.         '
  1239.         arrBuffer() = StrConv(m_strRecvBuffer, vbFromUnicode)
  1240.         '
  1241.         If Not blnPeek Then
  1242.             m_strRecvBuffer = ""
  1243.         End If
  1244.         '
  1245.     End If
  1246.     '
  1247.     If IsEmpty(varType) Then
  1248.         varData = CStr(StrConv(arrBuffer(), vbUnicode))
  1249.       Else 'ISEMPTY(VARTYPE) = FALSE
  1250.         '
  1251.         Select Case varType
  1252.           Case vbArray + vbByte
  1253.             'Modified 28-MAY-2002. Thanks to Michael Freidgeim
  1254.             '--------------------------------
  1255.             'Dim strArray As String
  1256.             'strArray = StrConv(arrBuffer(), vbUnicode)
  1257.             'varData = StrConv(strArray, vbFromUnicode)
  1258.             varData = arrBuffer()
  1259.             '--------------------------------
  1260.           Case vbBoolean
  1261.   Dim blnData As Boolean ':(燤ove line to top of current Function
  1262.             CopyMemory blnData, arrBuffer(0), LenB(blnData)
  1263.             varData = blnData
  1264.           Case vbByte
  1265.   Dim bytData As Byte ':(燤ove line to top of current Function
  1266.             CopyMemory bytData, arrBuffer(0), LenB(bytData)
  1267.             varData = bytData
  1268.           Case vbCurrency
  1269.   Dim curData As Currency ':(燤ove line to top of current Function
  1270.             CopyMemory curData, arrBuffer(0), LenB(curData)
  1271.             varData = curData
  1272.           Case vbDate
  1273.   Dim datData As Date ':(燤ove line to top of current Function
  1274.             CopyMemory datData, arrBuffer(0), LenB(datData)
  1275.             varData = datData
  1276.           Case vbDouble
  1277.   Dim dblData As Double ':(燤ove line to top of current Function
  1278.             CopyMemory dblData, arrBuffer(0), LenB(dblData)
  1279.             varData = dblData
  1280.           Case vbInteger
  1281.   Dim intData As Integer ':(燤ove line to top of current Function
  1282.             CopyMemory intData, arrBuffer(0), LenB(intData)
  1283.             varData = intData
  1284.           Case vbLong
  1285.   Dim lngData As Long ':(燤ove line to top of current Function
  1286.             CopyMemory lngData, arrBuffer(0), LenB(lngData)
  1287.             varData = lngData
  1288.           Case vbSingle
  1289.   Dim sngData As Single ':(燤ove line to top of current Function
  1290.             CopyMemory sngData, arrBuffer(0), LenB(sngData)
  1291.             varData = sngData
  1292.           Case vbString
  1293.   Dim strData As String ':(燤ove line to top of current Function
  1294.             strData = StrConv(arrBuffer(), vbUnicode)
  1295.             varData = strData
  1296.             '
  1297.         End Select
  1298.         '
  1299.     End If
  1300.     '
  1301.     'Added 28-MAY-2002. Thanks to Michael Freidgeim
  1302.     m_lngBytesReceived = Len(m_strRecvBuffer) 'reset BytesReceived after Getdata
  1303.     '
  1304. End Function
  1305. Private Sub DestroySocket()
  1306.   '
  1307.   'The purpose of this subroutine is to unregister the socket with
  1308.   'UnregisterSocket that can be found in the MSocketSupport module
  1309.   'and close the socket with the closesocket Winsock API function.
  1310.   '
  1311.   Dim lngRetValue As Long 'value returned by the closesocket
  1312.     'Winsock AP function
  1313.     '
  1314.     If Not m_lngSocketHandle = INVALID_SOCKET Then
  1315.         '
  1316.         'Unregister the socket. For more info on how it works
  1317.         'see the code of the function in the MSocketSupport module
  1318.         Call MSocketSupport.UnregisterSocket(m_lngSocketHandle)
  1319.         '
  1320.         'Close the socket with the closesocket Winsock API function.
  1321.         lngRetValue = api_closesocket(m_lngSocketHandle)
  1322.         '
  1323.         '
  1324.         If lngRetValue = SOCKET_ERROR Then
  1325.             Err.Raise Err.LastDllError, "CSocket.DestroySocket", GetErrorDescription(Err.LastDllError)
  1326.         End If
  1327.         '
  1328.         'Change the SocketHandle property value
  1329.         m_lngSocketHandle = INVALID_SOCKET
  1330.         '
  1331.         'If the bind Winsock API function has been called on
  1332.         'this socket, m_blnSocketIsBound = True. We need to
  1333.         'change this value.
  1334.         m_blnSocketIsBound = False  'Added: 10-MAR-2002
  1335.         '
  1336.     End If
  1337.     '
  1338. End Sub
  1339. Private Sub Class_Terminate()
  1340.   '
  1341.     If Not m_lngSocketHandle = INVALID_SOCKET Then
  1342.         Call DestroySocket
  1343.     End If
  1344.     '
  1345.     Call CleanupWinsock
  1346.     '
  1347. End Sub
  1348. Private Sub SendBufferedData()
  1349.   '
  1350.   'This procedure sends data from the local buffer (m_strSendBuffer).
  1351.   'The data from the client application is passed with the SendData
  1352.   'method of the class as an argument and is stored in the local
  1353.   'buffer until all the data from that buffer will be sent using this
  1354.   'subroutine.
  1355.   '
  1356.   'Why do we need to store data in the local buffer? There are some
  1357.   'things happenning in the Winsock's buffer for outgoing data since
  1358.   'we're using non-blocking sockets' calls. If that buffer is full,
  1359.   'the transport subsystem doesn't take the data and the send/sendto
  1360.   'functions return a value of SOCKET_ERROR, Err.LastDllError give
  1361.   'us a value of WSAEWOULDBLOCK. This means that if the socket would
  1362.   'be a blocking one, such a call would block socket until the buffer
  1363.   'will be freed and ready to accept some data to send.
  1364.   '
  1365.   'So this procedure can be called several (mostly not more than two)
  1366.   'times for the same chunk of data. First call is in the body of the
  1367.   'SendData method, and other calls (if necessary) will be performed
  1368.   'from the PostSocketEvent subroutine, as soon as the FD_WRITE message
  1369.   'will be received. The arrival of the FD_WRITE message means that a
  1370.   'socket is in a write-able state - its buffer is ready to get data.
  1371.   '
  1372.   Dim lngRetValue     As Long         'value returned by send/sendto Winsock API function
  1373.   Dim arrData()       As Byte         'data to send with the send/sendto function
  1374.   Dim lngBufferLength As Long         'size of the data buffer to send
  1375.   Dim udtSockAddr     As sockaddr_in  'address of the remote socket - for the sendto function
  1376.     '
  1377.     'The send/sendto function needs this value for one of its arguments
  1378.     lngBufferLength = Len(m_strSendBuffer)
  1379.     '
  1380.     'Convert data from a VB string to a byte array
  1381.     arrData() = StrConv(m_strSendBuffer, vbFromUnicode)
  1382.     '
  1383.     If mvarProtocol = sckTCPProtocol Then
  1384.         '
  1385.         'just call the send function in order to send data via connection
  1386.         lngRetValue = send(m_lngSocketHandle, arrData(0), lngBufferLength, 0&)
  1387.         '
  1388.       Else 'NOT MVARPROTOCOL...
  1389.         '
  1390.         'With UDP socket we are going to use the sendto Winsock API function.
  1391.         'This function needs the socket address of the remote host to send
  1392.         'message to.
  1393.         '
  1394.         If Len(m_strRemoteHostIP) = 0 Then
  1395.             '
  1396.             'If the RemoteHostIP property is empty, we don't know
  1397.             'the remote IP so we need to resolve that address.
  1398.             '
  1399.             m_varInternalState = istSendingDatagram
  1400.             m_lngRequestID = MSocketSupport.ResolveHost(m_strRemoteHost, ObjPtr(Me))
  1401.             '
  1402.             'The ResolveHost is an asynchronous call. This subroutine wiil be called
  1403.             'one more time from the PostGetHostEvent procedure when the host will be
  1404.             'resolved.
  1405.             '
  1406.           Else 'NOT LEN(M_STRREMOTEHOSTIP)...
  1407.             '
  1408.             'If we are here the host was resolved successfully and the RemoteHostIP
  1409.             'property provides us with IP to send a UDP message to.
  1410.             '
  1411.             'Build the sockaddr_in structure to pass the remote socket address
  1412.             'to the sendto function.
  1413.             With udtSockAddr
  1414.                 .sin_addr = inet_addr(m_strRemoteHostIP)
  1415.                 .sin_port = htons(UnsignedToInteger(m_lngRemotePort))
  1416.                 .sin_family = AF_INET
  1417.             End With 'UDTSOCKADDR
  1418.             '
  1419.             'Call the sendto function in order to send a UDP message
  1420.             lngRetValue = sendto(m_lngSocketHandle, arrData(0), lngBufferLength, 0&, udtSockAddr, Len(udtSockAddr))
  1421.             '
  1422.         End If
  1423.         '
  1424.     End If
  1425.     '
  1426.     If lngRetValue = SOCKET_ERROR Then
  1427.         '
  1428.         'If a value of Err.LastDllError is WSAEWOULDBLOCK, that means
  1429.         'that the Winsock's buffer for outgoing data is full and cannot
  1430.         'accept data to send. In this case we ignore this error and do
  1431.         'not empty local buffer (m_strSendBuffer).
  1432.         '
  1433.         If Not Err.LastDllError = WSAEWOULDBLOCK Then
  1434.             Err.Raise Err.LastDllError, "CSocket.SendData", GetErrorDescription(Err.LastDllError)
  1435.         End If
  1436.         '
  1437.       Else 'NOT LNGRETVALUE...
  1438.         '
  1439.         'The data were sent successfully. Raise the OnSendProgress or
  1440.         'OnSendComplete event to let the client app know.
  1441.         '
  1442.         '
  1443.         If Len(m_strSendBuffer) > lngRetValue Then
  1444.             '
  1445.             m_strSendBuffer = Mid$(m_strSendBuffer, lngRetValue + 1)
  1446.             '
  1447.           Else 'NOT LEN(M_STRSENDBUFFER)...
  1448.             m_strSendBuffer = ""
  1449.             RaiseEvent OnSendComplete
  1450.         End If
  1451.         '
  1452.         RaiseEvent OnSendProgress(lngRetValue, Len(m_strSendBuffer))
  1453.         '
  1454.     End If
  1455.     '
  1456. End Sub