Click here to Skip to main content
15,884,473 members
Articles / Desktop Programming / MFC
Article

Peer2Peer socket sample

Rate me:
Please Sign up or sign in to vote.
2.74/5 (11 votes)
30 Dec 20042 min read 88K   6.3K   61   8
A fully error handled and easy to use peer2peer2 library.

Sample Image

Introduction

Peer-2-peer socket communication is the base part of every network application and I want to develop an easy to use and fully error handled library for it.

Using the code

For using this, you must do the following steps:

  1. Make an instance of CP2PConnection class:
    CP2PConnection p2p;
  2. Create a static function in your class with this prototype:
    static void CALLBACK NotifyProc(LPVOID lpParam, CString LogMsg, 
                                     CString LogMsgDes, UINT nCode); 

    Call this function when any error or event occurs. When an error or event occurs, call this function with ncode == 0, and when you receive a message from peer application library, call this function with ncode == 1.

  3. Initialize library on constructor by:
    p2p(NotifyProc,this)
  4. Call connect() or bind() to create connection.

How the library works

This library consists of four classes (CBuffer, CLock, CP2PConnection, CListenSocket). CBuffer is a class that works as storage; you can read and write some data into this buffer without worrying about memory management. CLock is a wrapper class for CRITICAL_SECTION. The main classes of the library are CP2PConnection, CListenSocket.

CListenSocket is a listener thread that listens at a port and accepts new connection. Because our program is p2p, we can accept just one connection, and accepting new connection causes closing the old connection if it exists.

CP2PConnection keeps connection state and changes the connection state when different events or errors occur for socket. The errors and events that may occur for Connect are:

#define NETEVENT_ERROR 100  //network error
#define READ_ERROR 101 //read error
#define CONNECT_ERROR 102 //connection failed
#define CLOSE_ERROR 103 //connection closed
#define WRITE_ERROR 104 //write error
#define CONNON_ERROR 105 //socket error
#define CONNECT_EVENT 0  // connecting event
#define ACCEPT_EVENT 1    //accepting event
#define CLOSE_EVENT 2    //normal closing socket event

I use these events and errors and change state according to them.

void CP2PConnection::StateChange(int NewCondition,char * log)
{
    CLock cs(m_statecs, "CP2PConnection::StateChange" );

    if(log) ReportError(log);

    switch(m_state)
    {
        ////////////////////////////////////////////////////////////////
    case not_connect:  //in this state key is off and mouse isn't over the key
        {
            switch(NewCondition)
            {
            case CONNECT_EVENT:
                {
                    m_Active_Sock=m_Connect_Sock;
                    m_state=estab_connect;
                    InitSocketThread();
                }
                break;
            case ACCEPT_EVENT:
                {
                    m_Active_Sock=m_ClistenSock.clientSocket;
                    m_state=estab_accept;
                    InitSocketThread();
                }
                break;
            default:
                m_Active_Sock=NULL;
                break;
            }
        }
        break;
        //////////////////////////////////////////////////////////////////
    case estab_connect:
        {
            switch(NewCondition)
            {
            case CONNECT_EVENT:
                break;
            case ACCEPT_EVENT:
                {
                    stop();
                    m_Active_Sock=m_ClistenSock.clientSocket;
                    m_state=estab_accept;
                    InitSocketThread();
                }
                break;
            case NETEVENT_ERROR:
            case READ_ERROR:
            case CONNECT_ERROR:
            case CLOSE_ERROR:
            case CLOSE_EVENT:
            case WRITE_ERROR:

                {
                    stop();
                    m_state=not_connect;
                    m_Active_Sock=NULL;
                }
                break;
            }
        }
        break;
        //////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////
    case estab_accept:
        {
            switch(NewCondition)
            {
            case CONNECT_EVENT:
                {
                    stop();
                    m_Active_Sock=m_Connect_Sock;
                    m_state=estab_connect;
                    InitSocketThread();
                }
                break;
            case ACCEPT_EVENT:
                {
                    stop();
                    m_Active_Sock=m_ClistenSock.clientSocket;
                    m_state=estab_accept;
                    InitSocketThread();
                }
                break;
            case NETEVENT_ERROR:
            case READ_ERROR:
            case CONNECT_ERROR:
            case CLOSE_ERROR:
            case CLOSE_EVENT:
            case WRITE_ERROR:
            case CONNON_ERROR:
            //case default:
                {
                    stop();
                    m_state=not_connect;
                    m_Active_Sock=NULL;
                }
                break;
            }
        }
        break;
        //////////////////////////////////////////////////////////////////
    }

}

I use a simple format for messages, that is:

messagelen(int),message body

As I explained in my previous article, receiver must take care about messages that it receives form the network because some messages may be split into two or more network packets. So I use an intermediate buffer and store all arrived messages at that buffer. This buffer then evaluates packets and extract messages from it.

bool CP2PConnection::OnRead()
{
//    CLock cs(m_statecs, "CP2PConnection::OnRead" );
    DWORD dwSize = 0;
    int nRet = ioctlsocket(m_Active_Sock, FIONREAD, &dwSize);
    if (nRet == -1)
        return true;

    if (dwSize == 0)
        return false;


    BYTE* pData = new BYTE[dwSize+1];

    int nRead = recv(m_Active_Sock, (char*) pData, dwSize, 0);
    if (nRead == -1)
        return true;


    m_recvBuff.Write(pData, nRead);
    delete [] pData;

    while (m_recvBuff.GetBufferLen() > (sizeof(int)*1))
    {
        int nSize = 0;
        int nCommand = 0;
        int nPageId = 0;
        CopyMemory(&nSize, m_recvBuff.GetBuffer(), sizeof(int));


        if (nSize && m_recvBuff.GetBufferLen() >= (UINT) nSize)
        {
            // Read off header
            m_recvBuff.Read((PBYTE) &nSize, sizeof(int));

        ////////////////////////////////////////////////////////
            ////////////////////////////////////////////////////////
            // SO you would process your data here
            //
            // I'm just going to post message so we can see the data
        //    nSize -= sizeof(int);

            int nTrueSize = nSize+1;
            PBYTE pData = new BYTE[nTrueSize];

            CString str;

            m_recvBuff.Read(pData,nSize);
            pData[nTrueSize-1] =  NULL;
            str = pData;
            //::PostMessage(m_hWnd, WM_REC_MSG, 0, (LPARAM) AllocBuffer(str));
        //    AfxGetApp()->GetMainWnd()->SetWindowText(str);
            m_pNotifyProc(m_pNotifyProcCalss,str,(CString)"",1 );
    //        Sleep(1000);
            //((CScoketApp *)AfxGetApp())->DataReceived(str);

            // Clean Up
            delete [] pData;
        }
        else
            break;
    }

    return false;

}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Engineer neyshabur azad univeristy
Iran (Islamic Republic of) Iran (Islamic Republic of)
I had worked as programmer,project manager,web developer for more than 3 years, and have worked on programming personaly for more than 10 years.
I have worked in university as a teacher for 8 years.
my favorite langueges are : VC++,C#,ASP.NET,PHP

Comments and Discussions

 
QuestionNot able to use it Pin
ns_rana1313-Mar-13 0:31
ns_rana1313-Mar-13 0:31 
Questionexciting! Pin
yanghongbing31-May-12 5:59
yanghongbing31-May-12 5:59 
Questioncan not to run Pin
miki2343-May-09 19:37
miki2343-May-09 19:37 
QuestionHow to use it? Pin
lsf_200829-Aug-07 21:04
lsf_200829-Aug-07 21:04 
GeneralThanks Pin
braveboy11-Apr-07 20:02
braveboy11-Apr-07 20:02 
GeneralRe: Thanks Pin
Member 1397740613-Feb-19 12:53
Member 1397740613-Feb-19 12:53 
Generalnot easy to use Pin
horse199710-Sep-06 18:02
horse199710-Sep-06 18:02 
GeneralUnicode Pin
thelvaci27-Feb-06 20:23
thelvaci27-Feb-06 20:23 
Is it possible to make this code unicode aware?



Tahir Helvaci

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.