CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 1 of 1
  1. #1
    Join Date
    Feb 2003
    Location
    Bangalore, India
    Posts
    1,354

    Visual C++ Network: How do I transfer structure?

    Q: How do I transfer structure?
    Q: How do I impose a packet scheme?

    A:Even though TCP/IP is all about sending data, it is often, in reality, necessary to maintain a few information about the data that is being passed. It is common therefore, to have a structure that contains such information and passed as a header to the data that is being send. There are a number of ways to transfer a structure over a TCP connection using Winsock.

    Using 'struct and 'union' is one of the easiest ways to achieve this and provides a number of benefits. There is no extra copying, formatting or casting involved. You are basically sending and receiving strings. Moreover, one can send different headers depending on a variable value. This method is explained in the following section.

    We'll take a look at a typical packet header.

    Code:
    struct PACKET_HEADER
    {
      short         sCode;      // => 2 bytes (the header type or protocol)
      short         sService;   // => 2 bytes (the service requested/packet type)
      unsigned long ulHandle;   // => 4 bytes (the client/connection handle) 
      unsigned long ulLen;      // => 4 bytes (the data len)
    };
    Though most of the fields are self-explanatory, the first field 'sCode' needs a bit of explanation. This field indicates the type of header that we are using. For instance, there can be 2 headers, the one that is shown above, to communicate to the client, and another one that communicates between servers. If we are using variable headers, then at least the first field must be unique. Choosing the correct header depends on the value of this initial field, in our case 'sCode.

    The size of the packet header is fixed unlike the packet size, which can be unique or variable in length.

    Code:
    struct PACKET
    {
      PACKET_HEADER pktHdr;        // =>   12 bytes 
      char          sData[1012];   // => 1012 bytes
                                   // total: 1024 bytes (* See note)
    };
    A packet is shown above. Other than the packet header, the only other element it contains is a character array to hold the data.

    Code:
    union DataPacket
    {
      PACKET pkt;
      char   pktStr[1024];
    };
    The above code shows the packet is unioned with a character array which is the same size as that of the packet (*see note). As you know, the elements of the 'union' occupy the same memory space, with each element aligned on the least significant bit. So in this case both the elements occupy the same memory space.

    Then we can actually define some services:

    Code:
    enum Service
    {
      ACK,     // Acknoledgement
      FRAG,    // Fragment (continuation of the packet that was send previously)
      AUTH,    // Authentiation packet
      REQ1,    // Request 1
      REQ2     // Request 2
    };

    Code:
    //The client part
    int main()
    {
      DataPacket    dp;
      unsigned long ulErr;
    
      // ...
       
      dp.pkt.pktHdr.sCode    = 0;
      dp.pkt.pktHdr.sService = AUTH;
      dp.pkt.pktHdr.ulHandle = 0;
      dp.pkt.pktHdr.ulLen    = 100;
    
      // Copy 100 byte authentication code to dp.pkt.sData
      // sending the authentification packet to the server
      ulErr = send( cliSock,
                      dp.pktStr,
                      sizeof(PACKET_HEADER) + dp.pkt.pktHdr.dwLen,
                      0 );
      if ( ulErr == SOCKET_ERROR )
      {
        // Handle the error using 'WSAGetLastError()'
      }
      
      // continued...
    The above code should be self-explanatory. For simplicity, we assume here that all the data is send and received in one go. However in reality you should never make such an assumption. You can find more information in the following FAQ: Does one call to 'send()' result in one call to 'recv()'?

    The server side may look like this:

    Code:
    // Receiving the AUTH packet from the client
    ulErr = recv( cliSock,
                   dp.pktStr,
                   sizeof(PACKET),
                   0 );
    if ( ulErr == SOCKET_ERROR )
    {
      // Handle the error using 'WSAGetLastError()'
    }
    
    if ( dp.pkt.pktHdr.sService == AUTH )
    {
      // Verify the authentication data
    
      // Send the ACK if verified; else close the socket
    
      dp.pkt.pktHdr.ulHandle = 1;    // issuing a new handle
      dp.pkt.pktHdr.sService = ACK;  // acknowledging the authentication code
      dp.pkt.pktHdr.ulLen    = 0;    // Not sending any data
    
      ulErr = send( cliSock,
                      dp.pktStr,
                      sizeof(PACKET_HEADER) + dp.pkt.pktHdr.ulLen,
                      0 );
      if ( ulErr == SOCKET_ERROR )
      {
        // Handle the error using 'WSAGetLastError()'
      }
    }
    else
    {
      // Client is not authentated
      closesocket( cliSock );
    }
    More explanation to the above code doesn't seem to be necessary. The server that receives the authentification data from the client verifies and then acknowledges it back.

    The code below shows what can be the continuation on the client side.

    Code:
    // Recving the acknowledge packet from the server
    ulErr = recv( cliSock,
                   dp.pktStr,
                   sizeof(PACKET),
                   0 );
    if ( ulErr == SOCKET_ERROR )
    {
      // Handle the error using 'WSAGetLastError()'
    }
    
    if ( dp.pkt.pktHdr.sService == ACK )
    {
      ulCliHandle = dp.pkt.pktHdr.ulHandle;
    }
    else
    {
      // Wrong service code
    }
    
    // ...
    From the above client/server interaction code, it should be clear how structures and unions can be used to pass data and information pertaining to them. Implementing the usage of variable headers won't be difficult once you know the basics. All you need is a different header structure (with a unique first field) and an additional field in the 'union'.

    Note: Another thing you have to keep in mind while dealing with structures is that 'sizeof()' may report a size different from the actual size of all the elements of the structure. This is due to the structure alignment done by the compiler for optimization. You can work around this by using the '#pragma pack(1)'...'#pragma pack()' compiler directive.

    Thanks to Mr. Andreas Masur for his help with this FAQ.


    Last edited by Andreas Masur; July 25th, 2005 at 02:53 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured