The attached server program is my first attempt at using IOCP {Input Output Completion Port). It is based on one submitted by xiaoyao, but I am not sure who deserves the credit as it was a repost.
To be effective at fully serving the needs of a high usage server, IOCP needs to be used in Overlapped mode as well as Multi Threaded mode. Overlapped I/O in non-blocking or asynchronous mode using multiple threads based on the number of CPU cores available, delivers the highest throughput according to Microsoft. My own experience so far supports that. Unfortunately there is very little information out there on utilizing this function with VB6. What I could find was information about C++ and .NET implementations with echo servers, and this information was not very helpful. Stepping through the program to identify problems proved to be next to impossible without the IDE crashing, so I was left with a lot of trial and error solutions to fix bugs.
Although this program is currently designed to use non-encrypted data, the intention is to eventually add encryption. To this end the first transmission after connecting to the server is a simple Client Hello ("Hello") using a 5 byte header. Overlapped I/O apparently has the ability to split the data into separate buffers (eg. Header/Body), but I have not yet attempted to implement that.
Overlapped I/O uses a fixed length buffer along with a length variable. Therefore it is not necessary to clear the buffer on each pass. The size of that buffer is configurable, and should be adjusted to suit the application. An application that hosts many connections with relatively short messages should have a smaller buffer than one that exchanges larger messages. This has to be balanced against operations such as encryption. Longer buffers take more processor power than shorter buffers. For this particular application, I have chosen 8,192 bytes as a starting point.
When a request is passed to a thread with a WSARecv or WSASend, the request is directed to the appropriate thread defined by ComKey, which is the Completion Key being used as the index to the connection array.
Sending encrypted files over a network requires messages to be sent as data defined by a header that can be decrypted as a complete block of data as defined in:
https://www.vbforums.com/showthread....13-VB6-GetFile
This worked well until I started experimenting with high speed server traffic over a Local Area network using IOCP in the IDE. File transfers must store the file blocks as they are received and decrypted to avoid having to store the entire file in memory before storage. Normally, network speed is slower than program speed, but in this scenario, the opening of the file for download was slow enough to only save the first record. Subsequently I have implemented an option to open the file in the client prior to starting the download.
Because much of the work is done by the API, the code is relatively short in comparison to my previous attempts at server networking. The downside is that it is also less flexible. The same buffer is used for sending as receiving. We signal the buffer to receive data by applying a "WSARecv". An endless loop in the worker thread detects incoming traffic with GetQueuedCompletionStatus (GQCS), the index for the connection is recovered, and the recovered bytes are made available. Whether or not the buffer is returned before it is complete is unknown at this time. To change from receiving to sending, we apply a "WSASend" with the bytes to be sent in "PerData(nPerIndex).Buf" and the number of bytes in "PerData(nPerIndex).Len". This program currently does not support IPv6.
That's the gist of it, with a few extra guidelines to control file streaming. There are 3 different scenarios I had to contend with. The first one is when the file is less than or equal in size to the buffer. The second is when the file size is greater than the buffer size, necessitating multiple records. The third is when the file size is evenly divisible by the buffer size, and this special case necessitates sending one more record with just the header. In both the second and third cases, the sending program detects that the last record was sent and changes the connection back to "Recv". Failure to do that will cause the IDE to crash.
The client end also detects that the last record was received and closes the file. A word of caution here. If the current file in the download directory is of a greater length than the new file of the same name, the file length of the original file will be retained. You must either kill or rename the present file before downloading.
There will probably be some bugs to fix. Feedback is welcome.
J.A. Coutts
To be effective at fully serving the needs of a high usage server, IOCP needs to be used in Overlapped mode as well as Multi Threaded mode. Overlapped I/O in non-blocking or asynchronous mode using multiple threads based on the number of CPU cores available, delivers the highest throughput according to Microsoft. My own experience so far supports that. Unfortunately there is very little information out there on utilizing this function with VB6. What I could find was information about C++ and .NET implementations with echo servers, and this information was not very helpful. Stepping through the program to identify problems proved to be next to impossible without the IDE crashing, so I was left with a lot of trial and error solutions to fix bugs.
Although this program is currently designed to use non-encrypted data, the intention is to eventually add encryption. To this end the first transmission after connecting to the server is a simple Client Hello ("Hello") using a 5 byte header. Overlapped I/O apparently has the ability to split the data into separate buffers (eg. Header/Body), but I have not yet attempted to implement that.
Overlapped I/O uses a fixed length buffer along with a length variable. Therefore it is not necessary to clear the buffer on each pass. The size of that buffer is configurable, and should be adjusted to suit the application. An application that hosts many connections with relatively short messages should have a smaller buffer than one that exchanges larger messages. This has to be balanced against operations such as encryption. Longer buffers take more processor power than shorter buffers. For this particular application, I have chosen 8,192 bytes as a starting point.
When a request is passed to a thread with a WSARecv or WSASend, the request is directed to the appropriate thread defined by ComKey, which is the Completion Key being used as the index to the connection array.
Sending encrypted files over a network requires messages to be sent as data defined by a header that can be decrypted as a complete block of data as defined in:
https://www.vbforums.com/showthread....13-VB6-GetFile
This worked well until I started experimenting with high speed server traffic over a Local Area network using IOCP in the IDE. File transfers must store the file blocks as they are received and decrypted to avoid having to store the entire file in memory before storage. Normally, network speed is slower than program speed, but in this scenario, the opening of the file for download was slow enough to only save the first record. Subsequently I have implemented an option to open the file in the client prior to starting the download.
Because much of the work is done by the API, the code is relatively short in comparison to my previous attempts at server networking. The downside is that it is also less flexible. The same buffer is used for sending as receiving. We signal the buffer to receive data by applying a "WSARecv". An endless loop in the worker thread detects incoming traffic with GetQueuedCompletionStatus (GQCS), the index for the connection is recovered, and the recovered bytes are made available. Whether or not the buffer is returned before it is complete is unknown at this time. To change from receiving to sending, we apply a "WSASend" with the bytes to be sent in "PerData(nPerIndex).Buf" and the number of bytes in "PerData(nPerIndex).Len". This program currently does not support IPv6.
That's the gist of it, with a few extra guidelines to control file streaming. There are 3 different scenarios I had to contend with. The first one is when the file is less than or equal in size to the buffer. The second is when the file size is greater than the buffer size, necessitating multiple records. The third is when the file size is evenly divisible by the buffer size, and this special case necessitates sending one more record with just the header. In both the second and third cases, the sending program detects that the last record was sent and changes the connection back to "Recv". Failure to do that will cause the IDE to crash.
The client end also detects that the last record was received and closes the file. A word of caution here. If the current file in the download directory is of a greater length than the new file of the same name, the file length of the original file will be retained. You must either kill or rename the present file before downloading.
There will probably be some bugs to fix. Feedback is welcome.
J.A. Coutts