Developers.Code.CG1Neighbour

From Shareaza Wiki
Jump to navigation Jump to search

CG1Neighbour

Here is the inheritance tree for this part of the Shareaza code:

File:Neighbour.gif

The class CConnection holds the socket. CNeighbour inherits from it, adding lots of member variables we haven't seen used yet. And CG1Neighbour inherits from it.

Constructor and Destructor

The constructor for CG1Neighbour is declared like this:

<source lang="c"> CG1Neighbour::CG1Neighbour(CNeighbour* pBase) : CNeighbour( PROTOCOL_G1, pBase ) </source>

The part after the single colon, CNeighbour( PROTOCOL_G1, pBase ) means that before the code here runs, the computer should call the CNeighbour constructor. CG1Neighbour inherits from CNeighbour, so this call will setup the CNeighbour core of the new object we are making. Once that's done, control returns here. The computer runs the lines of code here to setup the CG1Neighbour-specific part of the new object.

pBase is a CNeighbour object to base this one upon. The constructor copies all the values it can from pBase, as this new object is a replacement for that old one. What happened was, we were talking to a new remote computer that just connected with a CNeighbour object. Then, we figured out that it wants to talk Gnutella. So, now we need a CG1Neighbour object to talk Gnutella with it. To make one, the program calls this constructor, and passes it the old one - pBase. As soon as all the values are copied in, the part of the program that made the new CG1Neighbour object will delete pBase.

Both the CNeighbour and CG1Neighbour constructors need pBase to copy values from it. We also pass the CNeighbour constructor the protocol identifier PROTOCOL_G1. This just means we know the remote computer is running Gnutella software, so please setup the CNeighbour core of our new CG1Neighbour object based on that information.

The code in the CG1Neighbour constructor here sets up the CG1Neighbour-specific part of the new object. This includes zeroing the 32 bytes of the m_nPongNeeded array. It includes making a new CG1PacketBuffer called m_pOutbound which will have us send the remote computer packets in bursts even if the program sends them one at a time.

The constructor sends the remote computer a ping packet. If the remote computer told us it supports vendor-specific messages, and program settings allow them, the constructor also sends a very strange vendor-specific message. It contains vendor codes like BEAR for BearShare and RAZA for Shareaza. The text characters are written backwards in the code so the 4 bytes they take up will be in network order when they are sent.

The constructor allocated memory with m_pOutbound = new CG1PacketBuffer. So, the destructor has to see if m_pOutbound points to something, and delete it if it does.

OnRead and OnWrite

CG1Neighbour inherits from CNeighbour, which in turn inherits from CConnection. All three of these classes define members named OnRead and OnWrite. At the start of the OnRead method in CG1Neighbour, code calls CNeighbour's OnRead. And at the start there, code calls CConnection::OnRead. So, when the program calls CG1Neighbour::OnRead, code from the following methods runs in this order:

CConnection::OnRead CNeighbour::OnRead CG1Neighbour::OnRead

Here's what each of these methods do:

CConnection::OnRead reads in data from the remote computer waiting on our end of the socket CNeighbour::OnRead decompresses it CG1Neighbour::OnRead processes the packets it contains

The same way, but backwards, a call to CG1Neighbour::OnWrite causes the computer to execute code from these methods in this order:

CG1Neighbour::OnWrite gets packets from the outbound packet buffer, and puts them in the buffer to send CNeighbour::OnWrite compresses the bytes of that buffer CConnection::OnWrite sends the compressed bytes into the socket

I don't agree with this design. CConnection does sockets and CNeighbour does compression, they should have names like CSocket and CCompress. Also, having a bunch of methods all with the same name call each other is really confusing. But, for now, that's how it works.

OnRun

OnRun is another method that appears in many classes. Here's how the OnRun in CG1Neighbour gets called. Look at this code from CConnection::DoRun:

<source lang="c"> BOOL CConnection::DoRun() {

(...near the bottom...)

   // Make sure the handshake doesn't take too long
   if ( ! OnRun() ) return FALSE; // If this is a CShakeNeighbour object, calls CShakeNeighbour::OnRun

</source>

CConnection, CNeighbour and CG1Neighbour all have methods called OnRun. When a method in CConnection calls OnRun without specifying which one it wants, control passes to the one the highest up the inheritance tree. So, if a CShakeNeighbour object calls CConnection::DoRun, the OnRun here will call CShakeNeighbour::OnRun. If a CG1Neighbour object calls CConnection::DoRun, the OnRun here will call CG1Neighbour::OnRun. That's how the OnRun method gets called.

Here are some OnRun methods and what they do:

CG1Neighbour::OnRun calls CNeighbour::OnRun and sends a ping packet CNeighbour::OnRun makes sure the remote computer hasn't been silent too long, and sends a query patch table CShakeNeighbour::OnRun makes sure the handshake hasn't been taking too long CConnection::OnRun does nothing

Send

Send takes a packet, adds it to the outbound packet buffer, and sends everything to the remote computer. There are two optional arguments. BOOL bRelease is TRUE by default, and causes the method to call Release on the packet after we send it. BOOL bBuffered is FALSE by default and just gets passed to m_pOutbound->Add.

ProcessPackets

Several methods in this class start with code like this:

<source lang="c"> // Point pInput at the buffer that has readable data from the remote computer CBuffer* pInput = m_pZInput ? m_pZInput : m_pInput; </source>

If the remote computer isn't sending us compressed data, the program will put the readable bytes in m_pInput. It's far more likely, however, that the remote computer supports compression and is sending us everything compressed. In that case, m_pZInput will point to a buffer. The program still puts the data the remote computer sends into m_pInput. CNeighbour::OnRead then has the job of decompressing the data into m_pZInput. So, even though m_pZInput has a Z in it, it's contents are not compressed. This line of code points pInput at the buffer where we can find the readable, not compressed, bytes from the remote computer regardless of whether they arrived compressed or not.

CG1Neighbour::OnRead reads all the data from the socket, decompresses it, and then calls ProcessPackets. The method contains a loop. Each time the loop runs, code looks at the start of the input buffer as though it were a Gnutella packet, processes this packet, and removes it from the start of the buffer. This is the code in the loop which does that:

<source lang="c"> // Process the packet CG1Packet* pPacketObject = CG1Packet::New( pPacket ); // Look at the buffer as a CG1Packet bSuccess = OnPacket( pPacketObject ); // Send it to OnPacket, and get the result pPacketObject->Release(); // Call release on the packet

// Remove the bytes of the packet from the start of the buffer pInput->Remove( nLength ); </source>

So, ProcessPackets processes all the packets, cutting them apart and handing each one to OnPacket.

OnPacket

OnPacket counts the packet, lowers a too-high time to live count, and then sorts it. If it's a ping packet, OnPacket hands it to OnPing and returns the result from OnPing. All the remaining classes in CG1Neighbour are specific to a type of Gnutella packet.