SimpleSock basically performs the same functions as NewSocket. Like NewSocket, it supports IPv6 as well as IPv4. This more or less restricts it's use to Windows Vista or better, as older operating systems do not support dual stack using "ws2_32.dll". Unlike NewSocket, it cannot be used as a Control Array because of the way it handles listening sockets (more on that later).
While Emiliano Scavuzzo's subclassing technique remains fairly much intact, the rest of the program has been completely rewritten and hopefully simplified. Notifying the Class with the protocol being used (TCP/UDP) is no longer required. Instead there are separate routines to handle each task. Lets take a look at some of the basics.
UDP (User Datagram Protocol)
I started with this one because it is the simplest. UDP is a peer-to-peer protocol, because both parties are equal and either one can initiate the conversation. It is also connectionless. That is to say that data is just sent with no idea if it made it correctly to the other end. The packet size is also very limited (256 bytes). For these reasons, it is rarely used for sensitive bulk data. In the sample program provided, an instance of SimpleSock is created called "mSocket". "mSocket" defaults to IPv4, so if IPv6 is required, you must notify the instance by setting the mSocket.IPvFlg to 6. To initiate a UDP session, you simply call:
The Destination Port and the Local Port are required, but if it is not known, the Destination can be left blank. This might be the case if the initial receiver does not know where the first message will be originating from. If blank, the GetAddrInfo function will return the LoopBack address (127.0.0.1 for IPv4 & ::1 for IPv6). You can test this functionality by setting the UDP option and the Local and Destination ports (they can both be the same), and typing a message in the text box followed by an <Enter>. The program will send the message to itself and the sender address (127.0.0.1/::1) will appear in the Destination text box. In the real world however, the sender's IP address will appear in the Destination text box, at which point the user can once again call the UDPInit function to update its information.
So what information gets updated? The first time through, UPDInit creates the socket and binds it to the Local Port. It then creates a "sockaddr" for the destination using GetAddrInfo. The sockaddr structure is the part that gets updated. For those familiar with the original IPv4 structure, it looked like this:
When IPv6 came along, this had to be changed to:
The larger sockaddr is used to carry the information for both IP protocols, with the extra 12 bytes being ignored for IPv4. Because the packet data is of limited length, UDP data is left in the Winsock Buffer and the calling program is informed of it's length. The calling program then recovers the data and empties the Winsock Buffer.
To send data via UDP, we need the Socket Handle, the binary Data and it's length, and the sockaddr and it's length for the destination. The data is passed to the output buffer as string data and converted to byte data, or sent directly to the output buffer as byte data. Providing that the sockaddr has been updated correctly, all the information is available to send back to the other end with a call to mSocket.UDPSend.
TCP (Transport Control Protocol)
The more commonly used protocol is TCP. There are actually 2 types of TCP, because one end acts as the server, and one end acts as the client. Lets look at the client end first, because it is the simpler. We establish a connection with the other end by calling:
We supply the Destination as either an IP address or a domain name, and the destination port as a long variable. GetAddrInfo will find the IP address for a Domain name, provided the name is defined in a DNS host, or it is a local network name. Normally, the Local port is not required, as the API will find the first available port. SimpleSock however does have the ability to use a selected port. If the port selected is not being used, it will bind the created socket to the port. It also eliminates the TIME_WAIT period by setting the options "SO_LINGER" & "SO_REUSEADDR". For reasons unknown, I had to set both these options to achieve the desired result. The API will send out a SYN request to the other end, and wait for a response. If the other end is listening for a connection request, it will send a SYN_ACK back to us. The API will acknowledge this by sending an ACK, and the connection is established. Once the connection is established, a "Connect" event is fired back to the calling program, and data can be sent immediately using "TCPSend".
Receipt of data is similar to UDP, except that SimpleSock removes the data from the Winsock buffer and adds it to it's own buffer. This is necessary because sent records can be quite lengthy, and are received in chunks. What is different about SimpleSock is the provision to handle encrypted data. This is accomplished by using 2 separate event messages (DataArrival/EncrDataArrival) to inform the calling program of data arrival.
To act as a TCP server, the socket is created and bound to the selected port using:
When a connection request is received from the other end, the API sends an "FD_ACCEPT" message to the "PostSocket" routine. This is where SimpleSock differs from NewSocket and it predecessors. The older programs would create a new socket and a temporary instance of the class to handle it. It would then be registered as an "Accept" item, before firing off a "ConnectionRequest" event to the calling program. The calling program would then close the Listening socket and call the class "Accept" function with the new socket handle. Closing of the listening socket and de-registering it caused the Socket Collection to be destroyed and the Window closed. The new socket would then be registered as a normal socket (causing a new Window and Socket Collection to be created), ownership of the new socket transferred from the temporary Class to the original Class, and the temporary Class destroyed. The calling program would then create a new Listening Socket. If this all sounds very complicated, it was. But it was necessary in order to duplicate the way that the MS Winsock Control handled things when used as a Control Array.
When SimpleSock receives an "FD_ACCEPT" message from an incoming connection attempt, it creates and registers the new socket as it normally would, and leaves the original listening socket intact. It then fires off a "ConnectionRequest" event to the calling program. The calling program then calls mSocket.Accept with the new socket handle. The Accept function saves the listening socket handle, sets a flag, and readies the new socket to receive and send data. If another connection request is received while the new socket is open, it will be ignored because the new socket is not in the listening mode. When the new socket is closed, the listening socket handle will be restored, and another connection request will be entertained.
This simplified approach is only useful when using the SimpleSock Class directly. It will not be effective if it was made into a Control and used as a Control Array. The next step is to make the Class able to handle multiple connections on the same listening port without creating a Control.
J.A. Coutts
Note: When using Link Local IPv6 addresses to communicate with older systems such as Vista, you may have to add the interface (eg. %8) to the IP address.
Note: The sample program is a demonstration program that uses various aspects of the socket function. It may not work properly when switching from one to another. Restart the program to test different functions.
While Emiliano Scavuzzo's subclassing technique remains fairly much intact, the rest of the program has been completely rewritten and hopefully simplified. Notifying the Class with the protocol being used (TCP/UDP) is no longer required. Instead there are separate routines to handle each task. Lets take a look at some of the basics.
UDP (User Datagram Protocol)
I started with this one because it is the simplest. UDP is a peer-to-peer protocol, because both parties are equal and either one can initiate the conversation. It is also connectionless. That is to say that data is just sent with no idea if it made it correctly to the other end. The packet size is also very limited (256 bytes). For these reasons, it is rarely used for sensitive bulk data. In the sample program provided, an instance of SimpleSock is created called "mSocket". "mSocket" defaults to IPv4, so if IPv6 is required, you must notify the instance by setting the mSocket.IPvFlg to 6. To initiate a UDP session, you simply call:
Code:
mSocket.UDPInit(Destination, PortConnect, PortLocal)
So what information gets updated? The first time through, UPDInit creates the socket and binds it to the Local Port. It then creates a "sockaddr" for the destination using GetAddrInfo. The sockaddr structure is the part that gets updated. For those familiar with the original IPv4 structure, it looked like this:
Code:
Private Type sockaddr_in
sin_family As Integer '2 bytes
sin_port As Integer '2 bytes
sin_addr As in_addr '4 bytes
sin_zero(0 To 7) As Byte '8 bytes
End Type 'Total 16 bytes
or reflected as:
Private Type sockaddr
sa_family As Integer '2 bytes
sa_data(0 to 13) As Byte '14 bytes
End Type 'Total 16 bytes
Code:
Private Type sockaddr_in6
sin6_family As Integer '2 bytes
sin6_port As Integer '2 bytes
sin6_flowinfo As Long '4 bytes
sin6_addr As in6_addr '16 bytes
sin6_scope_id As Long '4 bytes
End Type 'Total 28 bytes
Private Type sockaddr
sa_family As Integer '2 bytes
sa_data(0 to 25) As Byte '26 bytes
End Type 'Total 28 bytes
To send data via UDP, we need the Socket Handle, the binary Data and it's length, and the sockaddr and it's length for the destination. The data is passed to the output buffer as string data and converted to byte data, or sent directly to the output buffer as byte data. Providing that the sockaddr has been updated correctly, all the information is available to send back to the other end with a call to mSocket.UDPSend.
TCP (Transport Control Protocol)
The more commonly used protocol is TCP. There are actually 2 types of TCP, because one end acts as the server, and one end acts as the client. Lets look at the client end first, because it is the simpler. We establish a connection with the other end by calling:
Code:
mSocket.TCPConnect(Destination, PortConnect)
Receipt of data is similar to UDP, except that SimpleSock removes the data from the Winsock buffer and adds it to it's own buffer. This is necessary because sent records can be quite lengthy, and are received in chunks. What is different about SimpleSock is the provision to handle encrypted data. This is accomplished by using 2 separate event messages (DataArrival/EncrDataArrival) to inform the calling program of data arrival.
To act as a TCP server, the socket is created and bound to the selected port using:
Code:
mSocket.Listen(PortListen)
When SimpleSock receives an "FD_ACCEPT" message from an incoming connection attempt, it creates and registers the new socket as it normally would, and leaves the original listening socket intact. It then fires off a "ConnectionRequest" event to the calling program. The calling program then calls mSocket.Accept with the new socket handle. The Accept function saves the listening socket handle, sets a flag, and readies the new socket to receive and send data. If another connection request is received while the new socket is open, it will be ignored because the new socket is not in the listening mode. When the new socket is closed, the listening socket handle will be restored, and another connection request will be entertained.
This simplified approach is only useful when using the SimpleSock Class directly. It will not be effective if it was made into a Control and used as a Control Array. The next step is to make the Class able to handle multiple connections on the same listening port without creating a Control.
J.A. Coutts
Note: When using Link Local IPv6 addresses to communicate with older systems such as Vista, you may have to add the interface (eg. %8) to the IP address.
Note: The sample program is a demonstration program that uses various aspects of the socket function. It may not work properly when switching from one to another. Restart the program to test different functions.