From df9de38617a62ddc980f9030caecce7fd941ac92 Mon Sep 17 00:00:00 2001 From: afederici <ajf5@illinois.edu> Date: Tue, 24 Nov 2020 16:33:57 -0600 Subject: [PATCH] added file merging --- Wc | Bin 191080 -> 191272 bytes inc/FileObject.h | 2 +- inc/MessageTypes.h | 4 ++ inc/Node.h | 6 +- inc/TcpSocket.h | 8 ++- mappers/wc.cpp | 2 +- src/FileObject.cpp | 7 ++- src/Node.cpp | 121 ++++++++++++++++++++++---------------- src/TcpSocket.cpp | 142 ++++++++++++++++++++++++++++++++++----------- src/TestBench.cpp | 2 +- src/Threads.cpp | 14 +++-- src/Utils.cpp | 2 +- src/main.cpp | 2 +- 13 files changed, 209 insertions(+), 103 deletions(-) diff --git a/Wc b/Wc index 3c1fead01a06843bfc0429b84a8b5916a3292d22..c7dcfbb588800efe9e9e4acf9f0bba8e307328d6 100755 GIT binary patch delta 26935 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 b@9Nt9s+>;KbgPsazVpXTX><D1IsE?sM1&bW delta 26759 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 diff --git a/inc/FileObject.h b/inc/FileObject.h index ca0be72..cc4eb6e 100644 --- a/inc/FileObject.h +++ b/inc/FileObject.h @@ -16,7 +16,7 @@ using namespace std; string getMostRecentFile(string readfile); -void cleanupTmpFiles(); +void cleanupTmpFiles(string match); class FileObject { public: diff --git a/inc/MessageTypes.h b/inc/MessageTypes.h index e3deb01..63368e5 100644 --- a/inc/MessageTypes.h +++ b/inc/MessageTypes.h @@ -30,6 +30,10 @@ enum MessageType { MAPLEACK, CHUNK, //send to nodes so they have information about what kind of get request to send CHUNKACK, //after append ack received, send this back to master to know when things are Done + MERGE, + STARTMERGE, + MERGECOMPLETE, + MERGEFAIL }; enum PayloadType { diff --git a/inc/Node.h b/inc/Node.h index 7d8d123..b43e94b 100644 --- a/inc/Node.h +++ b/inc/Node.h @@ -5,13 +5,14 @@ #include <string> #include <vector> #include <map> +#include <set> #include <pthread.h> #include <time.h> #include <signal.h> #include <unistd.h> #include <dirent.h> #include <sys/types.h> - +#include <fstream> #include <stdio.h> #include <sys/stat.h> #include <unistd.h> @@ -83,7 +84,8 @@ public: map<string, tuple<bool, bool, bool>> pendingRequestSent; //? //master properties for MAPLEJUICE - map<string, vector<tuple<string, string, string>>> mapleProcessing; //ip -> [ (file, chunk_start, originIP) ] + map<string, vector<tuple<string, string>>> mapleProcessing; //ip -> [ (file, chunk_start) ] + map<string, set<tuple<string,string>>> workerTasks; //above is static, this removes tasks when done map<string, tuple<long int, int>> fileSizes; //used so master can partition in the map phase tuple is (bytes, lines) HashRing *mapleRing; map<string, vector<tuple<string, string>>> mapleSending; //originIP -> (file, chunk_start); diff --git a/inc/TcpSocket.h b/inc/TcpSocket.h index 69c9a2b..076f8aa 100644 --- a/inc/TcpSocket.h +++ b/inc/TcpSocket.h @@ -51,15 +51,19 @@ public: queue<string> regMessages;//other messages added here queue<string> pendSendMessages;//keeps messages for the tcp client to send queue<string> mapleMessages; //keeps track of sending + queue<string> mergeMessages; //keeps track of sending void bindServer(string port); - void sendFile(string ip, string port, string localfilename, string sdfsfilename, string remoteLocalfilename, string overwrite); + void sendFile(string ip, string port, FILE * fp, int size); + void putFile(string ip, string port, string localfilename, string sdfsfilename, string remoteLocalfilename); + void putDirectory(string ip, string port); //put everything in tmp directory void sendLines(string ip, string port, string execFile, string localFile, string prefix, int start, int end); void sendMessage(string ip, string port, string message); int messageHandler(int sockfd, string payloadMessage, string returnID); int createConnection(string ip, string port); TcpSocket(); private: - string getFileMetadata(int size, string checksum, string sdfsfilename, string localfilename, string remoteLocalfilename, string overwrite); + string getFileMetadata(int size, string checksum, string sdfsfilename, string localfilename, string remoteLocalfilename); + string getDirMetadata(); }; #endif //TCPSOCKET_H diff --git a/mappers/wc.cpp b/mappers/wc.cpp index 6a8535e..e8b2d6e 100644 --- a/mappers/wc.cpp +++ b/mappers/wc.cpp @@ -9,7 +9,7 @@ int main(int argc, char **argv) { while (std::getline(file, str)) { for (size_t i = 0; i < str.size(); i++) { - if (str[i] == '.' || str[i] == ',' || str[i] == '?' || str[i] == ';' || str[i] == '!') str[i] = ' '; + if (str[i] == '.' || str[i] == ',' || str[i] == '?' || str[i] == ';' || str[i] == '!' || str[i] == '-') str[i] = ' '; } std::transform(str.begin(), str.end(), str.begin(), ::tolower); std::vector<std::string> temp = splitString(str, delim); diff --git a/src/FileObject.cpp b/src/FileObject.cpp index 5042a49..209f5df 100644 --- a/src/FileObject.cpp +++ b/src/FileObject.cpp @@ -28,20 +28,20 @@ string getMostRecentFile(string readfile){ DIR *dp = nullptr; int matchLen = readfile.size(); vector<string> fileVersions; - if ((dp = opendir(".")) == nullptr) { cout << "tmp directory error " << endl; return ""; } + if ((dp = opendir(".")) == nullptr) { cout << "temp directory error " << endl; closedir(dp); return ""; } while ((entry = readdir(dp))){ if (strncmp(entry->d_name, readfile.c_str(), matchLen) == 0){ fileVersions.push_back(entry->d_name); } } sort(fileVersions.begin(), fileVersions.end()); + closedir(dp); return fileVersions[fileVersions.size()-1]; } -void cleanupTmpFiles(){ +void cleanupTmpFiles(string match){ struct dirent *entry = nullptr; DIR *dp = nullptr; - string match = "tmp-"; int matchLen = match.size(); if ((dp = opendir(".")) == nullptr) { cout << "tmp directory error " << endl;} while ((entry = readdir(dp))){ @@ -49,4 +49,5 @@ void cleanupTmpFiles(){ remove(entry->d_name); } } + closedir(dp); } diff --git a/src/Node.cpp b/src/Node.cpp index d603037..2905d03 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -178,7 +178,9 @@ int Node::failureDetection(){ auto vecCopy(mapleProcessing[get<0>(keyTuple)]); mapleProcessing[get<0>(mapleNodes[0])] = vecCopy; + for (auto el : vecCopy) workerTasks[get<0>(mapleNodes[0])].insert(el); mapleProcessing.erase(get<0>(keyTuple)); + workerTasks.erase(get<0>(keyTuple)); for (auto &e : mapleSending[get<0>(keyTuple)]){ vector<int> temp = randItems(1, fileList[get<0>(e)]); @@ -810,15 +812,16 @@ void Node::handleTcpMessage() // cout << "Has " << msg.type << " with " << msg.payload << endl; switch (msg.type) { case JUICESTART: { - if (mapleProcessing.size()) {tcpServent->regMessages.push(msg.toString()); break;} - //TODO + if (workerTasks.size()) {tcpServent->regMessages.push(msg.toString()); break;} + //TODO JUICESTART + //tell all nodes they can delete tmp files. } case MAPLESTART: { //leader only function //currently running something, dont start a new phase - if (mapleProcessing.size()) {tcpServent->regMessages.push(msg.toString()); cout << "[MAPLE] already mapling" << endl; break;} + if (workerTasks.size()) {tcpServent->regMessages.push(msg.toString()); cout << "[MAPLE] already mapling" << endl; break;} cout << "[MAPLE] Leader starting new Maple phase" << endl; - cleanupTmpFiles(); + cleanupTmpFiles("tmp-"); if (inMsg.size() >= 4){ string mapleExe = inMsg[0], num_maples = inMsg[1], sdfsPre = inMsg[2], sdfs_dir = inMsg[3] + "-"; int workers = stoi(num_maples); @@ -860,7 +863,8 @@ void Node::handleTcpMessage() vector<int> temp = randItems(1, fileList[file]); string sender = hashRing->getValue(temp[0]); //because files are part of sdfs anyone can be the sender string processor = mapleRing->getValue(id); //processor is a maple worker - mapleProcessing[processor].push_back(make_tuple(file, to_string(start), sender)); + mapleProcessing[processor].push_back(make_tuple(file, to_string(start))); + workerTasks[processor].insert(make_tuple(file, to_string(start))); cout << "[MAPLE] assign file " << file << " at " << to_string(start) << " to " << processor << endl; mapleSending[sender].push_back(make_tuple(file, to_string(start))); string maplemsg = sender + "::" + processor + "::" +mapleExe + "::" + s + "::" + sdfsPre; @@ -896,7 +900,7 @@ void Node::handleTcpMessage() int dataPipe[2]; if (pipe(dataPipe)){ fprintf (stderr, "Pipe failed.\n"); break; } pid_t pid = fork(); - if (pid){ //parent process, DONT need to waitpid because of signal handler set up + if (pid){ close(dataPipe[1]); handlePipe(dataPipe[0]); } else if (pid < 0) { @@ -912,37 +916,6 @@ void Node::handleTcpMessage() int status; waitpid(pid, &status, 0); close(dataPipe[0]);close(dataPipe[1]); - //go thorugh and process things from datapipe - //if processing success, send out TCP MAPLEACK - string match = "tmp-"; - int matchLen = match.size(); - struct dirent *entry = nullptr; - DIR *dp = nullptr; - if ((dp = opendir(".")) == nullptr) { cout << "tmp directory error " << match << endl; break; } - while ((entry = readdir(dp))){ - //cout << "[FILES] found " << entry->d_name << " looking to match " << to_string(matchLen) << " chars from " << match << endl; - if (strncmp(entry->d_name, match.c_str(), matchLen) == 0){ - vector<string> tempVec = splitString(entry->d_name, "-"); - if (tempVec.size() != 2) continue; //temp keys in form tmp-key - string keyFile = inMsg[5] + "-" + tempVec[tempVec.size()-1]; - cout << "[CHUNKACK] transform from: " << entry->d_name << " to " << keyFile << endl; - - /* TODO - * Need to change up how key comnbining works (also need master to keep track of all keys it sees for the next phase) - * 1) Master keeps track of the status of processing node assignments - * 2) on failure, instead of partial re-mapping, the entire node's assignments get redone - * 3) No longer merge temp files after assignment, instead just send the MAPLEACK - * 4) When the master marks all of a nodes assignments as done, send a message telling it to start merging - * 4b) may need to modify from PUT to a dedicated merge message - * 5) on success, notify the master and only then can the master remove the worker node from map of remaining tasks - */ - Messages outMsg(DNS, nodeInformation.ip + "::" + to_string(hashRingPosition) + "::" + keyFile + "::" + entry->d_name + "::" + to_string(-1) + "::" + to_string(-1) + "::" + keyFile + "::" + "0"); - - //cout << "[PUT] Got localfilename: " << entry->d_name << " with sdfsfilename: " << target << endl; - tcpServent->sendMessage(leaderIP, TCPPORT, outMsg.toString()); - } - } - closedir(dp); string ackStr = inMsg[0] + "::" + inMsg[4] + "::" + inMsg[2]; //IP, file, chunk Messages ackMsg(MAPLEACK, ackStr); tcpServent->sendMessage(leaderIP, TCPPORT, ackMsg.toString()); @@ -964,17 +937,65 @@ void Node::handleTcpMessage() } case MAPLEACK: { - vector<tuple<string,string,string>> temp; - for (auto &e : mapleProcessing[inMsg[0]]){ + vector<tuple<string,string>> temp; + for (auto &e : workerTasks[inMsg[0]]){ if (get<0>(e).compare(inMsg[1]) == 0){ if (get<1>(e).compare(inMsg[2]) == 0){ - continue; + temp.push_back(e); } } - temp.push_back(e); } - if (temp.size()) mapleProcessing[inMsg[0]] = temp; - else mapleProcessing.erase(inMsg[0]); + for (auto &e : temp) workerTasks[inMsg[0]].erase(e); + if (!workerTasks[inMsg[0]].size()) { + Messages outMsg(STARTMERGE, ""); + this->tcpServent->sendMessage(inMsg[0], TCPPORT, outMsg.toString()); + } + break; + } + + case STARTMERGE: { + string sendMsg = hashRing->getValue(leaderPosition) + "::" + TCPPORT; + this->tcpServent->mergeMessages.push(sendMsg); + } + + case MERGECOMPLETE: { + workerTasks.erase(inMsg[0]); + mapleProcessing.erase(inMsg[0]); + //actually merge files in + struct dirent *entry = nullptr; + DIR *dp = nullptr; + string match = "tmp-" + inMsg[0] + "-"; + int matchLen = match.size(); + if ((dp = opendir(".")) == nullptr) { cout << "tmp directory error " << endl;} + cout << "[MERGECOMPLETE] processing files matching " << match << " and replacing with prefix: " << sdfsPre << endl; + while ((entry = readdir(dp))){ + if (strncmp(entry->d_name, match.c_str(), matchLen) == 0){ + string entryName(entry->d_name); + string mapleOutput = sdfsPre + "-" + entryName.substr(matchLen); + ofstream keyFile; + keyFile.open(mapleOutput, ios::app); + ifstream toMerge(entry->d_name); + if (!toMerge.is_open() || !keyFile.is_open()) { + cout << "bad file permissions for " << entry->d_name << " and/or " << mapleOutput << endl; + break; + } + keyFile << toMerge.rdbuf(); + keyFile.close(); + } + } + + if (!mapleProcessing.size()) { //start replication of key files + + } + + break; + } + + //because TCP if we get a fail, we know that the node failed + //so re-requesting the files to merge will be taken care of in failureDetection() + case MERGEFAIL: { + string tmpFiles = "tmp-" + inMsg[0] + "-"; + cleanupTmpFiles(tmpFiles); break; } @@ -1047,7 +1068,7 @@ void Node::handleTcpMessage() if(isLeader){ // Check hashring, get positions and send out DNS ANS isBlackout = true; - if(inMsg.size() >= 8){ + if(inMsg.size() >= 7){ string inMsgIP = inMsg[0]; int nodePosition = stoi(inMsg[1]); string sdfsfilename = inMsg[2]; @@ -1055,7 +1076,6 @@ void Node::handleTcpMessage() long int size = stol(inMsg[4]); int lines = stoi(inMsg[5]); string overwriteFilename = inMsg[6]; - string overwrite = inMsg[7]; //cout << "[DNS] Got " << "inMsgIP: " << inMsgIP << ", sdfsfilename: " << sdfsfilename; //cout << ", localfilename: " << localfilename << ", pos: " << nodePosition << endl; // update fileList, client itself is one of the replicas @@ -1082,7 +1102,7 @@ void Node::handleTcpMessage() pendingRequests[sdfsfilename] = tuple<int, int, int>(closestNode, pred, succ); pendingRequestSent[sdfsfilename] = tuple<int, int, int>(true, false, false); pendingSenderRequests[sdfsfilename] = tuple<string, string, string>(inMsgIP, "", ""); - Messages outMsg(DNSANS, to_string(closestNode) + "::" + localfilename + "::" + sdfsfilename + "::" + overwriteFilename + "::" + overwrite); + Messages outMsg(DNSANS, to_string(closestNode) + "::" + localfilename + "::" + sdfsfilename + "::" + overwriteFilename); this->tcpServent->sendMessage(inMsgIP, TCPPORT, outMsg.toString()); } } @@ -1091,14 +1111,14 @@ void Node::handleTcpMessage() } case DNSANS:{ // Read the answer and send a PUT msg to dest - if(inMsg.size() >= 5){ + if(inMsg.size() >= 4){ int nodePosition = stoi(inMsg[0]); // since we do not keep files in hashRing, the value itself is IPaddress, not NODE:IP_Address string nodeIP = hashRing->getValue(nodePosition); //cout << "nodeIP " << nodeIP << endl; //cout << "[DNSANS] " << "we will put sdfsfilename: " << inMsg[2] << " to nodeIP: " << nodeIP; //cout << " using localfilename: " << inMsg[1] << endl; - string sendMsg = nodeIP+"::"+inMsg[1]+"::"+inMsg[2]+"::"+inMsg[3]+"::"+inMsg[4]; + string sendMsg = nodeIP+"::"+inMsg[1]+"::"+inMsg[2]+"::"+inMsg[3]; this->tcpServent->pendSendMessages.push(sendMsg); } break; @@ -1113,7 +1133,7 @@ void Node::handleTcpMessage() string localfilename = this->localFilelist[sdfsfilename]; cout << "[REREPLICATEGET] Got a request of sdfsfilename " << sdfsfilename << " to nodeIP " << nodeIP << endl; cout << "[REREPLICATEGET] Put localfilename " << localfilename << " to nodeIP " << nodeIP << endl; - string sendMsg = nodeIP+"::"+localfilename+"::"+sdfsfilename+"::"+remoteLocalfilename+"::"+"1"; + string sendMsg = nodeIP+"::"+localfilename+"::"+sdfsfilename+"::"+remoteLocalfilename; this->tcpServent->pendSendMessages.push(sendMsg); } break; @@ -1128,9 +1148,8 @@ void Node::handleTcpMessage() string localfilename = this->localFilelist[sdfsfilename]; cout << "[REREPLICATE] Got a request of sdfsfilename " << sdfsfilename << " to nodeIP " << nodeIP << endl; cout << "[REREPLICATE] Put localfilename " << localfilename << " to nodeIP " << nodeIP << endl; - string sendMsg = nodeIP+"::"+localfilename+"::"+sdfsfilename+"::"+"::"+"1"; + string sendMsg = nodeIP+"::"+localfilename+"::"+sdfsfilename+"::"; this->tcpServent->pendSendMessages.push(sendMsg); - //this->tcpServent->sendFile(nodeIP, TCPPORT, localfilename, sdfsfilename, ""); } break; } diff --git a/src/TcpSocket.cpp b/src/TcpSocket.cpp index 22b1d8d..c923da1 100644 --- a/src/TcpSocket.cpp +++ b/src/TcpSocket.cpp @@ -85,39 +85,89 @@ void TcpSocket::bindServer(string port) } string TcpSocket::getFileMetadata(int size, string checksum, - string sdfsfilename, string localfilename, string remoteLocalfilename, string overwrite) + string sdfsfilename, string localfilename, string remoteLocalfilename) { // format: size,checksum,sdfsfilename - string msg = to_string(size) + "," + checksum + "," + sdfsfilename+","+localfilename+","+remoteLocalfilename+","+overwrite; + string msg = to_string(size) + "," + checksum + "," + sdfsfilename+","+localfilename+","+remoteLocalfilename; return msg; } -void TcpSocket::sendFile(string ip, string port, - string localfilename, string sdfsfilename, string remoteLocalfilename, string overwrite) +string TcpSocket::getDirMetadata() { - int numbytes, sockfd; - char buf[DEFAULT_TCP_BLKSIZE]; - FILE *fp; - int size = 0, sendSize = 0; - bzero(buf, sizeof(buf)); - if ((sockfd = createConnection(ip, port)) == -1) return; - fp = fopen(localfilename.c_str(), "rb"); + struct dirent *entry = nullptr; + DIR *dp = nullptr; + FILE * fp; + string match = "tmp-"; + int matchLen = match.size(); + vector<string> split; + int size = 0; + string msg; + if ((dp = opendir(".")) == nullptr) { cout << "tmp directory error " << endl;} + while ((entry = readdir(dp))){ + if (strncmp(entry->d_name, match.c_str(), matchLen) == 0){ + split.clear(); + split = splitString(entry->d_name, "-"); + if (split.size() > 2) continue; + fp = fopen(entry->d_name, "rb"); + if (fp == NULL) { + printf("Could not open file to send."); + continue; + } + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + if (msg.size()) msg += ","; + msg += split[1]; + msg += to_string(size); + fclose(fp); + } + } + closedir(dp); + return msg; +} + +void TcpSocket::putDirectory(string ip, string port) { + string toSend = getDirMetadata(); + if (!toSend.size()) return; + Messages msg(MERGE, toSend); + sendMessage(ip, port, msg.toString()); + vector<string> toProcess = splitString(toSend, ","); + FILE * fp; + int index = 0; + while (index < toProcess.size() - 1){ + fp = fopen(toProcess[index].c_str(), "rb"); + if (fp == NULL) { + printf("Could not open file to send."); + continue; + } + sendFile(ip, port, fp, stoi(toProcess[index+1])); + fclose(fp); + index += 2; + } +} + + +void TcpSocket::putFile(string ip, string port, string localfilename, string sdfsfilename, string remoteLocalfilename){ + FILE *fp = fopen(localfilename.c_str(), "rb"); if (fp == NULL) { printf("Could not open file to send."); return; } fseek(fp, 0, SEEK_END); - size = ftell(fp); + int size = ftell(fp); fseek(fp, 0, SEEK_SET); - - // send bytes and filename first FileObject f(localfilename); - Messages msg(PUT, getFileMetadata(size, f.checksum, sdfsfilename, localfilename, remoteLocalfilename, overwrite)); - string payload = msg.toString(); - if (send(sockfd, payload.c_str(), strlen(payload.c_str()), 0) == -1) { - perror("send"); - } + Messages msg(PUT, getFileMetadata(size, f.checksum, sdfsfilename, localfilename, remoteLocalfilename)); + sendMessage(ip, port, msg.toString()); + sendFile(ip, port, fp, size); +} + +void TcpSocket::sendFile(string ip, string port, FILE * fp, int size) { sleep(1); + int numbytes, sockfd, sendSize; + if ((sockfd = createConnection(ip, port)) == -1) return; + char buf[DEFAULT_TCP_BLKSIZE]; + bzero(buf, sizeof(buf)); while (!feof(fp) && size > 0) { sendSize = (size < DEFAULT_TCP_BLKSIZE) ? size : DEFAULT_TCP_BLKSIZE; bzero(buf, sizeof(buf)); @@ -214,7 +264,8 @@ int TcpSocket::createConnection(string ip, string port){ int TcpSocket::messageHandler(int sockfd, string payloadMessage, string returnIP){ char buf[DEFAULT_TCP_BLKSIZE]; - int numbytes = 0; + int numbytes = 0, filesize = 0, byteReceived = 0; + FILE *fp; Messages msg(payloadMessage); switch (msg.type) { case ELECTION: @@ -222,22 +273,45 @@ int TcpSocket::messageHandler(int sockfd, string payloadMessage, string returnIP qMessages.push(payloadMessage); break; } + case MERGE: { + vector<string> filesAndSizes = splitString(msg.payload, ","); + int index = 0; + int fail = 0; + while (index < filesAndSizes.size() - 1){ + string filename = "tmp-" + returnIP + "-" + filesAndSizes[index]; + filesize = stoi(filesAndSizes[index+1]); + numbytes = 0; + byteReceived = 0; + fp = fopen(filename.c_str(), "wb"); + bzero(buf, sizeof(buf)); + while ((numbytes=recv(sockfd, buf, DEFAULT_TCP_BLKSIZE, 0)) > 0) { + fwrite(buf, sizeof(char), numbytes, fp); + byteReceived += numbytes; + if (byteReceived >= filesize) { + break; + } + bzero(buf, sizeof(buf)); + } + if (byteReceived < filesize) fail = 1; + cout << "we have " << to_string(byteReceived) << " bytes from this connection" << endl; + fclose(fp); + index += 2; + } + if (fail) { Messages ack(MERGEFAIL, returnIP + "::"); regMessages.push(ack.toString()); } + else { Messages ack(MERGECOMPLETE, returnIP + "::"); regMessages.push(ack.toString()); } + break; + } case PUT: { - FILE *fp; - int filesize = 0, byteReceived = 0; - string mode = "wb"; - string sdfsfilename = "", incomingChecksum = "", remoteLocalname = "", overwriteFilename = "", prefix = "", overwrite = "", localfilename = ""; + string sdfsfilename = "", incomingChecksum = "", remoteLocalname = "", overwriteFilename = "", prefix = "", localfilename = ""; // format: size,checksum,sdfsfilename vector<string> fields = splitString(msg.payload, ","); int start = -1; - if (fields.size() == 6) { + if (fields.size() == 5) { filesize = stoi(fields[0]); incomingChecksum = fields[1]; sdfsfilename = fields[2]; remoteLocalname = fields[3]; overwriteFilename = fields[4]; - overwrite = fields[5]; - if ((stoi(overwrite)) == 0) mode = "ab"; cout << "[PUT] file is " << sdfsfilename << " with size " << filesize << " and checksum " << incomingChecksum << endl; time_t fileTimestamp; time(&fileTimestamp); @@ -247,7 +321,8 @@ int TcpSocket::messageHandler(int sockfd, string payloadMessage, string returnIP //cout << "it's GET with filename " << overwriteFilename << endl; } //cout << "backup filename " << localfilename << endl; - } else { + } + if (fields.size() == 6){ //size, exec, read, start, tmp, prefix filesize = stoi(fields[0]); localfilename = fields[4]; //tempfile to read from @@ -257,13 +332,7 @@ int TcpSocket::messageHandler(int sockfd, string payloadMessage, string returnIP prefix = fields[5]; cout << "[PUT] bytes: " << filesize << " exec: " << sdfsfilename << ", actual: " << remoteLocalname << ", start: " << to_string(start) << ", temp: " << localfilename << ", prefix: " << prefix << endl; } - fp = fopen(localfilename.c_str(), mode.c_str()); - if (fp == NULL) { - cout << "file error" << endl; - close(sockfd); - return 1; - } - + fp = fopen(localfilename.c_str(), "wb"); bzero(buf, sizeof(buf)); while ((numbytes=recv(sockfd, buf, DEFAULT_TCP_BLKSIZE, 0)) > 0) { fwrite(buf, sizeof(char), numbytes, fp); @@ -305,6 +374,9 @@ int TcpSocket::messageHandler(int sockfd, string payloadMessage, string returnIP case MAPLEACK: case CHUNK: case CHUNKACK: + case STARTMERGE: + case MERGECOMPLETE: + case MERGEFAIL: case DNS:{ //cout << "["<< messageTypes[msg.type] << "] payloadMessage: " << payloadMessage << endl; regMessages.push(payloadMessage); //handle from queue diff --git a/src/TestBench.cpp b/src/TestBench.cpp index 94d1255..93cb1ce 100644 --- a/src/TestBench.cpp +++ b/src/TestBench.cpp @@ -118,7 +118,7 @@ void *runTcpClientTEST(void *tcpSocket) // testing to send file for (int i=0; i<2; i++) { sleep(1); - tcp->sendFile("127.0.0.1", TCPPORT, "file_example_MP3_700KB.mp3", "file_example_MP3_700KB.mp3", "file_example_MP3_700KB.mp3", "1"); + tcp->putFile("127.0.0.1", TCPPORT, "file_example_MP3_700KB.mp3", "file_example_MP3_700KB.mp3", "file_example_MP3_700KB.mp3"); } pthread_exit(NULL); } diff --git a/src/Threads.cpp b/src/Threads.cpp index dea6f26..091d391 100644 --- a/src/Threads.cpp +++ b/src/Threads.cpp @@ -24,7 +24,7 @@ void *runTcpSender(void *tcpSocket) while (!tcp->mapleMessages.empty()) { vector<string> msgSplit = splitString(tcp->mapleMessages.front(), "::"); string removeSender = tcp->mapleMessages.front().substr(msgSplit[0].size() + 2); - cout << "[TEST] " << removeSender << endl; + //cout << "[TEST] " << removeSender << endl; Messages msg(CHUNK, removeSender); //processor, exec, file, start, prefix tcp->sendMessage(msgSplit[0], TCPPORT, msg.toString()); @@ -32,9 +32,8 @@ void *runTcpSender(void *tcpSocket) } while (!tcp->pendSendMessages.empty()) { vector<string> msgSplit = splitString(tcp->pendSendMessages.front(), "::"); - if (msgSplit.size() >= 5) { - string nodeIP = msgSplit[0], localfilename = msgSplit[1], overwrite = msgSplit[4]; - string sdfsfilename = msgSplit[2], remoteLocalfilename = msgSplit[3]; + if (msgSplit.size() >= 4) { + string nodeIP = msgSplit[0], localfilename = msgSplit[1], sdfsfilename = msgSplit[2], remoteLocalfilename = msgSplit[3]; cout << "[DOSEND] nodeIP " << nodeIP << ", localfilename " << localfilename; cout << ", sdfsfilename " << sdfsfilename << ", remoteLocalfilename " << remoteLocalfilename << endl; if (msgSplit.size() == 6){ @@ -44,10 +43,15 @@ void *runTcpSender(void *tcpSocket) cout << "[CHUNK] sending : " << sdfsfilename << " from " << msgSplit[3] << " to " << msgSplit[5] << endl; tcp->sendLines(nodeIP, TCPPORT, localfilename, sdfsfilename, msgSplit[4], start, end); //exec, file, start, end } - else tcp->sendFile(nodeIP, TCPPORT, localfilename, sdfsfilename, remoteLocalfilename, overwrite); + else tcp->putFile(nodeIP, TCPPORT, localfilename, sdfsfilename, remoteLocalfilename); } tcp->pendSendMessages.pop(); } + while (!tcp->mergeMessages.empty()) { + vector<string> msgSplit = splitString(tcp->mergeMessages.front(), "::"); + tcp->putDirectory(msgSplit[0], msgSplit[1]); + tcp->mergeMessages.pop(); + } } pthread_exit(NULL); } diff --git a/src/Utils.cpp b/src/Utils.cpp index 2c06f2c..099b7b7 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -54,7 +54,7 @@ void handlePipe(int file) { size_t bufSize = 1024; cout << "[PIPE] sleeping for data. " << endl; sleep(5); - FILE *stream = fdopen(file, "r"); FILE *tmp; + FILE *stream = fdopen(file, "rb"); FILE *tmp; char str[bufSize]; const char * delim = ","; int lines = 0; diff --git a/src/main.cpp b/src/main.cpp index b11ba4a..87b41b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -104,7 +104,7 @@ int main(int argc, char *argv[]) string line; ifstream myfile(localfilename.c_str()); while (getline(myfile, line)) ++number_of_lines; - Messages outMsg(DNS, node->nodeInformation.ip + "::" + to_string(node->hashRingPosition) + "::" + sdfsfilename + "::" + localfilename + "::" + to_string(size) + "::" + to_string(number_of_lines) + "::" + "::" + "1"); + Messages outMsg(DNS, node->nodeInformation.ip + "::" + to_string(node->hashRingPosition) + "::" + sdfsfilename + "::" + localfilename + "::" + to_string(size) + "::" + to_string(number_of_lines) + "::"); cout << "[PUT] Got localfilename: " << localfilename << " with sdfsfilename: " << sdfsfilename << endl; node->tcpServent->sendMessage(node->leaderIP, TCPPORT, outMsg.toString()); } else { -- GitLab