Quantcast
Channel: VBForums - CodeBank - Visual Basic 6 and earlier
Viewing all articles
Browse latest Browse all 1478

VB^ - SimpleServer

$
0
0
CSocket was originally developed by Oleg Gdalevich as a replacement for MSWinSck. Emiliano Scavuzzo improved upon it with CSocketMaster, and I converted it to support IPv6 with cSocket2. With NewSocket, I attempted to streamline the code. SimpleSock is a complete rewrite designed to further simplify and streamline socket code. SimpleServer is designed to allow a single listening socket to support multiple connections without having to resort to using a control array. Like all socket tools, it must be used properly to be effective.

In theory, SimpleServer could be used for all socket access, but for only a few sockets, SimpleSock is simpler and easier to use. Understanding how SimpleServer works will allow you to make better use of it. There are 8 events or call-backs associated with a socket.
1. CloseSck
2. Connect
3. ConnectionRequest
4. DataArrival
5. EncrDataArrival
6. Error
7. SendComplete
8. SendProgress
In SimpleServer, there is another one called WndProc, but it is not used and only provides access to the other events. With one exception (CloseSck), these routines are not called directly. They simply provide information to the calling program.

The calling program Implements SimpleServer. That means that any procedure declared as Public in SimpleServer will be implemented in the calling program, and that includes the 8 routines noted above. When SimpleServer is first implemented, the individual routines have to be activated. This is accomplished by clicking on each one. As you do so, they will go from plain to bold text. Routines that we want to access from the calling program but we do not want to implement, are declared as Friend instead of Public.

When we add an instance of a class with call-backs, we simply define the procedure "WithEvents" and add a new instance. With Implements, we can't do that. So we have to do the following instead:
Code:

Implements SimpleServer
Private mServer() As New SimpleServer

    Dim lNum As Long
    ReDim mServer(MaxClients)
    For lNum = 0 To MaxClients
        Set mServer(lNum).Callback(lNum) = Me
        mServer(lNum).IPvFlg = 4
    Next
    ReDim RecLen(MaxClients)
    ReDim RecType(MaxClients)

Adding the IPvFlg is not strictly necessary, because SimpleServer defaults to IPv4. But it is a good practice to get into. With SimpleServer, the listening socket is always the first instance.
Code:

mServer(0).Listen(PortListen)
If SimpleServer was to be used to make a connection to another host, it would call "mServer.TCPConnect Destination, PortConnect". Once the connection is established, SimpleServer would receive an "FD_CONNECT" and fire off a "Connect" message to the calling program. That would leave the calling program ready to start sending data.

When a client attempts to connect, an "FD_ACCEPT" is received by SimpleServer, and it fires off a "ConnectionRequest" message to the calling program. If acceptable, the calling program sends "mServer(lIndex).Accept(requestID, RemotePort, RemoteHostIP)". If it is not acceptable, it sends "mServer(lIndex).Accept(requestID, 0, "")", and SimpleServer interprets the "0" port as invalid.

Data is received by a socket in packets of approximately 1500 bytes. Of this, a maximum of 1460 bytes is actual data. Winsock assembles those packets into blocks of data that vary with the system. Windows Vista has a block size of 8,192 bytes, and Win 8.1 has a block size of 65,536 bytes. Winsock doesn't necessarily use all that space, it is just a maximum. Whatever criteria the OS uses, when it is ready it will send an "FD_READ" message to SimpleServer. For TCP, SimpleServer will add that data to it's own buffer (m_bRecvBuffer) and remove it from the Winsock buffer. It then fires off a "DataArrival"/"EncrDataArrival" message to the calling program along with the number of bytes just received. For UDP, SimpleServer will leave the data in the Winsock buffer, and notify the calling program of the bytes received.

How the calling program handles this information depends on the program itself. SimpleServer will keep adding data to "m_bRecvBuffer" (TCP) until the calling program gives it instructions. In the sample program I have provided, I have used a header to provide more information about the data being sent. It includes a Record Type and Record Length. The Record Length tells the receiving program how much data to expect. Because the data length does not include the header itself, the header is removed from the buffer using the statements "Call mServer(Index).RecoverData(8)" & "RecHeader = mServer(Index).bInBuffer". The (8) is an optional number telling SimpleServer to only remove 8 bytes. If it was left out, SimpleServer would remove all bytes. If the Record Length includes the header, it can be recovered using the "PeekData" command and left in the buffer.

All the data could be removed and analyzed in the "DataArrival"/"EncrDataArrival" routines, but that would mean separate buffers would be required for each connection, and I don't know how to create an array of byte arrays. Instead, we simply allow the data to accumulate in the "m_bRecvBuffer" in each instance of SimpleServer, and remove the desired amount when it is exceeded.

Sending of data is similar. All the data is added to "m_bSendBuffer" regardless of size. When the calling program issues a TCPSend, it enters a loop. SimpleServer copies from "m_bSendBuffer" a maximum of the block size of the Winsock output buffer and forwards it to the Winsock API. If the API is successful in sending the data, it returns the number of bytes sent and they are removed from "m_bSendBuffer". It remains in the loop until all the bytes are sent. Should the API return an error "WSAEWOULDBLOCK", it means that the API is still busy sending the previous block. A message is sent to "SendProgress" with the total bytes sent and the bytes remaining, and the loop exited. When the Winsock output buffer is once again ready to send data, it sends an "FD_WRITE" message to SimpleServer, and SimpleServer calls TCPSend once again. When all the data has been sent, messages are sent to both "SendProgress" and "SendComplete".

All SimpleServer errors (with the exception of "WSAEWOULDBLOCK") are forwarded to the calling program for action. Normally, in a server application errors are logged, so as to prevent holding up the program itself.

That leaves the "CloseSck" event. There are 2 ways of closing the socket. Should the far end close the socket, Winsock will send an "FD_CLOSE" message to SimpleServer. SimpleServer will forward a message to "CloseSck" and change the socket state to "sckClosing". CloseSck will call "mServer(Index).CloseSocket" which will actually close the socket on the server side and change the socket state to "sckClosed". To close the socket from the server end, users should refrain from calling "CloseSocket" directly. This can cause the socket to remain in the "sckClosing" state and become unusable. Always call "CloseSck" in the calling program. As an added note, always include a routine in the "Form_Unload" event to close all sockets. Failure to do so can cause a port to become unusable.

J.A. Coutts
Attached Files

Viewing all articles
Browse latest Browse all 1478

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>