git-svn-id: svn://db.shs.com.ru/pip@4 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5

This commit is contained in:
2015-02-28 18:35:47 +00:00
parent 8e451c891d
commit 13336674eb
154 changed files with 44021 additions and 0 deletions

470
src/io/pifiletransfer.cpp Normal file
View File

@@ -0,0 +1,470 @@
#include "pifiletransfer.h"
const char PIFileTransfer::sign[] = {'P', 'F', 'T'};
PIFileTransfer::PIFileTransfer(): crc(standardCRC_16()) {
for (uint i = 0; i < sizeof(sign); i++)
header.sig[i] = sign[i];
header.version = PIFILETRANSFER_VERSION;
header.session_id = 0;
dir = PIDir::current();
fileinfo_size = sizeof(EntryInfo) + sizeof(PIByteArray) + 100;
min_packet_size = sizeof(PacketHeader) + sizeof(uint) + fileinfo_size;
is_sending = is_receiving = false;
bytes_file_all = bytes_file_cur = bytes_total_all = bytes_total_cur = 0;
timeout_ = 1.;
setPacketSize(4096);
srand(PISystemTime::current().toMilliseconds());
}
PIFileTransfer::~PIFileTransfer() {
break_ = true;
session.clear();
replies.clear();
}
bool PIFileTransfer::send(const PIString & file) {
return sendFiles(PIVector<PIFile::FileInfo>() << PIFile::fileInfo(file));
}
bool PIFileTransfer::send(PIVector<PIFile::FileInfo> entries) {
PIVector<PIFile::FileInfo> allEntries;
for (int i = 0; i < entries.size_s(); i++) {
allEntries << entries[i];
if (entries[i].isDir())
allEntries << PIDir::allEntries(dir.absolutePath() + dir.separator + entries[i].path);
}
return sendFiles(allEntries);
}
void PIFileTransfer::stopSend() {
if (!is_sending) return;
break_ = true;
}
void PIFileTransfer::stopReceive() {
if (!is_receiving) return;
break_ = true;
finish_receive(false);
}
void PIFileTransfer::received(PIByteArray & ba) {
if (ba.size() < sizeof(PacketHeader)) return;
PacketHeader h;
memcpy(&h, ba.data(), sizeof(PacketHeader));
PacketType pt = (PacketType)h.type;
// piCoutObj << "receive" << h.session_id << h.type << h.id;
switch (pt) {
case pt_Data:
if (h.session_id != header.session_id || !is_receiving) {
sendBreak(h.session_id);
return;
} else {
uint rcrc = *(uint *)(ba.data(ba.size_s() - sizeof(uint)));
uint ccrc = crc.calculate(ba.data(), ba.size_s() - sizeof(uint));
if (rcrc != ccrc) {
header.id = h.id;
sendReply(Invalid);
} else {
ba >> h;
ba.resize(ba.size_s() - sizeof(uint));
processData(h.id, ba);
}
}
break;
case pt_Reply:
if (h.session_id != header.session_id) return;
ba >> h;
if (ba.size() == sizeof(int)) {
int rci;
ba >> rci;
ReplyCode rc = (ReplyCode)rci;
// piCoutObj << "reply received" << rci;
if (rc == Break) {
break_ = true;
if (is_receiving) {
stopReceive();
return;
}
if (is_sending) {
stopSend();
return;
}
}
if (is_sending) {
if (h.id >= 0 && h.id < replies.size())
replies[h.id] = rc;
}
if (is_receiving && h.id == 0) {
if (checkSession() == 0) finish_receive(true);
}
}
break;
case pt_SendRequest:
if (is_sending) {
sendBreak(h.session_id);
return;
}
if (header.session_id != h.session_id && is_receiving) {
sendBreak(h.session_id);
return;
}
ba >> h;
if (ba.size() != sizeof(uint) + sizeof(llong)) return;
uint pcts;
ba >> pcts;
bytes_file_all = pcts;
ba >> bytes_total_all;
header.session_id = h.session_id;
header.id = 0;
bool ok = true;
state_string = "receive request";
receiveRequest(&ok);
if (ok) {
session.clear();
replies.clear();
session.resize(pcts);
replies.resize(pcts + 1);
replies.fill(Unknown);
is_receiving = true;
state_string = "receiving";
replies[0] = Success;
sendReply(Success);
} else {
sendReply(Invalid);
state_string = "wait user permit";
}
break;
}
}
bool PIFileTransfer::sendFiles(PIVector<PIFile::FileInfo> files) {
PIStringList names;
for(int i=0; i<files.size_s(); i++) {
files[i].path = dir.relative(files[i].path);
if (names.contains(files[i].path)) {files.remove(i); i--;}
else names << files[i].path;
}
// piCoutObj << "prepare to send" << files.size() << "files";
break_ = false;
is_sending = true;
buildSession(files);
replies.resize(session.size() + 1);
replies.fill(Unknown);
work_file.setPath("");
startSend();
bytes_file_all = bytes_file_cur = 0;
PIByteArray ba;
if (!getSendRequest()) return finish_send(false);
for (int i = 0; i < session.size_s(); i++) {
ba = buildPacket(i);
sendRequest(ba);
if (break_) return finish_send(false);
}
// piCoutObj << "correct errors";
PITimeMeasurer tm;
int prev_chk = 0;
while (tm.elapsed_s() < timeout_) {
int chk = checkSession();
if (chk != prev_chk) tm.reset();
if (chk == 0) return finish_send(true);
if (chk > 0) {
ba = buildPacket(chk - 1);
sendRequest(ba);
}
// if (chk == -1) return finish_send(false);
if (break_) return finish_send(false);
prev_chk = chk;
piMSleep(1);
}
return finish_send(false);;
}
int PIFileTransfer::checkSession() {
int miss = 0;
for (int i = 1; i < replies.size_s(); i++) {
if ((ReplyCode)replies[i] != Success) miss++;
if ((ReplyCode)replies[i] == Invalid) return i;
}
for (int i = 1; i < replies.size_s(); i++) {
if ((ReplyCode)replies[i] == Unknown) return i;
}
if (miss > 0) {
piCoutObj << "missing" << miss << "packets";
return -miss;
} else return 0;
}
void PIFileTransfer::buildSession(PIVector<PIFile::FileInfo> files) {
state_string = "calculating files...";
session.clear();
header.session_id = rand();
bytes_file_cur = 0;
bytes_file_all = files.size();
bytes_total_all = bytes_total_cur = 0;
// PIMap<llong, int> sizes;
// for (int i=0; i<files.size(); i++) {
// sizes[files[i].size] = i;
// }
// PIVector<int> indexes = sizes.values();
EntryInfo fi;
PIVector<EntryInfo> lfi;
int cur_size = min_packet_size;
for (int i = 0; i < files.size_s(); i++) {
bytes_file_cur = i;
fi.entry = files[i];
bytes_total_all += fi.entry.size;
// fi.size = fi.entry.size;
fi.fstart = 0;
fi.part_index = 1;
int rest = fi.entry.size - (max_packet_size - cur_size);
// piCout << i << fi.entry << rest;
if (rest <= 0) {
fi.parts = 1;
fi.fsize = fi.entry.size;
lfi << fi;
cur_size += fi.fsize + fileinfo_size;
} else {
fi.fsize = fi.entry.size - rest;
fi.parts = 1 + 1 + piMaxi(1, rest / (max_packet_size - min_packet_size));
// piCout << fi.parts;
lfi << fi;
session << lfi;
lfi.clear();
cur_size = min_packet_size;
llong fs = fi.fsize;
for (uint j = 1; j < fi.parts; j++) {
fi.part_index++;
fi.fstart = fs;
fi.fsize = piMinll(fi.entry.size - fs, max_packet_size - min_packet_size);
lfi << fi;
cur_size += fi.fsize + sizeof(PIByteArray);
if (fi.part_index != fi.parts) {
session << lfi;
lfi.clear();
cur_size = min_packet_size;
fs += fi.fsize;
}
}
}
if (max_packet_size - cur_size < min_packet_size) {
session << lfi;
lfi.clear();
cur_size = min_packet_size;
}
}
if (cur_size > min_packet_size) session << lfi;
}
void PIFileTransfer::sendBreak(int session_id) {
uint psid = header.session_id;
header.session_id = session_id;
sendReply(Break);
header.session_id = psid;
}
void PIFileTransfer::sendReply(PIFileTransfer::ReplyCode reply) {
// if (is_send_result) header.type = pt_SendResult;
// else
header.type = pt_Reply;
PIByteArray ba;
ba << header << (int)reply;
sendRequest(ba);
}
bool PIFileTransfer::getSendRequest() {
PITimeMeasurer tm;
header.type = pt_SendRequest;
header.id = 0;
PIByteArray ba;
ba << header;
ba << (uint)session.size() << bytes_total_all;
state_string = "send request";
for (int i = 0; i < 3; i++) {
tm.reset();
sendRequest(ba);
while (tm.elapsed_s() < timeout_) {
if (break_) return false;
//piCoutObj << send_replyes[0];
if (replies[0] == Success) {
state_string = "send permited!";
return true;
}
if (replies[0] == Invalid) {
state_string = "waiting for permit";
tm.reset();
}
piMSleep(10);
}
}
return false;
}
void PIFileTransfer::processData(int id, PIByteArray & data) {
// piCoutObj << "received packet" << id << ", size" << data.size();
if (id < 1 || id > replies.size_s()) return;
if (!session[id - 1].isEmpty()) {
header.id = id;
replies[id] = Success;
sendReply(Success);
if (checkSession() == 0) state_string = "receive ok";
return;
}
EntryInfo fi;
PIByteArray ba;
while (!data.isEmpty()) {
ba.clear();
data >> fi;
if (fi.entry.size > 0) data >> ba;
fi.fsize = ba.size();
bytes_total_cur += fi.fsize;
bytes_file_all = fi.entry.size;
bytes_file_cur = fi.fstart;
// piCoutObj << "recv" << fi;
session[id - 1] << fi;
state_string = "receiving " + fi.entry.path;
PIString path = dir.absolutePath() + dir.separator + fi.entry.path;
if (fi.entry.isDir()) {
// piCoutObj << "make dir" << fi.entry.path;
if (!PIDir::make(path, false)) {
state_string = "ERROR! while create directory " + path;
piCoutObj << state_string;
finish_receive(false);
return;
}
}
if (fi.entry.isFile()) {
if (work_file.path() != path || !work_file.isOpened()) {
work_file.close();
if (!work_file.open(path, PIIODevice::ReadWrite)) {
state_string = "ERROR! while open file " + path;
piCoutObj << state_string;
finish_receive(false);
return;
}
if (work_file.size() > fi.entry.size) {
piCoutObj << "*** error size" << work_file.size() << fi.entry.size;
work_file.clear();
work_file.resize(fi.entry.size);
piCoutObj << "*** correct size" << work_file.size() << fi.entry.size;
}
}
// piCoutObj << "write file" << path << work_file.path() << work_file.size() << fi.entry.size << work_file.pos() << fi.fstart << fi.fsize;
if (work_file.size() < fi.fstart) {
// piCoutObj << "error pos size" << work_file.pos() << fi.fstart;
work_file.resize(fi.fstart);
// piCoutObj << "correct pos size" << work_file.pos() << fi.fstart;
}
if (work_file.size() > fi.entry.size) {
piCoutObj << "!!! error size" << work_file.size() << fi.entry.size;
work_file.clear();
work_file.resize(fi.entry.size);
piCoutObj << "!!! correct size" << work_file.size() << fi.entry.size;
}
// if (fi.fstart != work_file.pos()) piCoutObj << "error pos" << work_file.pos() << fi.fstart;
work_file.seek(fi.fstart);
int rs = work_file.write(ba.data(), ba.size());
if (rs != fi.fsize) {
state_string = "ERROR! while read file " + fi.entry.path + " (must " + PIString::fromNumber(fi.fsize) + ", but read " + PIString::fromNumber(rs) + ")";
piCoutObj << state_string;
finish_receive(false);
return;
}
}
}
header.id = id;
replies[id] = Success;
if (checkSession() == 0) state_string = "receive ok";
sendReply(Success);
}
PIByteArray PIFileTransfer::buildPacket(int id) {
PIByteArray ret;
PIByteArray ba;
header.id = id + 1;
header.type = pt_Data;
//piCoutObj << "Packet" << header.id;
//piCoutObj << "session id" << header.session_id;
ret << header;
for (int i = 0; i < session[id].size_s(); i++) {
EntryInfo fi = session[id][i];
// piCoutObj << "send" << fi;
bytes_total_cur += fi.fsize;
ret << fi;
if (fi.entry.size > 0) {
PIString path = dir.absolutePath() + dir.separator + fi.entry.path;
if (work_file.path() != path || !work_file.isOpened()) {
if (!work_file.open(path, PIIODevice::ReadOnly)) {
break_ = true;
state_string = "ERROR! while open file " + fi.entry.path;
piCoutObj << state_string;
return ret;
}
}
work_file.seek(fi.fstart);
ba.resize(fi.fsize);
int rs = work_file.read(ba.data(), ba.size());
if (rs != fi.fsize) {
break_ = true;
state_string = "ERROR! while read file " + fi.entry.path + " (must " + PIString::fromNumber(fi.fsize) + ", but read " + PIString::fromNumber(rs) + ")";
piCoutObj << state_string;
return ret;
}
ret << ba;
}
}
EntryInfo cfile = session[id].back();
state_string = "sending: " + cfile.entry.path;
bytes_file_all = cfile.entry.size;
bytes_file_cur = cfile.fstart;
uint scrc = crc.calculate(ret);
ret << scrc;
//piCoutObj << "packet" << header.id << "send crc" << scrc;
return ret;
}
bool PIFileTransfer::finish_send(bool ok) {
if (ok) state_string = "send done";
else state_string = "send failed";
// piCoutObj << state_string << PIString::readableSize(bytes_total_all);
is_sending = false;
work_file.close();
work_file.setPath("");
bytes_file_all = bytes_file_cur = 0;
bytes_total_all = bytes_total_cur = 0;
header.id = 0;
if (!ok) sendBreak(header.session_id);
else sendReply(Success);
finishSend(ok);
return ok;
}
void PIFileTransfer::finish_receive(bool ok) {
if (ok) state_string = "receive done";
else state_string = "receive failed";
// piCoutObj << state_string << PIString::readableSize(bytes_total_all);
is_receiving = false;
work_file.close();
work_file.setPath("");
bytes_file_all = bytes_file_cur = 0;
bytes_total_all = bytes_total_cur = 0;
if (!ok) sendBreak(header.session_id);
finishReceive(ok);
}