Gnucleus에서 다운로드 받는 방법에 대한 설명
~cpp
void CSearchResults::OnButtonDownload()
{
std::list<ResultGroup>::iterator itGroup;
POSITION pos = m_lstResults.GetFirstSelectedItemPosition();
while(pos)
{
int nItem = m_lstResults.GetNextSelectedItem(pos);
ResultGroup* pGroup = (ResultGroup*) m_lstResults.GetItem(nItem);
if(pGroup)
RelayDownload(*pGroup);
}
}
에서 다운로드 버튼을 누르면 결과 그룹중에서 pos를 기억하고 이 pos를 이용해 다운받을
파일을 선택하고 그 그룹의 결과값을
RelayDownload 함수의 전달인자로 보낸다.
~cpp
void CSearchResults::RelayDownload(ResultGroup &Item) 에서는
CGnuDownloadShell* Download = new CGnuDownloadShell(m_pComm);
에서와 같이 새로운 다운로드 쉘을 만들고
~cpp
m_pComm->m_DownloadList.push_back(Download);
와 같이 m_
DownloadList에 Download 객체를 삽입하고
CGnuControl에서 제어하게 만든다.
~cpp
Download->m_Name = Item.Name;
Download->m_Search = ReSearch;
Download->m_FileLength = Item.Size;
Download->m_AvgSpeed = Item.AvgSpeed;
같이 초기화를 한다.
~cpp
// Add hosts to the download list
for(int i = 0; i < Item.ResultList.size(); i++)
Download->AddHost( Item.ResultList[i] );
처럼
DownloadShell에
AddHost함수를 통해 Item의 모든 Host를 다운로드 에 넣는다.
그러면 View 에 Download에 관한 페이지를 업데이트하고
DownloadShell의 생성자가 실행이 된다.
CGnuControl에서의 void
CGnuControl::ManageDownloads()에 의해 제어된다.
void
CGnuDownloadShell::Start() 로 다운로드가 시작이 되고 실제적인 다운로드는
CGnuDownload에서 이루어지면 쉘에서는 Timer()에서
CheckCompletion()로 완료 되었는 지 확인을 하게 되고
AttachChunk2Chunk 와
AttachChunk2Partial로 부분부분 완료된 Chunk들을 결합해 주는 역활을 하게 된다.
실제적으로 하나의 Host마다
CGnuDownload 클래스를 갖게 되며 데이타를 받는 소켓이 된다m_
StartPos가 받기 시작하는 Chunk의 시작을 나타내며
ReadyFile()에서는 전의 받던 파일이 있는 지 조사후에 File을 연다.
StartDownload() 함수에서 다운로드를 시작하며
GetStartPos() 를 통해 Chunk가 다운로드가 시작할 m_pChunk->m_
StartPos의 위치를 알게 한다.
~cpp
m_StartPos = m_pShell->m_BytesCompleted + ((m_pShell->m_FileLength - m_pShell->m_BytesCompleted) / 2);
m_pChunk->StartPos = m_StartPos;
m_pChunk->FileLength = m_pShell->m_FileLength - m_StartPos;
공식으로 Chunk의 시작위치를 구한다.
SendRequest() 에서 HTTP/GET 형식으로 헤더를 보내 받고자 하는 데이타를 요청한다
OnReceive(int nErrorCode) 에서 Content-length 만큼의 버퍼 데이타를 받아 청크와 연결 시킨다.
이어 받기를 할때에는 파일의 끝에서 -4096만큼 얻고 m_Verification 블럭의 4096크기 만큼 비교를 한 후에 이어받기를 시작한다.
새로운 청크를 만드는 조건은 이미 완료된 청크의 남은 부분이
EmptySize > 16384 보다 커야
한다는 것이다.
소스는 다음과 같고
~cpp
// Reset m_pChunk because this can be called multiple times
if(m_pChunk)
{
bool destroy = true;
for(int i = 0; i < m_pShell->m_ChunkList.size(); i++)
if(m_pShell->m_ChunkList[i] == m_pChunk)
destroy = false;
if(destroy)
delete m_pChunk;
m_pChunk = NULL;
}
부분은 소켓의 청크중에 지금의 청크가 없다면 지금 받은 청크는 끝마친 것으로
생각하고 m_pChunk = NULL로 만든다
Gnucleus에서 프로토콜 통신
GnuCreateGuid(&Guid) //
DescriptorID 생성
라우팅시 연결된 모든 nodeList에서 key->Origin를 찾아내어 key->Origin를 제외한 모든 node에 받은 pong 또는 queryHit를 전달
- Search
~cpp
int length = 25 + m_Search.GetLength() + 1;
pNode->SendPacket(m_Packet, length, PACKET_QUERY, true);
- Result Set
~cpp
// Extract results from the packet
while(HitsLeft > 0 && NextPos < Length - 16)
{
Result Item;
memcpy(&TempX, &Packet[NextPos], 4);
Item.FileIndex = makeD( flipX(TempX));
memcpy(&TempX, &Packet[NextPos + 4], 4);
Item.Size = makeD( flipX(TempX));
Item.Host = QueryHit->Host;
Item.Port = QueryHit->Port;
Item.Speed = makeD( flipX(QueryHit->Speed));
Item.Firewall = Firewall;
Item.Busy = Busy;
Item.Stable = Stable;
Item.ActualSpeed = ActualSpeed;
if(ExtendedPacket)
Item.Vendor = Vendor;
Item.Origin = Log->Origin;
memcpy(&Item.PushID, &Packet[Length - 16], 16);
Item.Distance = Log->Header->Hops;
// Get Filename
for(i = NextPos + 8; Packet[i] != 0; i++)
if(i < Length - 16)
Item.Name += (char) Packet[i];
else
break;
// Pass any data between double null
while(Packet[++i] != 0)
if(i > Length - 16)
break;
Item.NameLower = Item.Name;
Item.NameLower.MakeLower();
Item.Icon = m_pDoc->GetIconIndex(Item.NameLower);
m_WholeList.push_back(Item);
// Screen Item to user's preferences
if(Inspect(Item))
{
m_CurrentList.push_back(Item);
m_tabResults->UpdateList( AddtoGroup(Item) );
}
// Check for end of reply packet
if(i + 1>= Length - 16)
HitsLeft = 0;
else
{
HitsLeft--;
NextPos = i + 1;
}
}
패킷의 packet_
QueryHit 에서
ResultSet을 추출하여 Item Vector에 넣어준다.
~cpp
void CGnuNode::Recieve_Ping(packet_Ping* Ping, int nLength)
key_Value* key = m_pComm->m_TableRouting.FindValue(&Ping->Header.Guid);
통해 받았던 핑인지 검사하고 if(key == NULL) 받았던 핑이 아니라 새로운 핑이라면 m_pComm->m_
TableRouting.Insert(&Ping->Header.Guid, this) 처럼 라우팅 테이블에 넣고 Pong을 보내준다.
방화벽이 설치된 경우의 자세한 설명
~cpp
UINT AttemptPort = pPrefs->m_ForcedPort ? pPrefs->m_ForcedPort : pPrefs->m_LocalPort;
AttemptPort += rand() % 99 + 0;
Firewall에 있을 경우 이런 방법으로 포트를 열지 못하면 랜덤한 포트를 부여
ForcedPort로 접속 Attempts < 3 만큼 시도를 한다.
그리고 PUSH를 Route PUSH를 통해 보내 그누텔라 default 포트가 아닌 열 수 있는 포트로 직접연결을 한다.
만약 상대방 서버가
FireWall에 있다면
QueryHit중의 bool Firewall;필드가 True가 되므로 이에 대한 응답으로
~cpp
Item.Host = QueryHit->Host; Item.Port = QueryHit->Port;
에 해당하는 Port로 Download를 한다.