new PIBaseTransfer, PIDataTransfer and update PIFileTransfer
git-svn-id: svn://db.shs.com.ru/pip@9 12ceb7fc-bf1f-11e4-8940-5bc7170c53b5
This commit is contained in:
@@ -1,374 +1,24 @@
|
|||||||
#include "pidatatransfer.h"
|
#include "pidatatransfer.h"
|
||||||
|
|
||||||
const uint PIBaseTransfer::signature = 0x54444950;
|
|
||||||
|
|
||||||
PIBaseTransfer::PIBaseTransfer(): crc(standardCRC_16()) {
|
PIByteArray PIDataTransfer::buildPacket(Part fi) {
|
||||||
header.sig = signature;
|
|
||||||
header.session_id = 0;
|
|
||||||
packet_header_size = sizeof(PacketHeader);
|
|
||||||
part_header_size = sizeof(Part) + sizeof(PIByteArray);
|
|
||||||
is_sending = is_receiving = false;
|
|
||||||
break_ = true;
|
|
||||||
bytes_all = bytes_cur = 0;
|
|
||||||
timeout_ = 1.;
|
|
||||||
setPacketSize(4096);
|
|
||||||
srand(PISystemTime::current().toMilliseconds());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PIBaseTransfer::~PIBaseTransfer() {
|
|
||||||
break_ = true;
|
|
||||||
session.clear();
|
|
||||||
replies.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIBaseTransfer::stopSend() {
|
|
||||||
if (!is_sending) return;
|
|
||||||
break_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIBaseTransfer::stopReceive() {
|
|
||||||
if (!is_receiving) return;
|
|
||||||
break_ = true;
|
|
||||||
finish_receive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIBaseTransfer::received(PIByteArray& data) {
|
|
||||||
if (data.size() < sizeof(PacketHeader)) return;
|
|
||||||
PacketHeader h;
|
|
||||||
memcpy(&h, data.data(), sizeof(PacketHeader));
|
|
||||||
PacketType pt = (PacketType)h.type;
|
|
||||||
// piCoutObj << "receive" << h.session_id << h.type << h.id;
|
|
||||||
switch (pt) {
|
|
||||||
case pt_Unknown:
|
|
||||||
break;
|
|
||||||
case pt_Data:
|
|
||||||
if (h.session_id != header.session_id || !is_receiving) {
|
|
||||||
sendBreak(h.session_id);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
uint rcrc = h.crc;
|
|
||||||
uint ccrc = crc.calculate(data.data(sizeof(PacketHeader)), data.size_s() - sizeof(PacketHeader));
|
|
||||||
if (rcrc != ccrc) {
|
|
||||||
header.id = h.id;
|
|
||||||
sendReply(pt_ReplyInvalid);
|
|
||||||
} else {
|
|
||||||
data >> h;
|
|
||||||
processData(h.id, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case pt_ReplySuccess:
|
|
||||||
case pt_ReplyInvalid:
|
|
||||||
if (h.session_id != header.session_id) return;
|
|
||||||
if (is_sending) {
|
|
||||||
if (h.id >= 0 && h.id < replies.size())
|
|
||||||
replies[h.id] = pt;
|
|
||||||
}
|
|
||||||
if (is_receiving && h.id == 0) {
|
|
||||||
if (checkSession() == 0 && pt == pt_ReplySuccess) finish_receive(true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case pt_Break:
|
|
||||||
break_ = true;
|
|
||||||
if (is_receiving) {
|
|
||||||
stopReceive();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (is_sending) {
|
|
||||||
stopSend();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case pt_Start:
|
|
||||||
if (is_sending) {
|
|
||||||
sendBreak(h.session_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (header.session_id != h.session_id && is_receiving) {
|
|
||||||
sendBreak(h.session_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (data.size() == sizeof(StartRequest) + sizeof(PacketHeader)) {
|
|
||||||
StartRequest sr;
|
|
||||||
memcpy(&sr, data.data(sizeof(PacketHeader)), sizeof(StartRequest));
|
|
||||||
bytes_all = sr.size;
|
|
||||||
header.session_id = h.session_id;
|
|
||||||
header.id = 0;
|
|
||||||
state_string = "start request";
|
|
||||||
session.clear();
|
|
||||||
replies.clear();
|
|
||||||
session.resize(sr.packets);
|
|
||||||
replies.resize(sr.packets + 1);
|
|
||||||
replies.fill(pt_Unknown);
|
|
||||||
is_receiving = true;
|
|
||||||
break_ = false;
|
|
||||||
state_string = "receiving";
|
|
||||||
replies[0] = pt_ReplySuccess;
|
|
||||||
sendReply(pt_ReplySuccess);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool PIBaseTransfer::send_process() {
|
|
||||||
break_ = false;
|
|
||||||
is_sending = true;
|
|
||||||
startSend();
|
|
||||||
replies.resize(session.size() + 1);
|
|
||||||
replies.fill(pt_Unknown);
|
|
||||||
PIByteArray ba;
|
PIByteArray ba;
|
||||||
if (!getStartRequest()) return finish_send(false);
|
ba.resize(fi.size);
|
||||||
for (int i = 0; i < session.size_s(); i++) {
|
memcpy(ba.data(), data_.data(fi.start), fi.size);
|
||||||
ba = build_packet(i);
|
return ba;
|
||||||
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 PIBaseTransfer::checkSession() {
|
void PIDataTransfer::receivePart(Part fi, PIByteArray ba, PIByteArray pheader) {
|
||||||
int miss = 0;
|
if (data_.size() < fi.start + fi.size) data_.resize(fi.start + fi.size);
|
||||||
for (int i = 1; i < replies.size_s(); i++) {
|
memcpy(data_.data(fi.start), ba.data(), ba.size_s());
|
||||||
if (replies[i] != pt_ReplySuccess) miss++;
|
|
||||||
if (replies[i] == pt_ReplyInvalid) return i;
|
|
||||||
}
|
|
||||||
for (int i = 1; i < replies.size_s(); i++) {
|
|
||||||
if (replies[i] != pt_ReplySuccess) return i;
|
|
||||||
}
|
|
||||||
if (miss > 0) {
|
|
||||||
piCoutObj << "missing" << miss << "packets";
|
|
||||||
return -miss;
|
|
||||||
} else return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIBaseTransfer::buildSession(PIVector<Part> parts) {
|
bool PIDataTransfer::send(const PIByteArray& ba) {
|
||||||
state_string = "calculating files... ";
|
data_ = ba;
|
||||||
session.clear();
|
buildSession(PIVector<Part>() << Part(0, data_.size()));
|
||||||
header.session_id = rand();
|
return send_process();
|
||||||
bytes_all = 0;
|
|
||||||
Part fi;
|
|
||||||
int fi_index, fi_prts;
|
|
||||||
PIVector<Part> lfi;
|
|
||||||
int min_size = packet_header_size + part_header_size;
|
|
||||||
int cur_size = min_size;
|
|
||||||
for (int i = 0; i < parts.size_s(); i++) {
|
|
||||||
state_string = "calculating files... " + PIString::fromNumber(i) + " of " + PIString::fromNumber(parts.size());
|
|
||||||
fi.id = parts[i].id;
|
|
||||||
bytes_all += fi.size;
|
|
||||||
// fi.size = fi.entry.size;
|
|
||||||
fi.start = 0;
|
|
||||||
int rest = parts[i].size - (packet_size - cur_size);
|
|
||||||
// piCout << i << fi.entry << rest;
|
|
||||||
if (rest <= 0) {
|
|
||||||
fi.size = parts[i].size;
|
|
||||||
lfi << fi;
|
|
||||||
cur_size += fi.size + part_header_size;
|
|
||||||
} else {
|
|
||||||
fi.size = parts[i].size - rest;
|
|
||||||
fi_index = 1;
|
|
||||||
fi_prts = 1 + 1 + piMaxi(1, rest / (packet_size - min_size));
|
|
||||||
// piCout << fi_prts;
|
|
||||||
lfi << fi;
|
|
||||||
session << lfi;
|
|
||||||
lfi.clear();
|
|
||||||
cur_size = min_size;
|
|
||||||
llong fs = fi.size;
|
|
||||||
for (int j = 1; j < fi_prts; j++) {
|
|
||||||
fi_index++;
|
|
||||||
fi.start = fs;
|
|
||||||
fi.size = piMin<ullong>(parts[i].size - fs, packet_size - min_size);
|
|
||||||
lfi << fi;
|
|
||||||
cur_size += fi.size + part_header_size;
|
|
||||||
if (fi_index != fi_prts) {
|
|
||||||
session << lfi;
|
|
||||||
lfi.clear();
|
|
||||||
cur_size = min_size;
|
|
||||||
fs += fi.size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (packet_size - cur_size < min_size) {
|
|
||||||
session << lfi;
|
|
||||||
lfi.clear();
|
|
||||||
cur_size = min_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cur_size > min_size) session << lfi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIBaseTransfer::sendBreak(int session_id) {
|
|
||||||
uint psid = header.session_id;
|
|
||||||
header.session_id = session_id;
|
|
||||||
sendReply(pt_Break);
|
|
||||||
header.session_id = psid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIBaseTransfer::sendReply(PacketType reply) {
|
|
||||||
header.type = reply;
|
|
||||||
PIByteArray ba;
|
|
||||||
ba << header;
|
|
||||||
sendRequest(ba);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool PIBaseTransfer::getStartRequest() {
|
|
||||||
PITimeMeasurer tm;
|
|
||||||
header.type = pt_Start;
|
|
||||||
header.id = 0;
|
|
||||||
PIByteArray ba;
|
|
||||||
ba << header;
|
|
||||||
ba << (uint)session.size() << bytes_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] == pt_ReplySuccess) {
|
|
||||||
state_string = "send permited!";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
piMSleep(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIBaseTransfer::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] = pt_ReplySuccess;
|
|
||||||
sendReply(pt_ReplySuccess);
|
|
||||||
if (checkSession() == 0) state_string = "receive ok";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Part fi;
|
|
||||||
PIByteArray ba, pheader;
|
|
||||||
pheader.resize(packet_header_size - sizeof(PacketHeader));
|
|
||||||
if (!pheader.isEmpty()) {
|
|
||||||
memcpy(pheader.data(), data.data(), pheader.size());
|
|
||||||
data.remove(0, pheader.size_s());
|
|
||||||
}
|
|
||||||
while (!data.isEmpty()) {
|
|
||||||
ba.clear();
|
|
||||||
data >> fi;
|
|
||||||
if (fi.size > 0) data >> ba;
|
|
||||||
// fi.fsize = ba.size();
|
|
||||||
bytes_cur += fi.size;
|
|
||||||
// piCoutObj << "recv" << fi;
|
|
||||||
session[id - 1] << fi;
|
|
||||||
state_string = "receiving...";
|
|
||||||
receivePart(fi, ba, pheader);
|
|
||||||
}
|
|
||||||
header.id = id;
|
|
||||||
replies[id] = pt_ReplySuccess;
|
|
||||||
if (checkSession() == 0) state_string = "receive ok";
|
|
||||||
sendReply(pt_ReplySuccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PIByteArray PIBaseTransfer::build_packet(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;
|
|
||||||
ret << buildPacket(id);
|
|
||||||
/*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 PIBaseTransfer::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;
|
|
||||||
bytes_all = bytes_cur = 0;
|
|
||||||
header.id = 0;
|
|
||||||
if (!ok) sendBreak(header.session_id);
|
|
||||||
else sendReply(pt_ReplySuccess);
|
|
||||||
finishSend(ok);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIBaseTransfer::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;
|
|
||||||
bytes_all = bytes_cur = 0;
|
|
||||||
if (!ok) sendBreak(header.session_id);
|
|
||||||
finishReceive(ok);
|
|
||||||
}
|
|
||||||
PIByteArray PIDataTransfer::buildPacket(int id) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,103 +1,7 @@
|
|||||||
#ifndef PIDATATRANSFER_H
|
#ifndef PIDATATRANSFER_H
|
||||||
#define PIDATATRANSFER_H
|
#define PIDATATRANSFER_H
|
||||||
|
|
||||||
#include "picrc.h"
|
#include "pibasetransfer.h"
|
||||||
#include "pitimer.h"
|
|
||||||
|
|
||||||
class PIBaseTransfer : public PIObject
|
|
||||||
{
|
|
||||||
PIOBJECT(PIBaseTransfer)
|
|
||||||
public:
|
|
||||||
PIBaseTransfer();
|
|
||||||
~PIBaseTransfer();
|
|
||||||
|
|
||||||
struct PacketHeader {
|
|
||||||
uint sig;
|
|
||||||
int type; // PacketType
|
|
||||||
uint session_id;
|
|
||||||
uint id;
|
|
||||||
uint crc;
|
|
||||||
bool check_sig() {return (sig == signature);}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Part {
|
|
||||||
Part(uint id_ = 0, ullong size_ = 0, ullong start_ = 0) : id(id_), size(size_), start(start_) {}
|
|
||||||
uint id;
|
|
||||||
ullong size;
|
|
||||||
ullong start;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void stopSend();
|
|
||||||
virtual void stopReceive();
|
|
||||||
|
|
||||||
virtual bool isSending() const {return is_sending;}
|
|
||||||
virtual bool isReceiving() const {return is_receiving;}
|
|
||||||
|
|
||||||
void setPacketSize(int size) {packet_size = size;}
|
|
||||||
int packetSize() const {return packet_size;}
|
|
||||||
|
|
||||||
void setTimeout(double sec) {timeout_ = sec;}
|
|
||||||
double timeout() const {return timeout_;}
|
|
||||||
|
|
||||||
const PIString & stateString() const {return state_string;}
|
|
||||||
llong bytesAll() const {return bytes_all;}
|
|
||||||
llong bytesCur() const {return bytes_cur;}
|
|
||||||
const PIString * stateString_ptr() const {return &state_string;}
|
|
||||||
const llong * bytesAll_ptr() const {return &bytes_all;}
|
|
||||||
const llong * bytesCur_ptr() const {return &bytes_cur;}
|
|
||||||
|
|
||||||
EVENT(startReceive)
|
|
||||||
EVENT1(finishReceive, bool, ok)
|
|
||||||
EVENT(startSend)
|
|
||||||
EVENT1(finishSend, bool, ok)
|
|
||||||
EVENT1(sendRequest, PIByteArray &, data)
|
|
||||||
EVENT_HANDLER1(void, received, PIByteArray &, data);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
uint packet_header_size, part_header_size;
|
|
||||||
bool break_, is_sending, is_receiving;
|
|
||||||
PIString state_string;
|
|
||||||
llong bytes_all, bytes_cur;
|
|
||||||
|
|
||||||
void buildSession(PIVector<Part> parts);
|
|
||||||
virtual PIByteArray buildPacket(int id) = 0;
|
|
||||||
virtual void receivePart(Part fi, PIByteArray ba, PIByteArray header) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
enum PacketType {pt_Unknown, pt_Data, pt_ReplySuccess, pt_ReplyInvalid, pt_Break, pt_Start};
|
|
||||||
|
|
||||||
struct StartRequest {
|
|
||||||
uint packets;
|
|
||||||
ullong size;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const uint signature;
|
|
||||||
int packet_size;
|
|
||||||
double timeout_;
|
|
||||||
PIVector<PIVector<Part> > session;
|
|
||||||
PIVector<PacketType> replies;
|
|
||||||
PacketHeader header;
|
|
||||||
CRC_16 crc;
|
|
||||||
|
|
||||||
void processData(int id, PIByteArray &data);
|
|
||||||
PIByteArray build_packet(int id);
|
|
||||||
int checkSession();
|
|
||||||
bool send_process();
|
|
||||||
void sendBreak(int session_id);
|
|
||||||
void sendReply(PacketType reply);
|
|
||||||
bool getStartRequest();
|
|
||||||
bool finish_send(bool ok);
|
|
||||||
void finish_receive(bool ok);
|
|
||||||
};
|
|
||||||
|
|
||||||
inline PIByteArray & operator <<(PIByteArray & s, const PIBaseTransfer::PacketHeader & v) {s << v.sig << v.type << v.session_id << v.id; return s;}
|
|
||||||
inline PIByteArray & operator >>(PIByteArray & s, PIBaseTransfer::PacketHeader & v) {s >> v.sig >> v.type >> v.session_id >> v.id; return s;}
|
|
||||||
|
|
||||||
inline PIByteArray & operator <<(PIByteArray & s, const PIBaseTransfer::Part & v) {s << v.id << v.size << v.start; return s;}
|
|
||||||
inline PIByteArray & operator >>(PIByteArray & s, PIBaseTransfer::Part & v) {s >> v.id >> v.size >> v.start; return s;}
|
|
||||||
|
|
||||||
|
|
||||||
class PIDataTransfer : public PIBaseTransfer
|
class PIDataTransfer : public PIBaseTransfer
|
||||||
{
|
{
|
||||||
@@ -105,11 +9,14 @@ class PIDataTransfer : public PIBaseTransfer
|
|||||||
public:
|
public:
|
||||||
PIDataTransfer() {;}
|
PIDataTransfer() {;}
|
||||||
~PIDataTransfer() {;}
|
~PIDataTransfer() {;}
|
||||||
virtual PIByteArray buildPacket(int id);
|
|
||||||
virtual void receivePart(Part fi, PIByteArray ba, PIByteArray header) {;}
|
bool send(const PIByteArray &ba);
|
||||||
|
const PIByteArray &data() {return data_;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PIByteArray data;
|
virtual PIByteArray buildPacket(Part p);
|
||||||
|
virtual void receivePart(PIBaseTransfer::Part fi, PIByteArray ba, PIByteArray pheader);
|
||||||
|
PIByteArray data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
PIP - Platform Independent Primitives
|
PIP - Platform Independent Primitives
|
||||||
Directory
|
Directory
|
||||||
Copyright (C) 2015 Ivan Pelipenko peri4ko@gmail.com
|
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -185,6 +185,7 @@ bool PIDir::make(bool withParents) {
|
|||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
} else
|
} else
|
||||||
if (makeDir(d.path_)) return true;
|
if (makeDir(d.path_)) return true;
|
||||||
return false;
|
return false;
|
||||||
@@ -217,12 +218,7 @@ PIVector<PIFile::FileInfo> PIDir::entries() {
|
|||||||
# else
|
# else
|
||||||
const_cast<char*>(p.data()), 0
|
const_cast<char*>(p.data()), 0
|
||||||
# endif
|
# endif
|
||||||
, 0,
|
, 0, versionsort);
|
||||||
# ifdef MAC_OS
|
|
||||||
alphasort);
|
|
||||||
# else
|
|
||||||
versionsort);
|
|
||||||
# endif
|
|
||||||
for (int i = 0; i < cnt; ++i) {
|
for (int i = 0; i < cnt; ++i) {
|
||||||
l << PIFile::fileInfo(dp + PIString(list[i]->d_name));
|
l << PIFile::fileInfo(dp + PIString(list[i]->d_name));
|
||||||
delete list[i];
|
delete list[i];
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
/*
|
/*
|
||||||
PIP - Platform Independent Primitives
|
PIP - Platform Independent Primitives
|
||||||
Directory
|
Directory
|
||||||
Copyright (C) 2015 Ivan Pelipenko peri4ko@gmail.com
|
Copyright (C) 2014 Ivan Pelipenko peri4ko@gmail.com
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,480 +2,246 @@
|
|||||||
|
|
||||||
const char PIFileTransfer::sign[] = {'P', 'F', 'T'};
|
const char PIFileTransfer::sign[] = {'P', 'F', 'T'};
|
||||||
|
|
||||||
PIFileTransfer::PIFileTransfer(): crc(standardCRC_16()) {
|
PIFileTransfer::PIFileTransfer() {
|
||||||
for (uint i = 0; i < sizeof(sign); i++)
|
for (uint i = 0; i < sizeof(sign); i++)
|
||||||
header.sig[i] = sign[i];
|
pftheader.sig[i] = sign[i];
|
||||||
header.version = PIFILETRANSFER_VERSION;
|
pftheader.version = __PIFILETRANSFER_VERSION;
|
||||||
header.session_id = 0;
|
pftheader.session_id = 0;
|
||||||
|
pftheader.step = pft_None;
|
||||||
dir = PIDir::current();
|
dir = PIDir::current();
|
||||||
fileinfo_size = sizeof(EntryInfo) + sizeof(FilePart) + 100;
|
|
||||||
min_packet_size = sizeof(PacketHeader) + sizeof(uint) + fileinfo_size;
|
|
||||||
is_sending = is_receiving = false;
|
is_sending = is_receiving = false;
|
||||||
bytes_file_all = bytes_file_cur = bytes_total_all = bytes_total_cur = 0;
|
bytes_file_all = bytes_file_cur = 0;
|
||||||
timeout_ = 1.;
|
// CONNECT(void, this, sendStarted, this, send_started);
|
||||||
setPacketSize(4096);
|
CONNECT(void, this, receiveStarted, this, receive_started);
|
||||||
srand(PISystemTime::current().toMilliseconds());
|
CONNECT1(void, bool, this, sendFinished, this, send_finished);
|
||||||
|
CONNECT1(void, bool, this, receiveFinished, this, receive_finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PIFileTransfer::~PIFileTransfer() {
|
PIFileTransfer::~PIFileTransfer() {
|
||||||
break_ = true;
|
break_ = true;
|
||||||
session.clear();
|
files_.clear();
|
||||||
replies.clear();
|
work_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PIFileTransfer::send(const PIString & file) {
|
bool PIFileTransfer::send(const PIString & file) {
|
||||||
PIVector<PIFile::FileInfo> ce = dir.entries();
|
return send(PIVector<PIFile::FileInfo>() << PIFile::fileInfo(file));
|
||||||
PIFile::FileInfo e;
|
|
||||||
e.path = "";
|
|
||||||
for (int i = 0; i < ce.size_s(); i++) {
|
|
||||||
if (ce[i].path == file) e = ce[i];
|
|
||||||
}
|
|
||||||
if (e.path != "") return sendFiles(PIVector<PIFile::FileInfo>() << e);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PIFileTransfer::send(PIVector<PIFile::FileInfo> entries) {
|
bool PIFileTransfer::send(PIVector<PIFile::FileInfo> entries) {
|
||||||
PIVector<PIFile::FileInfo> allEntries;
|
PIVector<PIFile::FileInfo> allEntries;
|
||||||
PIVector<PIFile::FileInfo> ce;
|
|
||||||
PISet<PIString> names;
|
|
||||||
for (int i = 0; i < entries.size_s(); i++) {
|
for (int i = 0; i < entries.size_s(); i++) {
|
||||||
ce.clear();
|
allEntries << entries[i];
|
||||||
if (entries[i].isDir())
|
if (entries[i].isDir())
|
||||||
ce = PIDir::allEntries(dir.absolutePath() + dir.separator + entries[i].path);
|
allEntries << PIDir::allEntries(dir.absolutePath() + dir.separator + entries[i].path);
|
||||||
for (int k = 0; k < ce.size_s(); k++) ce[k].path = entries[i].path + dir.separator + ce[k].path;
|
|
||||||
ce.push_front(entries[i]);
|
|
||||||
for (int j = 0; j < ce.size_s(); j++) {
|
|
||||||
if (names.contains(ce[j].path)) continue;
|
|
||||||
allEntries << ce[j];
|
|
||||||
//piCout << ce[j].path;
|
|
||||||
names << ce[j].path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return sendFiles(allEntries);
|
return sendFiles(allEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIFileTransfer::stopSend() {
|
bool PIFileTransfer::sendFiles(const PIVector<PIFile::FileInfo> &files) {
|
||||||
if (!is_sending) return;
|
files_ = files;
|
||||||
break_ = true;
|
PIStringList names;
|
||||||
|
// piCoutObj << "prepare to send" << files_.size() << "files";
|
||||||
|
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;
|
||||||
|
if (files_[i].isDir()) files_[i].size = 0;
|
||||||
|
// piCout << "prepare" << i << files_[i].path << files_[i].size;
|
||||||
}
|
}
|
||||||
|
srand(PISystemTime::current().toMilliseconds());
|
||||||
|
pftheader.session_id = rand();
|
||||||
void PIFileTransfer::stopReceive() {
|
sendFilesStarted();
|
||||||
if (!is_receiving) return;
|
desc.clear();
|
||||||
break_ = true;
|
desc << files_;
|
||||||
finish_receive(false);
|
pftheader.step = pft_Description;
|
||||||
|
buildSession(PIVector<Part>() << Part(0, desc.size()));
|
||||||
|
if (!send_process()) return false;
|
||||||
|
pftheader.step = pft_Data;
|
||||||
|
PIVector<Part> pts;
|
||||||
|
for (int i=0; i<files_.size_s(); i++) {
|
||||||
|
pts << Part(i+1, files_[i].size);
|
||||||
}
|
}
|
||||||
|
buildSession(pts);
|
||||||
|
bool ok = send_process();
|
||||||
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) {
|
|
||||||
// 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(FilePart);
|
|
||||||
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;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIFileTransfer::finish_receive(bool ok) {
|
|
||||||
if (ok) state_string = "receive done";
|
void PIFileTransfer::processFile(int id, ullong start, PIByteArray & data) {
|
||||||
else state_string = "receive failed";
|
// piCout << "processFile" << id << files_.size();
|
||||||
// piCoutObj << state_string << PIString::readableSize(bytes_total_all);
|
PIFile::FileInfo fi = files_[id-1];
|
||||||
is_receiving = false;
|
bytes_file_all = fi.size;
|
||||||
|
bytes_file_cur = start;
|
||||||
|
cur_file_string = fi.path;
|
||||||
|
PIString path = dir.absolutePath() + dir.separator + fi.path;
|
||||||
|
// piCout << "receive" << path << fi.size << data.size();
|
||||||
|
if (fi.isDir()) {
|
||||||
|
// piCoutObj << "make dir" << fi.entry.path;
|
||||||
|
if (!PIDir::make(path)) {
|
||||||
|
cur_file_string = "ERROR! while create directory " + path;
|
||||||
|
piCoutObj << cur_file_string;
|
||||||
|
stopReceive();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fi.isFile()) {
|
||||||
|
if (work_file.path() != path || !work_file.isOpened()) {
|
||||||
work_file.close();
|
work_file.close();
|
||||||
work_file.setPath("");
|
if (!work_file.open(path, PIIODevice::ReadWrite)) {
|
||||||
bytes_file_all = bytes_file_cur = 0;
|
cur_file_string = "ERROR! while open file " + path;
|
||||||
bytes_total_all = bytes_total_cur = 0;
|
piCoutObj << cur_file_string;
|
||||||
if (!ok) sendBreak(header.session_id);
|
stopReceive();
|
||||||
finishReceive(ok);
|
return;
|
||||||
|
}
|
||||||
|
if (work_file.size() > fi.size) {
|
||||||
|
// piCoutObj << "error size" << work_file.size() << fi.size;
|
||||||
|
work_file.clear();
|
||||||
|
work_file.resize(fi.size);
|
||||||
|
// piCoutObj << "correct size" << work_file.size() << fi.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() < (llong)start) {
|
||||||
|
// piCoutObj << "error pos size" << work_file.pos() << fi.fstart;
|
||||||
|
work_file.resize(start);
|
||||||
|
// piCoutObj << "correct pos size" << work_file.pos() << fi.fstart;
|
||||||
|
}
|
||||||
|
if (work_file.size() > fi.size) {
|
||||||
|
piCoutObj << "****** error size" << work_file.size() << fi.size;
|
||||||
|
work_file.clear();
|
||||||
|
work_file.resize(fi.size);
|
||||||
|
piCoutObj << "****** correct size" << work_file.size() << fi.size;
|
||||||
|
}
|
||||||
|
// if (fi.fstart != work_file.pos()) piCoutObj << "error pos" << work_file.pos() << fi.fstart;
|
||||||
|
work_file.seek(start);
|
||||||
|
int rs = work_file.write(data.data(), data.size());
|
||||||
|
if (rs != data.size_s()) {
|
||||||
|
cur_file_string = "ERROR! while write file " + fi.path + " (must " + PIString::fromNumber(data.size()) + ", but write " + PIString::fromNumber(rs) + ")";
|
||||||
|
piCoutObj << cur_file_string;
|
||||||
|
stopReceive();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PIByteArray PIFileTransfer::buildPacket(Part p) {
|
||||||
|
// piCoutObj << "Step" << pftheader.step;
|
||||||
|
// piCoutObj << "session id" << pftheader.session_id;
|
||||||
|
PIByteArray ba;
|
||||||
|
switch(pftheader.step) {
|
||||||
|
case pft_None:
|
||||||
|
stopSend();
|
||||||
|
return PIByteArray();
|
||||||
|
case pft_Description:
|
||||||
|
ba.resize(p.size);
|
||||||
|
memcpy(ba.data(), desc.data(p.start), p.size);
|
||||||
|
return ba;
|
||||||
|
case pft_Data:
|
||||||
|
// piCout << "send data" << p.id << files_.size();
|
||||||
|
PIFile::FileInfo fi = files_[p.id-1];
|
||||||
|
if (fi.isFile()) {
|
||||||
|
// piCout << "send file" << fi.name() << fi.size;
|
||||||
|
PIString path = dir.absolutePath() + dir.separator + fi.path;
|
||||||
|
if (work_file.path() != path || !work_file.isOpened()) {
|
||||||
|
if (!work_file.open(path, PIIODevice::ReadOnly)) {
|
||||||
|
break_ = true;
|
||||||
|
cur_file_string = "ERROR! while open file " + fi.path;
|
||||||
|
piCoutObj << cur_file_string;
|
||||||
|
stopSend();
|
||||||
|
return PIByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
work_file.seek(p.start);
|
||||||
|
ba.resize(p.size);
|
||||||
|
int rs = work_file.read(ba.data(), ba.size());
|
||||||
|
if ((llong)rs != (llong)p.size) {
|
||||||
|
break_ = true;
|
||||||
|
cur_file_string = "ERROR! while read file " + fi.path + " (must " + PIString::fromNumber(p.size) + ", but read " + PIString::fromNumber(rs) + ")";
|
||||||
|
piCoutObj << cur_file_string;
|
||||||
|
stopSend();
|
||||||
|
return PIByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if (fi.isDir()) {
|
||||||
|
// piCout << "create dir" << fi.path;
|
||||||
|
// dir.make(fi.path);
|
||||||
|
// }
|
||||||
|
cur_file_string = fi.path;
|
||||||
|
bytes_file_all = fi.size;
|
||||||
|
bytes_file_cur = p.start;
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
return PIByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIFileTransfer::receivePart(PIBaseTransfer::Part fi, PIByteArray ba, PIByteArray pheader) {
|
||||||
|
PFTHeader h;
|
||||||
|
// piCout << pheader.size() << sizeof(PFTHeader);
|
||||||
|
pheader >> h;
|
||||||
|
// piCout << h.session_id;
|
||||||
|
StepType st = (StepType)h.step;
|
||||||
|
pftheader.step = st;
|
||||||
|
if (!h.check_sig()) {
|
||||||
|
cur_file_string = "ERROR File Transfer uncompatable! or invalid version";
|
||||||
|
piCoutObj << cur_file_string;
|
||||||
|
stopReceive();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch(st) {
|
||||||
|
case pft_None:
|
||||||
|
break;
|
||||||
|
case pft_Description:
|
||||||
|
pftheader.session_id = h.session_id;
|
||||||
|
if (desc.size() < fi.start + fi.size) desc.resize(fi.start + fi.size);
|
||||||
|
memcpy(desc.data(fi.start), ba.data(), ba.size_s());
|
||||||
|
break;
|
||||||
|
case pft_Data:
|
||||||
|
if (h.session_id == pftheader.session_id)
|
||||||
|
processFile(fi.id, fi.start, ba);
|
||||||
|
else stopReceive();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PIByteArray PIFileTransfer::customHeader() {
|
||||||
|
PIByteArray ba;
|
||||||
|
ba << pftheader;
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIFileTransfer::receive_started() {
|
||||||
|
if (pftheader.step == pft_None) {
|
||||||
|
files_.clear();
|
||||||
|
receiveFilesStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIFileTransfer::receive_finished(bool ok) {
|
||||||
|
if (pftheader.step == pft_Description) {
|
||||||
|
if (ok) desc >> files_;
|
||||||
|
else {
|
||||||
|
pftheader.session_id--;
|
||||||
|
receiveFilesFinished(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pftheader.step == pft_Data) receiveFilesFinished(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIFileTransfer::send_finished(bool ok) {
|
||||||
|
sendFilesFinished(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,38 +2,21 @@
|
|||||||
#define PIFILETRANSFER_H
|
#define PIFILETRANSFER_H
|
||||||
|
|
||||||
#include "pidir.h"
|
#include "pidir.h"
|
||||||
#include "picrc.h"
|
#include "pibasetransfer.h"
|
||||||
|
|
||||||
#define PIFILETRANSFER_VERSION 1
|
#define __PIFILETRANSFER_VERSION 2
|
||||||
|
|
||||||
class PIFileTransfer : public PIObject
|
class PIFileTransfer : public PIBaseTransfer
|
||||||
{
|
{
|
||||||
PIOBJECT(PIFileTransfer)
|
PIOBJECT_SUBCLASS(PIFileTransfer, PIBaseTransfer)
|
||||||
public:
|
public:
|
||||||
PIFileTransfer();
|
PIFileTransfer();
|
||||||
~PIFileTransfer();
|
~PIFileTransfer();
|
||||||
|
|
||||||
enum ReplyCode {Unknown = 0, Success, Invalid, Break};
|
enum StepType {pft_None, pft_Description, pft_Data};
|
||||||
enum PacketType {pt_Data = 1, pt_Reply, pt_SendRequest};//, pt_SendResult};
|
|
||||||
|
|
||||||
struct EntryInfo {
|
#pragma pack(push,1)
|
||||||
EntryInfo() {
|
struct PFTHeader {
|
||||||
fstart = fsize = 0;
|
|
||||||
parts = part_index = 0;
|
|
||||||
}
|
|
||||||
PIFile::FileInfo entry;
|
|
||||||
llong fstart;
|
|
||||||
llong fsize;
|
|
||||||
uint parts;
|
|
||||||
uint part_index;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FilePart {
|
|
||||||
uint index;
|
|
||||||
PIByteArray data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PacketHeader {
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
char sig[3]; // PFT
|
char sig[3]; // PFT
|
||||||
@@ -41,93 +24,64 @@ public:
|
|||||||
};
|
};
|
||||||
uint raw_sig;
|
uint raw_sig;
|
||||||
};
|
};
|
||||||
int type; // PacketType
|
int step; // PacketType
|
||||||
uint session_id;
|
int session_id;
|
||||||
uint id;
|
|
||||||
bool check_sig() {
|
bool check_sig() {
|
||||||
if (sig[0] != sign[0] || sig[1] != sign[1] || sig[2] != sign[2] || version != PIFILETRANSFER_VERSION) return false;
|
if (sig[0] != sign[0] || sig[1] != sign[1] || sig[2] != sign[2] || version != __PIFILETRANSFER_VERSION) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// uint crc;
|
|
||||||
};
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
//bool send(const PIFile & file);
|
bool send(const PIFile & file);
|
||||||
bool send(const PIString & file);
|
bool send(const PIString & file);
|
||||||
// bool send(const PIStringList &files);
|
bool send(const PIStringList &files);
|
||||||
bool send(PIFile::FileInfo entry) {return send(PIVector<PIFile::FileInfo>() << entry);}
|
bool send(PIFile::FileInfo entry) {return send(PIVector<PIFile::FileInfo>() << entry);}
|
||||||
bool send(PIVector<PIFile::FileInfo> entries);
|
bool send(PIVector<PIFile::FileInfo> entries);
|
||||||
|
|
||||||
void stopSend();
|
|
||||||
void stopReceive();
|
|
||||||
|
|
||||||
bool isSending() const {return is_sending;}
|
|
||||||
bool isReceiving() const {return is_receiving;}
|
|
||||||
|
|
||||||
void setPacketSize(int size) {max_packet_size = size;}
|
|
||||||
int packetSize() const {return max_packet_size;}
|
|
||||||
|
|
||||||
void setTimeout(double sec) {timeout_ = sec;}
|
|
||||||
double timeout() const {return timeout_;}
|
|
||||||
|
|
||||||
void setDirectory(const PIDir &d) {dir = d;}
|
void setDirectory(const PIDir &d) {dir = d;}
|
||||||
void setDirectory(const PIString &path) {dir.setDir(path);}
|
void setDirectory(const PIString &path) {dir.setDir(path);}
|
||||||
PIDir directory() const {return dir;}
|
PIDir directory() const {return dir;}
|
||||||
|
|
||||||
const PIString & stateString() const {return state_string;}
|
const PIString & curFile() const {return cur_file_string;}
|
||||||
llong bytesTotalAll() const {return bytes_total_all;}
|
|
||||||
llong bytesTotalCur() const {return bytes_total_cur;}
|
|
||||||
llong bytesFileAll() const {return bytes_file_all;}
|
llong bytesFileAll() const {return bytes_file_all;}
|
||||||
llong bytesFileCur() const {return bytes_file_cur;}
|
llong bytesFileCur() const {return bytes_file_cur;}
|
||||||
const PIString * stateString_ptr() const {return &state_string;}
|
const PIString * curFile_ptr() const {return &cur_file_string;}
|
||||||
const llong * bytesTotalAll_ptr() const {return &bytes_total_all;}
|
|
||||||
const llong * bytesTotalCur_ptr() const {return &bytes_total_cur;}
|
|
||||||
const llong * bytesFileAll_ptr() const {return &bytes_file_all;}
|
const llong * bytesFileAll_ptr() const {return &bytes_file_all;}
|
||||||
const llong * bytesFileCur_ptr() const {return &bytes_file_cur;}
|
const llong * bytesFileCur_ptr() const {return &bytes_file_cur;}
|
||||||
|
|
||||||
EVENT(startReceive)
|
EVENT(receiveFilesStarted)
|
||||||
EVENT1(receiveRequest, bool *, ok)
|
EVENT1(receiveFilesFinished, bool, ok)
|
||||||
EVENT1(finishReceive, bool, ok)
|
EVENT(sendFilesStarted)
|
||||||
EVENT(startSend)
|
EVENT1(sendFilesFinished, bool, ok)
|
||||||
EVENT1(finishSend, bool, ok)
|
|
||||||
|
|
||||||
EVENT1(sendRequest, PIByteArray &, data)
|
|
||||||
EVENT_HANDLER1(void, received, PIByteArray &, data);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const char sign[];
|
static const char sign[];
|
||||||
int max_packet_size, min_packet_size, fileinfo_size;
|
PIVector<PIFile::FileInfo> files_;
|
||||||
bool break_, is_sending, is_receiving;
|
PIString cur_file_string;
|
||||||
double timeout_;
|
llong bytes_file_all, bytes_file_cur;
|
||||||
|
PFTHeader pftheader;
|
||||||
PIVector<PIVector<EntryInfo> > session;
|
|
||||||
PIVector<ReplyCode> replies;
|
|
||||||
PIString state_string;
|
|
||||||
llong bytes_total_all, bytes_total_cur, bytes_file_all, bytes_file_cur;
|
|
||||||
PacketHeader header;
|
|
||||||
PIDir dir;
|
PIDir dir;
|
||||||
PIFile work_file;
|
PIFile work_file;
|
||||||
CRC_16 crc;
|
PIByteArray desc;
|
||||||
|
|
||||||
bool sendFiles(PIVector<PIFile::FileInfo> files);
|
bool sendFiles(const PIVector<PIFile::FileInfo> &files);
|
||||||
int checkSession();
|
void processFile(int id, ullong start, PIByteArray &data);
|
||||||
void buildSession(PIVector<PIFile::FileInfo> files);
|
virtual void receivePart(Part fi, PIByteArray ba, PIByteArray pheader);
|
||||||
void sendBreak(int session_id);
|
virtual PIByteArray buildPacket(Part fi);
|
||||||
void sendReply(ReplyCode reply);
|
virtual PIByteArray customHeader();
|
||||||
bool getSendRequest();
|
// EVENT_HANDLER(void, send_started);
|
||||||
void processData(int id, PIByteArray &data);
|
EVENT_HANDLER(void, receive_started);
|
||||||
PIByteArray buildPacket(int id);
|
EVENT_HANDLER1(void, send_finished, bool, ok);
|
||||||
bool finish_send(bool ok);
|
EVENT_HANDLER1(void, receive_finished, bool, ok);
|
||||||
void finish_receive(bool ok);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline PIByteArray & operator <<(PIByteArray & s, const PIFileTransfer::PacketHeader & v) {s << v.raw_sig << v.type << v.session_id << v.id; return s;}
|
inline PIByteArray & operator <<(PIByteArray & s, const PIFileTransfer::PFTHeader & v) {s << v.raw_sig << v.step << v.session_id; return s;}
|
||||||
inline PIByteArray & operator >>(PIByteArray & s, PIFileTransfer::PacketHeader & v) {s >> v.raw_sig >> v.type >> v.session_id >> v.id; return s;}
|
inline PIByteArray & operator >>(PIByteArray & s, PIFileTransfer::PFTHeader & v) {s >> v.raw_sig >> v.step >> v.session_id; return s;}
|
||||||
|
|
||||||
|
inline PIByteArray & operator <<(PIByteArray & s, const PIFile::FileInfo & v) {s << v.path << v.size << v.time_access << v.time_modification <<
|
||||||
inline PIByteArray & operator <<(PIByteArray & s, const PIFileTransfer::EntryInfo & v) {s << v.entry.path << v.entry.size << v.entry.time_modification << v.entry.time_access << int(v.entry.flags)
|
(int)v.flags << v.id_user << v.id_group << v.perm_user.raw << v.perm_group.raw << v.perm_other.raw; return s;}
|
||||||
<< v.entry.id_user << v.entry.id_group << v.fstart; return s;}
|
inline PIByteArray & operator >>(PIByteArray & s, PIFile::FileInfo & v) {s >> v.path >> v.size >> v.time_access >> v.time_modification >>
|
||||||
inline PIByteArray & operator >>(PIByteArray & s, PIFileTransfer::EntryInfo & v) {s >> v.entry.path >> v.entry.size >> v.entry.time_modification >> v.entry.time_access >> (int&)(v.entry.flags)
|
*(int*)(&(v.flags)) >> v.id_user >> v.id_group >> v.perm_user.raw >> v.perm_group.raw >> v.perm_other.raw; return s;}
|
||||||
>> v.entry.id_user >> v.entry.id_group >> v.fstart; return s;}
|
|
||||||
inline PICout operator <<(PICout s, const PIFileTransfer::EntryInfo & v) {s.setControl(0, true); s << "FileInfo(\"" << v.entry.path << "\", " << PIString::fromNumber(v.entry.flags, 16) << PIString::readableSize(v.entry.size) << " b | " << PIString::readableSize(v.fsize) << " b)"; s.restoreControl(); return s;}
|
|
||||||
|
|
||||||
#endif // PIFILETRANSFER_H
|
#endif // PIFILETRANSFER_H
|
||||||
|
|||||||
Reference in New Issue
Block a user