Sunday, July 22, 2012

Sending file through UDP Socket

1 comment
Introduction
(THIS is a repost, original post can be found on DNX Group blog)
User Datagram Protocol or UDP is a simple protocol. This protocol is unreliable - when a data packet is sent, it cannot be not known if it will reach its destination or not. There is no acknowledgement, retransmission, or timeout. When multiple data is sent at once, the order which they arrive cannot be predicted. They might be out of order. The data that is sent with sequence 1,2,3 might be recieved as 2,3,1. So, sending files through UDP socket is a bit difficult.



Normally in a realtime game such as FPS, UDP protocol is used to send player location and behaviour. This data will be send continuously to provide a realtime player action to other player. But when the data is not arrived, the data will be skipped without retransmission so that the gameplay will be realtime, and not delayed. When sending files, a new protocol must be constructed as the file data must be sent completely. It cannot be skipped as it will corrupt the file.

About This Project
This is a simple project to demonstrate sending a file through UDP Socket. The code here might not perfect and may contains several flaws and bugs which might prevent the file from being sent perfectly.

Download Project file + Demo

UDP
To start this project, a simple UDP socket class must be constructed to assists sending and receiving data packet through UDP socket. This class will be named CSockUDP and will contain this method:
class CSockUDP
{
public:
 static int InitWinsock();
 static void DestroyWinsock();

 void SetCallback(cbDataRecieved fnRecieved);
 int CreateSocket(int nPort = 31313);

 void StartListen();
 void StopListen();

 bool SetServerAddress(char* szServer, int nPort);
 int SendData(LPVOID lpData, int nSize);
 int ResendData();
}

InitWinsock() and DestroyWinsock() is a call to WinsockAPI to initiates and stop the use of Winsock Dll. It must be called on very early when the program is started and on the very end before the program terminated.

A call to CreateSocket() will create a new UDP socket and assign it with a port number. Then, SetCallback() is called to set receive event function. StartListen() will create a new thread which will executes a loop code and listen for any incoming data. Other method is self explanation.

----

To send files through UDP socket, firstly a protocol must be constructed. Every data packet that is sent must have some sort of acknowledgement and identification so the receiver can understand what is it and what should it do. We can start by creating a data header followed by the data itself.

struct TUDPHeader
{
 char szID[3]; // 'IKH' - unique id
 DWORD dwCRC32; // Checksum of Data
 char type;  // eDataType
 int nLength; // Data length

 // Data (TUDPFileHeader/TUDPFileReply/TUDPFileTransfer)
};
Inside this header, szID is an identification. It will contain unique data, "IKH" in this case and send it to the receiver. When the receiver read the data and found that the first 3 bytes is equal to "IKH", it will continue read the packet but when it different, the packet will simply be skipped as it might come from other software that is also sending a data to the same port.

dwCRC32 will contain a data checksum (not including TUDPHeader). The sender will calculate the data checksum and fill dwCRC32 with it. When it arrives at destination, the receiver will calculate the data checksum again and compare it with dwCRC32. If the checksum calculated by receiver differ from what is sent by sender, it will indicates that the data is corrupted or altered before its reach its destination. If this happen, the receiver will request that the data must be sent back again.

type explains to the receiver what must it do with the data.

enum eDataType
{
 eDataTypeConnect,   // ping
 eDataTypeConnected,   // Reply if server got connect message
 eDataTypeResend,   // Corrupted data, request for resend
 eDataTypeQuit,    // Exit the program, client and server

 eDataTypeFileHeader,  // TUDPFileHeader
 eDataTypeFileTransfer,  // TUDPFileTransfer

 eDataTypeFileReplyAccept, // Accept the file send request
 eDataTypeFileReplyReject, // Denied the file send request
 eDataTypeFileReplyOK,  // Proceed to next stream
 eDataTypeFileReplyFinish // Finished transfer
};

Lastly inside this header (TUDPHeader), is nLengthnLength will tell how big is the data sent by sender. Without it, the receiver does not know when the data end.

----

When a file is being sent, a packet is constructed from TUDPHeader followed by TUDPFileHeader. It will send a file name followed by size of the file in bytes.

struct TUDPFileHeader
{
 char szFileName[128];
 int nFileSize;
};

After that, receiver will reply either to accept the file or reject it like we can see in eDataTypeeDataTypeFileReplyAccept/eDataTypeFileReplyReject. If the file is accepted, the receiver will create a new file and the sender will immediately send the file data using TUDPFileTransfer which contain file offset, data size and the file data itself.

struct TUDPFileTransfer
{
 int nFileOffset;
 int nLength;
 char szData[2048];
};

The receiver then writes the newly created file with the data its received in TUDPFileTransfer until the file completed.

Example of sending data packet with TUDPHeader + TUDPFileHeader

Artikel ini juga boleh didapati dalam Bahasa Melayu: Klik disini

1 comment :