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:
- Make an instance of
CP2PConnection
class:
CP2PConnection p2p;
- 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.
- Initialize library on constructor by:
p2p(NotifyProc,this)
- 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:
{
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:
{
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()
{
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)
{
m_recvBuff.Read((PBYTE) &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;
m_pNotifyProc(m_pNotifyProcCalss,str,(CString)"",1 );
delete [] pData;
}
else
break;
}
return false;
}
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