Windows Sockets: Example of Sockets Using Archives

This article presents an example of using class CSocket. The example employs CArchive objects to serialize data through a socket. Note that this is not document serialization to or from a file.

The following example illustrates how you use the archive to send and receive data through CSocket objects. The example is designed so that two instances of the application (on the same machine or on different machines on the network) exchange data. One instance sends data, which the other instance receives and acknowledges. Either application can initiate an exchange, and either can act as server or as client to the other application. The following function is defined in the application's view class:

void PacketSerialize(long nPackets, CArchive &arData, CArchive &arAck)
{
   BYTE bValue = 0;
   WORD nCopies = 0;

   if (arData.IsStoring())
   {
      CString strText;
      errno_t err;
      unsigned int number;

      for (int p = 0; p < nPackets; p++)
      {
         err = rand_s(&number);
         // if (err == 0)...
         bValue = (BYTE)(number % 256);

         err = rand_s(&number);
         // if (err == 0)...
         nCopies = (WORD)(number % 32000);

         // Send header information
         arData << bValue << nCopies;
         for (int c = 0; c < nCopies; c++)
         {
            // Send data
            arData << bValue;
         }

         strText.Format(_T("Sender sent packet %d of %d (Value = %d, Copies = %d)"),
                        p + 1, nPackets, (int)bValue, nCopies);

         // Send receipt string
         arData << strText;
         arData.Flush();

         // Receive acknowledgment
         arAck >> strText;
         // display it
         DisplayMessage(strText);
      }
   }
   else
   {
      CString strText;
      BYTE bCheck;

      for (int p = 0; p < nPackets; p++)
      {
         // Receive header information
         arData >> bCheck >> nCopies;
         for (int c = 0; c < nCopies; c++)
         {
            // Receive data
            arData >> bValue;
            if (bCheck != bValue)
            {
               AfxMessageBox(_T("Packet Failure"));
            }
         }

         // Receive receipt string and display it
         arData >> strText;
         DisplayMessage(strText);

         strText.Format(_T("Recipient received packet %d of %d (Value = %d, Copies = %d)"),
                        p + 1, nPackets, (int)bValue, nCopies);

         // Send acknowledgment
         arAck << strText;
         arAck.Flush();
      }
   }
}

The most important thing about this example is that its structure parallels that of an MFC Serialize function. The PacketSerialize member function consists of an if statement with an else clause. The function receives two CArchive references as parameters: arData and arAck. If the arData archive object is set for storing (sending), the if branch executes; otherwise, if arData is set for loading (receiving) the function takes the else branch. For more information about serialization in MFC, see Serialization.

Note

The arAck archive object is assumed to be the opposite of arData. If arData is for sending, arAck receives, and the converse is true.

For sending, the example function loops for a specified number of times, each time generating some random data for demonstration purposes. Your application would obtain real data from some source, such as a file. The arData archive's insertion operator (<<) is used to send a stream of three consecutive chunks of data:

  • A "header" that specifies the nature of the data (in this case, the value of the bValue variable and how many copies will be sent).

    Both items are generated randomly for this example.

  • The specified number of copies of the data.

    The inner for loop sends bValue the specified number of times.

  • A string called strText that the receiver displays to its user.

For receiving, the function operates similarly, except that it uses the archive's extraction operator (>>) to get data from the archive. The receiving application verifies the data it receives, displays the final "Received" message, and then sends back a message that says "Sent" for the sending application to display.

In this communications model, the word "Received", the message sent in the strText variable, is for display at the other end of the communication, so it specifies to the receiving user that a certain number of packets of data have been received. The receiver replies with a similar string that says "Sent", for display on the original sender's screen. Receipt of both strings indicates that successful communication has occurred.

Caution

If you are writing an MFC client program to communicate with established (non-MFC) servers, do not send C++ objects through the archive. Unless the server is an MFC application that understands the kinds of objects you want to send, it won't be able to receive and deserialize your objects. An example in the article Windows Sockets: Byte Ordering shows a communication of this type.

For more information, see Windows Sockets Specification: htonl, htons, ntohl, ntohs. Also, for more information, see:

See also

Windows Sockets in MFC
CArchive::IsStoring
CArchive::operator <<
CArchive::operator >>
CArchive::Flush
CObject::Serialize