int TorrentFile::save_block(PIECE_INDEX piece, BLOCK_OFFSET block_offset, uint32_t block_length, char * block) { if (block == NULL) return ERR_BAD_ARG; uint32_t block_index; m_torrent->get_block_index_by_offset(piece, block_offset, block_index) ; uint32_t real_block_length; m_torrent->get_block_length_by_index(piece, block_index, real_block_length); if (real_block_length != block_length) return ERR_INTERNAL; FILE_INDEX file_index; FILE_OFFSET offset; m_torrent->get_block_info(piece, block_index, file_index, offset); uint32_t pos = 0; BLOCK_ID block_id; generate_block_id(piece, block_index, block_id); while(pos < block_length) { //определяем сколько возможно записать в файл uint64_t remain = m_files[file_index].length - offset; //если данных для записи больше,чем это возможно, пишем в файл сколько можем(remain), иначе пишем все что есть uint64_t to_write = block_length - pos > remain ? remain : block_length - pos; try { m_fm->File_write(m_files[file_index++].file_, &block[pos], to_write, offset, block_id); } catch (Exception & e) { if (e.get_errcode() == Exception::ERR_CODE_NULL_REF || e.get_errcode() == Exception::ERR_CODE_FILE_NOT_EXISTS || e.get_errcode() == Exception::ERR_CODE_UNDEF || e.get_errcode() == Exception::ERR_CODE_FILE_WRITE_OFFSET_OVERFLOW || e.get_errcode() == Exception::ERR_CODE_BLOCK_TOO_BIG) { torrent_failure tf; tf.exception_errcode = e.get_errcode(); tf.errno_ = 0; tf.description = m_files[file_index - 1].name; tf.where = TORRENT_FAILURE_WRITE_FILE; m_torrent->set_failure(tf); } return ERR_INTERNAL; } catch (SyscallException & e) { torrent_failure tf; tf.exception_errcode = Exception::NO_ERROR; tf.errno_ = e.get_errno(); tf.description = m_files[file_index - 1].name; tf.where = TORRENT_FAILURE_WRITE_FILE; m_torrent->set_failure(tf); return ERR_INTERNAL; } pos += to_write; offset = 0; } return ERR_NO_ERROR; }
int TorrentFile::block_done(uint32_t piece_index, uint32_t block_index) { if (piece_index >= m_pieces_count) return -1; if (block_index >= m_piece_info[piece_index].block_count) return -1; std::map<uint32_t, Peer*>::iterator iter = m_piece_info[piece_index].blocks2download.find(block_index); if (iter == m_piece_info[piece_index].blocks2download.end() && m_piece_info[piece_index].blocks2download.count(block_index) == 0) return 0; if ((*iter).second != NULL) { printf("block is done %u %u\n", piece_index, block_index); m_piece_info[piece_index].marked_blocks--; (*iter).second->erase_requested_block(generate_block_id(piece_index, block_index)); m_piece_info[piece_index].blocks2download.erase(iter); } return 0; }
int TorrentFile::save_block(uint32_t piece, uint32_t block_offset, uint32_t block_length, char * block) { if (piece >= m_pieces_count || block == NULL) return ERR_BAD_ARG; uint32_t block_index = block_offset / BLOCK_LENGTH; if (block_index >= m_piece_info[piece].block_count) return ERR_INTERNAL; printf("saving block %u %u\n", piece, block_index); uint32_t real_block_length; if (get_block_length_by_index(piece, block_index, &real_block_length) != ERR_NO_ERROR || real_block_length != block_length) { unmark_block(piece, block_index); return ERR_INTERNAL; } int file_index = m_piece_info[piece].file_index; uint64_t offset = block_offset + m_piece_info[piece].offset; while(offset >= m_files[file_index].length) { offset -= m_files[file_index++].length; } uint32_t pos = 0; uint64_t id = generate_block_id(piece, block_index); //uint64_t id = ((uint64_t)piece << 32) + block_index; while(pos < block_length) { //определяем сколько возможно записать в файл uint32_t remain = m_files[file_index].length - offset; //если данных для записи больше,чем это возможно, пишем в файл сколько можем(remain), иначе пишем все что есть uint32_t to_write = block_length - pos > remain ? remain : block_length - pos; //bad arg на этом этапе получить не реально, ошибку нехватки места в кэше не прокидываем, //т.к блок сможем скачать позже у кого угодно if (m_fm->File_write(m_files[file_index++].fm_id, &block[pos], to_write, offset, id) != ERR_NO_ERROR) { printf("can not save block %u %u\n", piece, block_index); unmark_block(piece, block_index); } pos += to_write; offset = 0; } return ERR_NO_ERROR; }
int TorrentFile::read_block(uint32_t piece, uint32_t block_index, char * block, uint32_t * block_length) { if (piece >= m_pieces_count) return ERR_BAD_ARG; if (block_index >= m_piece_info[piece].block_count) return ERR_INTERNAL; get_block_length_by_index(piece, block_index, block_length); uint64_t id = generate_block_id(piece, block_index); //printf("look to cache...\n"); if (m_torrent->m_bc->get(m_torrent, id, block) == ERR_NO_ERROR) return ERR_NO_ERROR; //printf("not in cache, read from file\n"); int file_index = m_piece_info[piece].file_index; uint32_t offset = block_index * BLOCK_LENGTH + m_piece_info[piece].offset; while(offset >= m_files[file_index].length) { offset -= m_files[file_index++].length; } uint32_t pos = 0; while(pos < *block_length) { //определяем сколько возможно прочитать из файла uint32_t remain = m_files[file_index].length - offset; //если прочитать надо больше, чем это возможно, читаем сколько можем(remain), иначе читаем все uint32_t to_read = *block_length - pos > remain ? remain : *block_length - pos; int ret = m_fm->File_read_immediately(m_files[file_index++].fm_id, &block[pos], offset, to_read); if (ret < 0) return ERR_INTERNAL; pos += to_read; offset = 0; } //printf("Put in cache\n"); m_torrent->m_bc->put(m_torrent, id, block); return ERR_NO_ERROR; }
int Peer::request(PIECE_INDEX piece_index, BLOCK_INDEX block_index) { BLOCK_ID id; generate_block_id(piece_index, block_index, id); return request(id); }
int Peer::process_messages() { //is handshake if (m_buf.length - m_buf.pos >= HANDSHAKE_LENGHT && m_buf.data[m_buf.pos] == '\x13' && memcmp(&m_buf.data[m_buf.pos + 1], "BitTorrent protocol", 19) == 0 && m_torrent->memcmp_infohash_bin((unsigned char*)&m_buf.data[m_buf.pos + 28])) { if (m_state != PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; m_buf.pos += HANDSHAKE_LENGHT; m_state = PEER_STATE_GENERAL_READY; #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received handshake\n"; #endif } char ip[16]; memset(ip, 0, 16); strcpy(ip, inet_ntoa(m_sock->m_peer.sin_addr)); uint32_t piece_count = m_torrent->get_piece_count(); BLOCK_ID block_id; while((int)m_buf.length - (int)m_buf.pos > 4) { uint32_t len = 0; memcpy(&len, &m_buf.data[m_buf.pos], 4); len = ntohl(len); if (m_buf.length - m_buf.pos < len + 4) goto end; m_buf.pos += 4; if (len != 0) { char id = m_buf.data[m_buf.pos++]; switch (id) { case '\x00'://choke: <len=0001><id=0> #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received choke"; #endif if ( m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; m_peer_choking = true; if (m_state == PEER_STATE_WAIT_UNCHOKE) return ERR_INTERNAL;//m_state = PEER_STATE_GENERAL_READY; иначе зацикливаемся break; case '\x01'://unchoke: <len=0001><id=1> #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received unchoke"; #endif if ( m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; m_peer_choking = false; if (m_state == PEER_STATE_WAIT_UNCHOKE) m_state = PEER_STATE_REQUEST_READY; break; case '\x02'://interested: <len=0001><id=2> #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received interested"; #endif if ( m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; m_peer_interested = true; send_unchoke(); break; case '\x03'://not interested: <len=0001><id=3> #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received not interested"; #endif if ( m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; m_peer_interested = false; break; case '\x04'://have: <len=0005><id=4><piece index> #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received have"; #endif if ( m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; PIECE_INDEX piece_index; memcpy(&piece_index, &m_buf.data[m_buf.pos], 4); piece_index = ntohl(piece_index); if (piece_index >= piece_count) return ERR_INTERNAL; m_available_pieces.insert(piece_index); set_bitfield(piece_index, piece_count, m_bitfield); break; case '\x05'://bitfield: <len=0001+X><id=5><bitfield> #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received bitfield"; #endif if (len - 1 != m_torrent->get_bitfield_length()) return ERR_INTERNAL; if (m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; memcpy(m_bitfield, &m_buf.data[m_buf.pos], len - 1); for(PIECE_INDEX i = 0; i < piece_count; i++) { if (bit_in_bitfield(i, piece_count, m_bitfield)) m_available_pieces.insert(i); } break; case '\x06'://request: <len=0013><id=6><index><begin><length> if ( m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; PIECE_INDEX index;//индекс куска BLOCK_OFFSET offset; uint32_t length; memcpy(&index,&m_buf.data[m_buf.pos], 4); index = ntohl(index); memcpy(&offset,&m_buf.data[m_buf.pos + 4], 4); offset = ntohl(offset); memcpy(&length,&m_buf.data[m_buf.pos + 8], 4); length = ntohl(length); //проверяем валидность индексов, смещений if (length == 0 || length > BLOCK_LENGTH || offset % BLOCK_LENGTH != 0) return ERR_INTERNAL; BLOCK_INDEX block_index; m_torrent->get_block_index_by_offset(index, offset, block_index) ; uint32_t real_block_length; m_torrent->get_block_length_by_index(index, block_index, real_block_length); if (real_block_length != length) return ERR_INTERNAL; //кладем индекс блока в очередь запросов generate_block_id(index, block_index, block_id); m_requests_queue.insert(block_id); #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received request piece=" << index << " block=" << block_index; #endif break; case '\x07'://piece: <len=0009+X><id=7><index><begin><block> { PIECE_INDEX index;//индекс куска BLOCK_OFFSET offset; uint32_t block_length = len - 9; if (m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; if (block_length == 0 || block_length > BLOCK_LENGTH) return ERR_INTERNAL; memcpy(&index,&m_buf.data[m_buf.pos], 4); index = ntohl(index); memcpy(&offset,&m_buf.data[m_buf.pos + 4], 4); offset = ntohl(offset); if (offset % BLOCK_LENGTH != 0) return ERR_INTERNAL; generate_block_id(index, offset / BLOCK_LENGTH, block_id); std::set<BLOCK_ID>::iterator iter = m_requested_blocks.find(block_id); #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received piece piece=" << index << " block=" << offset / BLOCK_LENGTH; #endif if (iter == m_requested_blocks.end()) return ERR_INTERNAL; if (m_torrent->save_block(index, offset, block_length, &m_buf.data[m_buf.pos + 8]) != ERR_NO_ERROR) return ERR_INTERNAL; m_requested_blocks.erase(iter); m_downloaded += block_length; break; } case '\x08'://cancel: <len=0013><id=8><index><begin><length> { if (m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; PIECE_INDEX index;//индекс куска BLOCK_OFFSET offset; uint32_t length; memcpy(&index,&m_buf.data[m_buf.pos], 4); index = ntohl(index); memcpy(&offset,&m_buf.data[m_buf.pos + 4], 4); offset = ntohl(offset); memcpy(&length,&m_buf.data[m_buf.pos + 8], 4); length = ntohl(length); //проверяем валидность индексов, смещений if (length == 0 || length > BLOCK_LENGTH || offset % BLOCK_LENGTH != 0) return ERR_INTERNAL; uint32_t block_index; m_torrent->get_block_index_by_offset(index, offset, block_index); #ifdef PEER_DEBUG logger::LOGGER() << "Peer " << m_ip.c_str() << ": is received cancel piece=" << index << " block=" << block_index; #endif uint32_t real_block_length; m_torrent->get_block_length_by_index(index, block_index, real_block_length); if (real_block_length != length) return ERR_INTERNAL; generate_block_id(index, block_index, block_id); m_requested_blocks.erase(block_id); break; } case '\x09'://port: <len=0003><id=9><listen-port> if (m_state == PEER_STATE_WAIT_HANDSHAKE) return ERR_INTERNAL; break; default: return ERR_INTERNAL; break; } m_buf.pos += len - 1; } } end: char temp[BUFFER_SIZE]; size_t len = m_buf.length - m_buf.pos; memcpy(temp, &m_buf.data[m_buf.pos], len); m_buf.length = len; m_buf.pos = 0; memcpy(m_buf.data, temp, len); return ERR_NO_ERROR; }
int TorrentFile::read_block(PIECE_INDEX piece, BLOCK_INDEX block_index, char * block, uint32_t & block_length) { m_torrent->get_block_length_by_index(piece, block_index, block_length); BLOCK_ID block_id; generate_block_id(piece, block_index, block_id); block_cache::cache_key key(m_torrent.get(), block_id); try { m_torrent->get_bc()->get(key, block); return ERR_NO_ERROR; } catch (Exception & e) { if (e.get_errcode() != Exception::ERR_CODE_LRU_CACHE_NE) { torrent_failure tf; tf.exception_errcode = e.get_errcode(); tf.errno_ = 0; tf.description = ""; tf.where = TORRENT_FAILURE_GET_BLOCK_CACHE; m_torrent->set_failure(tf); return ERR_INTERNAL; } } FILE_INDEX file_index; FILE_OFFSET offset; m_torrent->get_block_info(piece, block_index, file_index, offset); uint32_t pos = 0; while(pos < block_length) { //определяем сколько возможно прочитать из файла uint64_t remain = m_files[file_index].length - offset; //если прочитать надо больше, чем это возможно, читаем сколько можем(remain), иначе читаем все uint64_t to_read = block_length - pos > remain ? remain : block_length - pos; try { m_fm->File_read_immediately(m_files[file_index++].file_, &block[pos], offset, to_read); } catch (Exception & e) { if (e.get_errcode() == Exception::ERR_CODE_NULL_REF || e.get_errcode() == Exception::ERR_CODE_FILE_NOT_EXISTS || e.get_errcode() == Exception::ERR_CODE_UNDEF || e.get_errcode() == Exception::ERR_CODE_FILE_WRITE_OFFSET_OVERFLOW || e.get_errcode() == Exception::ERR_CODE_FILE_NOT_OPENED) { torrent_failure tf; tf.exception_errcode = e.get_errcode(); tf.errno_ = 0; tf.description = m_files[file_index - 1].name; tf.where = TORRENT_FAILURE_WRITE_FILE; m_torrent->set_failure(tf); } return ERR_INTERNAL; } catch (SyscallException & e) { torrent_failure tf; tf.exception_errcode = Exception::NO_ERROR; tf.errno_ = e.get_errno(); tf.description = m_files[file_index - 1].name; tf.where = TORRENT_FAILURE_WRITE_FILE; m_torrent->set_failure(tf); return ERR_INTERNAL; } pos += to_read; offset = 0; } try { m_torrent->get_bc()->put(key, block); } catch(Exception & e) { torrent_failure tf; tf.exception_errcode = e.get_errcode(); tf.errno_ = 0; tf.description = ""; tf.where = TORRENT_FAILURE_PUT_BLOCK_CACHE; m_torrent->set_failure(tf); return ERR_INTERNAL; } return ERR_NO_ERROR; }