< Socket, Bind, Connect, Send, Receive etc. | Main | Adding Ipv6 & Ipv6 Fragment C++ Header Definition Classes 2 >

 


 

 

Chapter 8 Part 8:

Client (and Server) Sockets Communication

 

 

What do we have in this chapter 8 Part 8?

  1. Creating Protocols Header Definition Class (C++) 1

 

 

Creating Protocols Header Definition Class (C++) 1

 

Create a new class library project and you might use ProtocolHeaderDefinitionCP as the name. This class will be used in the next related TCP/IP program examples. It contains the header definition of several protocols such as TCP, UDP and IP (for both IPv4 and IPv6). This definition class is quite long, so be careful.

 

Creating Protocols Header Definition Class (C++) - a new C++ class library project creation

 

Add/edit the following using directives at the top of the file.

 

using namespace System;

using namespace System::Net;

using namespace System::Net::Sockets;

using namespace System::Collections;

 

Rename the default given class to WinsockIoctl.

 

 

Add the following code for the WinsockIoctl class.

 

public ref class WinsockIoctl

{

            /// <summary>

    /// An interface query takes the socket address of a remote destination and

    /// returns the local interface that destination is reachable on.

    /// </summary>

public:

    static const int SIO_ROUTING_INTERFACE_QUERY = -939524076;  // otherwise equal to 0xc8000014

    /// <summary>

    /// The address list query returns a list of all local interface addresses.

    ///

    /// </summary>

public:

    static const int SIO_ADDRESS_LIST_QUERY = 0x48000016;

};

 

Add another class, SocketaddrConvert and its code.

 

public ref class SockaddrConvert

{

    /// <summary>

    /// This routine converts an IPEndPoint into a byte array that represents the

    /// underlying sockaddr structure of the correct type. Currently this routine

    /// supports only IPv4 and IPv6 socket address structures.

    /// </summary>

    /// <param name="endPoint">IPEndPoint to convert to a binary form</param>

    /// <returns>Binary array of the serialized socket address structure</returns>

public:

    static array<Byte>^ GetSockaddrBytes(IPEndPoint^ endPoint)

    {

        SocketAddress^ socketAddress = endPoint->Serialize();

        array<Byte>^ sockaddrBytes;

 

        sockaddrBytes = gcnew array<Byte>(socketAddress->Size);

 

        for (int i = 0; i < socketAddress->Size; i++)

        {

            sockaddrBytes[i] = socketAddress[i];

        }

        return sockaddrBytes;

    }

 

    /// <summary>

    /// This routine converts the binary representation of a sockaddr structure back

    /// into an IPEndPoint object. This is done by looking at the first 2 bytes of the

    /// serialized byte array which always indicate the address family of the underlying

    /// structure. From this we can construct the appropriate IPEndPoint object.

    /// </summary>

    /// <param name="sockaddrBytes"></param>

    /// <returns></returns>

public:

    static IPEndPoint^ GetEndPoint(array<Byte>^ sockaddrBytes)

    {

        IPEndPoint^ unpackedEndpoint = nullptr;

        IPAddress^ unpackedAddress;

        unsigned short addressFamily, unpackedPort;

 

        // Reconstruct the 16-bit (short) value representing the address family    

        addressFamily = BitConverter::ToUInt16(sockaddrBytes, 0);

 

        if (addressFamily == 2)   // AF_INET

        {

            array<Byte>^ addressBytes = gcnew array<Byte>(4);

 

            unpackedPort = BitConverter::ToUInt16(sockaddrBytes, 2);

            unpackedAddress = gcnew IPAddress(BitConverter::ToUInt32(sockaddrBytes, 4));

            unpackedEndpoint = gcnew IPEndPoint(unpackedAddress, unpackedPort);

        }

        else if (addressFamily == 23)     // AF_INET6

        {

            array<Byte>^ addressBytes = gcnew array<Byte>(16);

            unpackedPort = BitConverter::ToUInt16(sockaddrBytes, 2);

            Array::Copy(sockaddrBytes, 8, addressBytes, 0, 16);

            unpackedAddress = gcnew IPAddress(addressBytes);

            unpackedEndpoint = gcnew IPEndPoint(unpackedAddress, unpackedPort);

        }

        else

        {

            Console::WriteLine("GetEndPoint: Unknown address family: {0}", addressFamily);

        }

        return unpackedEndpoint;

    }

};

 

Next, add the abstract class, ProtocolHeader.

 

public ref class ProtocolHeader abstract

{

    /// <summary>

    /// This abstracted method returns a byte array that is the protocol

    /// header and the payload. This is used by the BuildPacket method

    /// to build the entire packet which may consist of multiple headers

    /// and data payload.

    /// </summary>

    /// <param name="payLoad">The byte array of the data encapsulated in this header</param>

    /// <returns>A byte array of the serialized header and payload</returns>

public:

    virtual array<Byte>^ GetProtocolPacketBytes(array<Byte>^ payLoad) abstract;

 

    /// <summary>

    /// This method builds the entire packet to be sent on the socket. It takes

    /// an ArrayList of all encapsulated headers as well as the payload. The

    /// ArrayList of headers starts with the outermost header towards the

    /// innermost. For example when sending an IPv4/UDP packet, the first entry

    /// would be the IPv4 header followed by the UDP header. The byte payload of

    /// the UDP packet is passed as the second parameter.

    /// </summary>

    /// <param name="headerList">An array list of all headers to build the packet from</param>

    /// <param name="payLoad">Data payload appearing after all the headers</param>

    /// <returns>Returns a byte array representing the entire packet</returns>

public:

    array<Byte>^ BuildPacket(ArrayList^ headerList, array<Byte>^ payLoad)

    {

        ProtocolHeader^ protocolHeader;

        array<Byte>^ newPayload = nullptr;

 

        // Traverse the array in reverse order since the outer headers may need

        //    the inner headers and payload to compute checksums on.

        for (int i = headerList->Count - 1; i >= 0; i--)

        {

            protocolHeader = (ProtocolHeader^)headerList[i];

            newPayload = protocolHeader->GetProtocolPacketBytes(payLoad);

            // The payLoad for the next iteration of the loop is now any

            //    encapsulated headers plus the original payload data.

            payLoad = newPayload;

        }

 

        return payLoad;

    }

 

    /// <summary>

    /// This is a simple method for computing the 16-bit one's complement

    /// checksum of a byte buffer. The byte buffer will be padded with

    /// a zero byte if an uneven number.

    /// </summary>

    /// <param name="payLoad">Byte array to compute checksum over</param>

    /// <returns></returns>

public:

    static unsigned short ComputeChecksum(array<Byte>^ payLoad)

    {

        unsigned int xsum = 0;

        unsigned short shortval = 0, hiword = 0, loword = 0;

 

        // Sum up the 16-bits

        for (int i = 0; i < payLoad->Length / 2; i++)

        {

            hiword = (unsigned short)(((unsigned short)payLoad[i * 2]) << 8);

            loword = (unsigned short)payLoad[(i * 2) + 1];

            shortval = (unsigned short)(hiword | loword);

            xsum = xsum + (unsigned int)shortval;

        }

        // Pad if necessary

        if ((payLoad->Length % 2) != 0)

        {

            xsum += (unsigned int)payLoad[payLoad->Length - 1];

        }

 

        xsum = ((xsum >> 16) + (xsum & 0xFFFF));

        xsum = (xsum + (xsum >> 16));

        shortval = (unsigned short)(~xsum);

 

        return shortval;

    }

 

    /// <summary>

    /// Utility function for printing a byte array into a series of 4 byte hex digits with

    /// four such hex digits displayed per line.

    /// </summary>

    /// <param name="printBytes">Byte array to display</param>

public:

    static void PrintByteArray(array<Byte>^ printBytes)

    {

        int index = 0;

 

        while (index < printBytes->Length)

        {

            for (int i = 0; i < 4; i++)

            {

               if (index >= printBytes->Length)

               break;

 

                for (int j = 0; j < 4; j++)

                {

                    if (index >= printBytes->Length)

                        break;

                    Console::Write("{0}", printBytes[index++].ToString("x2"));

                }

                    Console::Write(" ");

            }

               Console::WriteLine("");

        }

    }

};

 

 

 

Then add a derived class, Ipv4Header. This is IPv4 definition.

 

/// <summary>

/// This is the IPv4 protocol header.

/// </summary>

public ref class Ipv4Header : ProtocolHeader

{

private:

    Byte ipVersion;               // actually only 4 bits

    Byte ipLength;                // actually only 4 bits

    Byte ipTypeOfService;

    unsigned short ipTotalLength;

    unsigned short ipId;

    unsigned short ipOffset;

    Byte ipTtl;

    Byte ipProtocol;

    unsigned short ipChecksum;

    IPAddress^ ipSourceAddress;

    IPAddress^ ipDestinationAddress;

public:

    static int Ipv4HeaderLength = 20;

 

    /// <summary>

    /// Simple constructor that initializes the members to zero.

    /// </summary>

public:

     Ipv4Header()

    {

        ipVersion = 4;

        ipLength = (Byte)Ipv4HeaderLength;    // Set the property so it will convert properly

        ipTypeOfService = 0;

        ipId = 0;

        ipOffset = 0;

        ipTtl = 1;

        ipProtocol = 0;

        ipChecksum = 0;

        ipSourceAddress = IPAddress::Any;

        ipDestinationAddress = IPAddress::Any;

    }

 

    /// <summary>

    /// Gets and sets the IP version. This should be 4 to indicate the IPv4 header.

    /// </summary>

public:

    property Byte Version

    {

        Byte get()

        {

            return ipVersion;

        }

        void set(Byte value)

        {

            ipVersion = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the length of the IPv4 header. This property takes and returns

    /// the number of bytes, but the actual field is the number of 32-bit DWORDs

    /// (the IPv4 header is a multiple of 4-bytes).

    /// </summary>

public:

    property Byte Length

    {

        Byte get()

        {

            return (Byte)(ipLength * 4);

        }

        void set(Byte value)

        {

            ipLength = (Byte)(value / 4);

        }

    }

 

    /// <summary>

    /// Gets and sets the type of service field of the IPv4 header. Since it

    /// is a byte, no byte order conversion is required.

    /// </summary>

public:

    property Byte TypeOfService

    {

        Byte get()

        {

            return ipTypeOfService;

        }

        void set(Byte value)

        {

            ipTypeOfService = value;

        }

    }

 

    /// <summary>

    ///  Gets and sets the total length of the IPv4 header and its encapsulated

    ///  payload. Byte order conversion is required.

    /// </summary>

public:

    property unsigned short TotalLength

    {

        unsigned short get()

        {

            return (unsigned short)IPAddress::NetworkToHostOrder((short)ipTotalLength);

        }

        void set(unsigned short value)

        {

            ipTotalLength = (unsigned short)IPAddress::HostToNetworkOrder((short)value);

        }

    }

 

    /// <summary>

    /// Gets and sets the ID field of the IPv4 header. Byte order conversion is required.

    /// </summary>

    public:

    property unsigned short Id

    {

        unsigned short get()

        {

            return (unsigned short)IPAddress::NetworkToHostOrder((short)ipId);

        }

        void set(unsigned short value)

        {

             ipId = (unsigned short)IPAddress::HostToNetworkOrder((short)value);

        }

    }

 

    /// <summary>

    /// Gets and sets the offset field of the IPv4 header which indicates if

    /// IP fragmentation has occurred.

    /// </summary>

    public:

    property unsigned short Offset

    {

        unsigned short get()

        {

            return (unsigned short)IPAddress::NetworkToHostOrder((short)ipOffset);

        }

        void set(unsigned short value)

        {

            ipOffset = (unsigned short)IPAddress::HostToNetworkOrder((short)value);

        }

    }

 

    /// <summary>

    /// Gets and sets the time-to-live (TTL) value of the IP header. This field

    /// determines how many router hops the packet is valid for.

    /// </summary>

    public:

    property Byte Ttl

    {

        Byte get()

        {

            return ipTtl;

        }

        void set(Byte value)

        {

            ipTtl = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the protocol field of the IPv4 header. This field indicates

    /// what the encapsulated protocol is.

    /// </summary>

    public:

    property Byte Protocol

    {

        Byte get()

        {

            return ipProtocol;

        }

        void set(Byte value)

        {

            ipProtocol = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the checksum field of the IPv4 header. For the IPv4 header, the

    /// checksum is calculated over the header and payload. Note that this field isn't

    /// meant to be set by the user as the GetProtocolPacketBytes method computes the

    /// checksum when the packet is built.

    /// </summary>

    public:

    property unsigned short Checksum

    {

        unsigned short get()

        {

             return (unsigned short)IPAddress::NetworkToHostOrder((short)ipChecksum);

        }

        void set(unsigned short value)

        {

             ipChecksum = (unsigned short)IPAddress::HostToNetworkOrder((short)value);

        }

    }

 

    /// <summary>

    /// Gets and sets the source IP address of the IPv4 packet. This is stored

    /// as an IPAddress object which will be serialized to the appropriate

    /// byte representation in the GetProtocolPacketBytes method.

    /// </summary>

    public:

    property IPAddress^ SourceAddress

    {

        IPAddress^ get()

        {

            return ipSourceAddress;

        }

        void set(IPAddress^ value)

        {

            ipSourceAddress = value;

        }

    }

 

    /// <summary>

    /// Gets and sets the destination IP address of the IPv4 packet. This is stored

    /// as an IPAddress object which will be serialized to the appropriate byte

    /// representation in the GetProtocolPacketBytes method.

    /// </summary>

    public:

    property IPAddress^ DestinationAddress

    {

        IPAddress^ get()

        {

            return ipDestinationAddress;

        }

        void set(IPAddress^ value)

        {

            ipDestinationAddress = value;

        }

    }

 

    /// <summary>

    /// This routine creates an instance of the Ipv4Header class from a byte

    /// array that is a received IGMP packet. This is useful when a packet

    /// is received from the network and the header object needs to be

    /// constructed from those values.

    /// </summary>

    /// <param name="ipv4Packet">Byte array containing the binary IPv4 header</param>

    /// <param name="bytesCopied">Number of bytes used in header</param>

    /// <returns>Returns the Ipv4Header object created from the byte array</returns>

    public:

    static Ipv4Header^ Create(array<Byte>^ ipv4Packet, int bytesCopied)

    {

        Ipv4Header^ ipv4Header = gcnew Ipv4Header();

        // Make sure byte array is large enough to contain an IPv4 header

        if (ipv4Packet->Length < Ipv4Header::Ipv4HeaderLength)

            return nullptr;

 

        // Decode the data in the array back into the class properties

        ipv4Header->ipVersion = (Byte)((ipv4Packet[0] >> 4) & 0xF);

        ipv4Header->ipLength = (Byte)(ipv4Packet[0] & 0xF);

        ipv4Header->ipTypeOfService = ipv4Packet[1];

        ipv4Header->ipTotalLength = BitConverter::ToUInt16(ipv4Packet, 2);

        ipv4Header->ipId = BitConverter::ToUInt16(ipv4Packet, 4);

        ipv4Header->ipOffset = BitConverter::ToUInt16(ipv4Packet, 6);

        ipv4Header->ipTtl = ipv4Packet[8];

        ipv4Header->ipProtocol = ipv4Packet[9];

        ipv4Header->ipChecksum = BitConverter::ToUInt16(ipv4Packet, 10);

 

        ipv4Header->ipSourceAddress = gcnew IPAddress(BitConverter::ToUInt32(ipv4Packet, 12));

        ipv4Header->ipDestinationAddress = gcnew IPAddress(BitConverter::ToUInt32(ipv4Packet, 16));

        bytesCopied = ipv4Header->Length;

        return ipv4Header;

    }

 

    /// <summary>

    /// This routine takes the properties of the IPv4 header and marshalls them into

    /// a byte array representing the IPv4 header that is to be sent on the wire.

    /// </summary>

    /// <param name="payLoad">The encapsulated headers and data</param>

    /// <returns>A byte array of the IPv4 header and payload</returns>

    public:

    virtual array<Byte>^ GetProtocolPacketBytes(array<Byte>^ payLoad) override

    {

        array<Byte>^ ipv4Packet;

        array<Byte>^ byteValue;

        int index = 0;

 

        // Allocate space for the IPv4 header plus payload

        ipv4Packet = gcnew array<Byte>(Ipv4HeaderLength + payLoad->Length);

        ipv4Packet[index++] = (Byte)((ipVersion << 4) | ipLength);

        ipv4Packet[index++] = ipTypeOfService;

 

        byteValue = BitConverter::GetBytes(ipTotalLength);

        Array::Copy(byteValue, 0, ipv4Packet, index, byteValue->Length);

        index += byteValue->Length;

 

        byteValue = BitConverter::GetBytes(ipId);

        Array::Copy(byteValue, 0, ipv4Packet, index, byteValue->Length);

        index += byteValue->Length;

 

        byteValue = BitConverter::GetBytes(ipOffset);

        Array::Copy(byteValue, 0, ipv4Packet, index, byteValue->Length);

        index += byteValue->Length;

 

        ipv4Packet[index++] = ipTtl;

        ipv4Packet[index++] = ipProtocol;

        ipv4Packet[index++] = 0; // Zero the checksum for now since we will

        ipv4Packet[index++] = 0; // calculate it later

 

        // Copy the source address

        byteValue = ipSourceAddress->GetAddressBytes();

        Array::Copy(byteValue, 0, ipv4Packet, index, byteValue->Length);

        index += byteValue->Length;

 

        // Copy the destination address

        byteValue = ipDestinationAddress->GetAddressBytes();

        Array::Copy(byteValue, 0, ipv4Packet, index, byteValue->Length);

        index += byteValue->Length;

 

        // Copy the payload

        Array::Copy(payLoad, 0, ipv4Packet, index, payLoad->Length);

        index += payLoad->Length;

 

        // Compute the checksum over the entire packet (IPv4 header + payload)

        Checksum = ComputeChecksum(ipv4Packet);

 

        // Set the checksum into the built packet

        byteValue = BitConverter::GetBytes(ipChecksum);

        Array::Copy(byteValue, 0, ipv4Packet, 10, byteValue->Length);

 

        return ipv4Packet;

    }

};

 

 

 


 

< Socket, Bind, Connect, Send, Receive etc. | Main | Adding Ipv6 & Ipv6 Fragment C++ Header Definition Classes 2 >