SimpleSock is reliable and easy to use, but it is also complex and relatively inefficient. It was originally designed as a replacement for the Winsock Control, and many of the functions presented were not absolutely necessary. There were also a number of improvements made to Winsock2 (WS2_32.dll) that were not necessarily taken advantage of. So I decided to develop a simpler and faster version.
Objectives:
- Support normal TCP only (SOCK_STREAM, IPPROTO_IP).
- Use simple Module (Class module not beyond consideration).
- Try to use the same Module for both Client and Server.
- Try to use fixed byte arrays instead of variable length arrays.
- Support either IPv4 or IPv6.
Explanation:
The Client was developed first because it could be done without using callbacks. That is to say that a message could be sent to the Client and recovered manually by using the "recv" API call.
The Server on the other hand has to use callbacks in order to place the Server in listening mode and respond to a connection request. It has to create a socket (WSASocketA API), bind to that socket (bind API), and then place it in the listening mode (listen API). When a connection request is received by the Server, it has to create another socket and accept the connection request on the new socket (accept API). Once that connection is established, the callback routine will receive a "FD_WRITE" system message (2). This message simply informs us that the system outgoing buffer is empty and ready to receive data.
In our little example, the Client has created it's own socket when the Server accepted the connection and uses the "send" API to send data to the system outgoing buffer. The buffer will automatically send the data to the Server in packets. The outgoing and incoming buffers are more than likely rotary buffers that use pointers to determine what part of the 64K buffer is currently being used. A single packet can quite easily contain parts of two different records.
The Server receives that data via the "recv" API, and responds appropriately via our program using the "send" API. For example, if the Client requested the "date?", the Server responds with the current date.
These are nonblocking or asynchronous programs, and it is quite possible that the system "send" buffer could fill up. If this happens, a "WSAEWOULDBLOCK" error is returned and the system waits until there is adequate room available. The same is theoretically possible with the "recv" function, but I have not encountered that problem yet, as the operating system is much faster than the network sub system.
If you are going to connect via the network rather than the loopback address (127.0.0.1), be aware that your firewall may restrict access to the Server while in the IDE. If you compile the programs and run the Server, your system should prompt you to allow access.
This is a work in progress and feedback is welcome. Please be aware that the callback routines will prevent you from stopping the program or stepping through it. I used Debug.Print to troubleshoot it.
J.A. Coutts
Updated: 09/28/2024 = see post #4 for details
Last edited by couttsj; Oct 1st, 2024 at 09:18 AM.
The use of headers has been added. Headers are not necessary for our little example, but they are essential for larger file transfer. They also provide for identifying different types of records. For example, instead of identifying the text of each command, we could just identify each one as a different type. The Client Hello is identified as a Type 1, where Text is identified as Type 0.
To further understand what happens when transferring a file using headers, the following is provided.
File size - 66927
2915 + 1460 + 1460 + 1460 + 892 (8187)
2915 + 1460 + 2920 + 892 (8187)
4375 + 1460 + 1460 + 892 (8187)
4375 + 1460 + 1460 + 892 (8187)
1455 + 1460 + 4380 + 892 (8187)
2915 + 2920 + 1460 + 892 (8187)
2915 + 2920 + 2352 (8187)
2915 + 5272 (8187)
+ 1431 = 66927
These are byte counts. Notice how each is a multiple of 1460 with the exception of the first one which is 5 bytes less to account for the header. 8192 is the transmitted record size, 8187 is the saved record size, and 1460 is the packet size. In this particular case, the sending computer is slower, uses WiFi instead of hard wire, and the program is using SimpleSock which is slower. This has resulted in a number of partial records being captured. The opposite could also occur, where the receiving computer is slower, causing full records to be received and even possibly overflowing the system buffer (64K).