I have been fairly satisfied with the performance of SimpleSock, but even though SimpleServer is being used successfully in a couple of server routines, I have not been able to stress test it. I would like to thank dilettante for providing the test routine for MSWinSck. After modifying the routines to use SimpleSock and SimpleServer, the SimpleServer routine failed the test miserably. It ran fine when tested with the default conditions (5 Clients, 100 Sends, same machine), but when I moved the client to a different machine, it failed at 3 Clients. The mode of failure was an incorrect calculation of the record length, which left the server waiting for the rest of the record. The point at which the failure occurred was not consistent, which made it difficult to troubleshoot. This is the relative debug output from one such failure.
-----------------------------------------------
FD_READ 664 = (socket 3)
InBuff 0
OK Bytes obtained from buffer: 5840 (4 x 1460)
Header:
01 01 00 00 00 00 28 57 (10327)
FD_READ 860 (socket 1)
InBuff 5832
OK Bytes obtained from buffer: 8192
Header:
87 88 89 8A 8B 8C 8D 8E
FD_READ 664 = (socket 3)
InBuff 3689
OK Bytes obtained from buffer: 4495
-----------------------------------------------
Socket 3 received 5,840 bytes. This represents 4 packets of 1,460 bytes each. Socket 1 then received 8,192 bytes. This represents the maximum Winsock buffer size this particular machine can handle. But that buffer already contained 5,832 bytes. This is equal to what socket 3 received less the header bytes.
To understand this situation better, I ran a packet sniffer and captured a different failure.
The first transfer (socket 65460) represents a normal transfer (8 packets totaling 10,327 bytes. On the second transfer, socket 65461 received a single packet, and was then interrupted by a transfer to socket 65459. This is what caused the failure, and is a real world scenario. This is because the sending programs are 3 separate applications operating independently.
SimpleServer originally extracted all the bytes received by a socket, and accumulated and analyzed them in the "DataArrival" routine. Any left over bytes were retained, to be included in the next record. This worked well in SimpleSock because it only supported a single socket. Even though there are independent routines for each socket (defined by Index), the variables defined within each routine appear to be common. The solution was to define those variables (RecLen/Type) as an array, and let the local buffer in the class instance (m_bRecvBuffer) accumulate bytes until the entire record is received. This uses a feature that has always existed in SimpleServer that allows you to extract a restricted number of bytes (maxLen). This is used to extract the header, which I used to define the length of the following record. If you decide that the length will include the header, a routine called PeekData was added that allows you to copy the number of bytes needed, but leave them in place.
I am glad that you can continue this update work, may be perfect replacement ms winsock.ocx.
Hope you can make a topic update post, perhaps easier to read.thanks .
I am glad that you can continue this update work, may be perfect replacement ms winsock.ocx.
Hope you can make a topic update post, perhaps easier to read.thanks .
Not sure what you mean by this, but I assume that you would like to see a separate post specific to SimpleServer with an explanation of its use. I can do that when I get time.
In the meantime, this version of the test worked because all the records used were more than 4,096 bytes. But what if 2 or more small records were recovered within the same block of data. There would only be a single "DataArrival" event, and the second record would be left stranded. This required a further adjustment to the "DataArrival" routine.
Code:
Private Sub SimpleServer_DataArrival(ByVal Index As Long, ByVal bytesTotal As Long)
Dim bData() As Byte
Dim RecHeader() As Byte
Dim bResponse() As Byte
GetNextRecord:
If RecLen(Index) = 0 Then 'Remove header
Call mServer(Index).RecoverData(8)
RecHeader = mServer(Index).bInBuffer
Call DebugPrintByte("Header", RecHeader)
CopyMemory ByVal VarPtr(RecLen(Index)), RecHeader(4), 4
RecLen(Index) = ntohl(RecLen(Index))
RecType(Index) = RecHeader(0)
bytesTotal = bytesTotal - 8
End If
Debug.Print bytesTotal, RecLen(Index)
If bytesTotal >= RecLen(Index) Then
Call mServer(Index).RecoverData(RecLen(Index))
bData = mServer(Index).bInBuffer
bytesTotal = bytesTotal - RecLen(Index)
Select Case RecType(Index)
Case 1 'Data
'Pretend we have processed Data, generate new Data response:
bResponse = GetData(4096 + Int(Rnd() * 8192) - 1)
TotalMsgs = TotalMsgs + 1
lblTotalMsgs.Caption = Format$(TotalMsgs, "#,##0")
Call AddRecHeader(1, bResponse)
mServer(Index).bOutBuffer = bResponse
mServer(Index).TCPSend
Case Else 'Ignore record
'Do nothing
End Select
RecLen(Index) = 0
RecType(Index) = 0
ReDim RecHeader(0)
If bytesTotal > 0 Then GoTo GetNextRecord
Else
'Wait for all the data
End If
End Sub
In the Server project, in the ServerUI form, you have:
Code:
Private Function InitServer() As Boolean
ReDim mServer(MaxClients)
For lNum = 0 To MaxClients
Set mServer(lNum).Callback(lNum) = Me
mServer(lNum).IPvFlg = 4
Next
End Function
Me in that case, invoked in a form, returns a reference to it, to that instance of ServerUI.
But in the SimpleServer Class, in the CallBack property Set, it expects a SimpleServer object reference, not a form:
Code:
Friend Property Set Callback(ByVal Index As Long, ByRef RHS As SimpleServer)
I found that when I just started to try to understand the issue discussed here.
PS: I would have expected the IDE to raise an error because of the type mismatch, but it doesn't.
In the Server project, in the ServerUI form, you have:
Code:
Private Function InitServer() As Boolean
ReDim mServer(MaxClients)
For lNum = 0 To MaxClients
Set mServer(lNum).Callback(lNum) = Me
mServer(lNum).IPvFlg = 4
Next
End Function
Me in that case, invoked in a form, returns a reference to it, to that instance of ServerUI.
But in the SimpleServer Class, in the CallBack property Set, it expects a SimpleServer object reference, not a form:
Code:
Friend Property Set Callback(ByVal Index As Long, ByRef RHS As SimpleServer)
I found that when I just started to try to understand the issue discussed here.
PS: I would have expected the IDE to raise an error because of the type mismatch, but it doesn't.
I found the reason why it doesn't raise a type mismath: the code module of the form implements the Simpleserver interface.
I didn't examine the code in extend, but as far as I can tell, the confussion raised in the other thread (about the instances of Classes sharing the variables values, what is not true) comes from this design. I'll explain:
You are using the Class SimpleServer in two different and unrelated ways:
1) For handling the server tasks.
2) As an interface for the callback to the object that is the caller.
Related to 1) you have the mServer() variable array that holds several instances of the SimpleServer class.
Related to 2) you have the Implements SimpleServer statement.
It means that that object, that is the ServerUI form, will implement the interface of the SimpleServer class.
It is not actually a SimpleServer class object, so it is not an instance of the SimpleServer class, but it is a ServerUI form that implements that interface.
It doesn't actually hold an instance to any SimpleServer class object, but a reference to the calling object, that in this case is the form ServerUI.
Doing so you can access the members of ServerUI that are in the SimpleServer interface, like:
Code:
Call m_Callback.Error(...
or
Code:
Call m_Callback.Connect(...
To avoid the confussion, I suggest to use two different classes for each purpose.
One for the normal task performed by the class, and another one for the callback interface.
This callback interface could be named ISimpleServerCallback, for example.
Then, the implements statement would be:
Code:
Implements ISimpleServerCallback
And you need to move the procedures definitions CloseSck, Connect, ConnectionRequest, DataArrival, Etc. to the new class.
And in the SimpleServer make the necessary changes, like:
Code:
Friend Property Set Callback(ByVal Index As Long, ByRef RHS As ISimpleServerCallback)
Perhaps you won't gain anything, but a clearer design.
Last edited by Eduardo-; Jan 18th, 2018 at 02:44 AM.