From fab8aa947b2e99ad189896ff7e0ef8a6eeae37ed Mon Sep 17 00:00:00 2001 From: Unbewohnte Date: Sun, 14 Nov 2021 19:32:00 +0300 Subject: [PATCH] [protocol] added ALREADYHAVE packet; [node] fixed logical path error when receiving a single file; ALREADY HAVE FILE FEATURE --- src/node/node.go | 135 +++++++++++++++++++++++++++------------- src/protocol/headers.go | 7 +++ 2 files changed, 98 insertions(+), 44 deletions(-) diff --git a/src/node/node.go b/src/node/node.go index 8336991..e1ace94 100644 --- a/src/node/node.go +++ b/src/node/node.go @@ -35,12 +35,13 @@ type netInfoInfo struct { // Sending-side node information type sending struct { - ServingPath string // path to the thing that will be sent - IsDirectory bool // is ServingPath a directory - Recursive bool // recursively send directory - CanSendBytes bool // is the other node ready to receive another piece - FilesToSend []*fsys.File - CurrentFileIndex uint64 // an id of a file that is currently being transported + ServingPath string // path to the thing that will be sent + IsDirectory bool // is ServingPath a directory + Recursive bool // recursively send directory + CanSendBytes bool // is the other node ready to receive another piece + FilesToSend []*fsys.File + // CurrentFileIndex uint64 // an index of a file with a specific ID that is currently being transported + CurrentFileID uint64 // an id of a file that is currently being transported } // Receiving-side node information @@ -313,7 +314,7 @@ func (node *Node) Start() { node.transferInfo.Sending.FilesToSend = append(node.transferInfo.Sending.FilesToSend, file) // set current file id to the first file - node.transferInfo.Sending.CurrentFileIndex = 0 + node.transferInfo.Sending.CurrentFileID = 0 filePacket, err := protocol.CreateFilePacket(file) if err != nil { @@ -342,7 +343,7 @@ func (node *Node) Start() { node.transferInfo.Sending.FilesToSend = append(node.transferInfo.Sending.FilesToSend, fileToSend) // set current file index to the first and only file - node.transferInfo.Sending.CurrentFileIndex = 0 + node.transferInfo.Sending.CurrentFileID = 0 filePacket, err := protocol.CreateFilePacket(node.transferInfo.Sending.FilesToSend[0]) if err != nil { @@ -373,6 +374,23 @@ func (node *Node) Start() { case protocol.HeaderDisconnecting: node.state.Stopped = true fmt.Printf("%s disconnected\n", node.netInfo.Conn.RemoteAddr()) + + case protocol.HeaderAlreadyHave: + // the other node already has a file with such ID. + // do not send it + + fileIDReader := bytes.NewReader(incomingPacket.Body) + var fileID uint64 + binary.Read(fileIDReader, binary.BigEndian, &fileID) + + for index, fileToSend := range node.transferInfo.Sending.FilesToSend { + if fileToSend.ID == fileID { + node.transferInfo.Sending.FilesToSend = append(node.transferInfo.Sending.FilesToSend[:index], node.transferInfo.Sending.FilesToSend[index+1:]...) + + node.transferInfo.Sending.CurrentFileID++ + } + } + } // Transfer section @@ -386,6 +404,8 @@ func (node *Node) Start() { fmt.Printf("Transfer ended successfully\n") node.state.Stopped = true + + continue } // if allowed to transfer and the other node is ready to receive packets - send one piece @@ -394,7 +414,15 @@ func (node *Node) Start() { // sending a piece of a single file - currentFileIndex := node.transferInfo.Sending.CurrentFileIndex + // determine an index of a file with current ID + var currentFileIndex uint64 = 0 + for index, fileToSend := range node.transferInfo.Sending.FilesToSend { + if fileToSend.ID == node.transferInfo.Sending.CurrentFileID { + currentFileIndex = uint64(index) + break + } + } + err = protocol.SendPiece(node.transferInfo.Sending.FilesToSend[currentFileIndex], node.netInfo.Conn, node.netInfo.EncryptionKey) switch err { case protocol.ErrorSentAll: @@ -419,21 +447,10 @@ func (node *Node) Start() { protocol.SendPacket(node.netInfo.Conn, endFilePacket) - // start sending the next file - if uint64(len(node.transferInfo.Sending.FilesToSend)) == (node.transferInfo.Sending.CurrentFileIndex + 1) { - // all files has been sent - node.state.Stopped = true - - // send DONE packet - donePacket := protocol.Packet{ - Header: protocol.HeaderDone, - } - - protocol.SendPacket(node.netInfo.Conn, donePacket) + node.transferInfo.Sending.FilesToSend = append(node.transferInfo.Sending.FilesToSend[:currentFileIndex], node.transferInfo.Sending.FilesToSend[currentFileIndex+1:]...) - } else { - node.transferInfo.Sending.CurrentFileIndex++ - } + // start sending the next file + node.transferInfo.Sending.CurrentFileID++ case nil: node.transferInfo.Sending.CanSendBytes = false @@ -441,8 +458,7 @@ func (node *Node) Start() { default: node.state.Stopped = true - CurrentFileIndex := node.transferInfo.Sending.CurrentFileIndex - fmt.Printf("An error occured while sending a piece of \"%s\": %s\n", node.transferInfo.Sending.FilesToSend[CurrentFileIndex].Name, err) + fmt.Printf("An error occured while sending a piece of \"%s\": %s\n", node.transferInfo.Sending.FilesToSend[currentFileIndex].Name, err) panic(err) } @@ -585,7 +601,12 @@ func (node *Node) Start() { panic(err) } - file.Path = filepath.Join(node.transferInfo.Receiving.DownloadsPath, file.RelativeParentPath) + if file.RelativeParentPath == "" { + // does not have a parent dir + file.Path = filepath.Join(node.transferInfo.Receiving.DownloadsPath, file.Name) + } else { + file.Path = filepath.Join(node.transferInfo.Receiving.DownloadsPath, file.RelativeParentPath) + } // create all underlying directories right ahead err = os.MkdirAll(filepath.Dir(file.Path), os.ModePerm) @@ -593,17 +614,54 @@ func (node *Node) Start() { panic(err) } - // check if the file already exists; if yes - remove it and replace with a new one + // check if the file already exists _, err = os.Stat(file.Path) if err == nil { // exists - // remove it - os.Remove(file.Path) - } + // check if it is the exact file + existingFileHandler, err := os.Open(file.Path) + if err != nil { + os.Remove(file.Path) + } - node.mutex.Lock() - node.transferInfo.Receiving.AcceptedFiles = append(node.transferInfo.Receiving.AcceptedFiles, file) - node.mutex.Unlock() + existingFileChecksum, _ := checksum.GetPartialCheckSum(existingFileHandler) + + if existingFileChecksum == file.Checksum { + // it`s the exact same file. No need to receive it again + // notify the other node + + fmt.Printf("Already have \"%s\". Skipping...\n\n", file.Name) + + alreadyHavePacketBodyBuffer := new(bytes.Buffer) + binary.Write(alreadyHavePacketBodyBuffer, binary.BigEndian, file.ID) + + alreadyHavePacket := protocol.Packet{ + Header: protocol.HeaderAlreadyHave, + Body: alreadyHavePacketBodyBuffer.Bytes(), + } + + encryptedBody, err := encryption.Encrypt(node.netInfo.EncryptionKey, alreadyHavePacket.Body) + if err != nil { + panic(err) + } + + alreadyHavePacket.Body = encryptedBody + + protocol.SendPacket(node.netInfo.Conn, alreadyHavePacket) + + } else { + // not the same file. Remove it and await new bytes + os.Remove(file.Path) + } + + existingFileHandler.Close() + } else { + // does not exist + + node.mutex.Lock() + node.transferInfo.Receiving.AcceptedFiles = append(node.transferInfo.Receiving.AcceptedFiles, file) + node.mutex.Unlock() + } case protocol.HeaderFileBytes: // check if this file has been accepted to receive @@ -632,17 +690,6 @@ func (node *Node) Start() { wrote, err := acceptedFile.Handler.WriteAt(fileBytes, int64(acceptedFile.SentBytes)) if err != nil { panic(err) - // // fmt.Printf("[Debug] %+v\n", acceptedFile) - - // // this file won`t be completed, so it`ll be ignored - - // // remove this file from the pool - // node.transferInfo.Receiving.AcceptedFiles = append(node.transferInfo.Receiving.AcceptedFiles[:index], node.transferInfo.Receiving.AcceptedFiles[index+1:]...) - - // fmt.Printf("[ERROR] an error occured when receiving a file \"%s\": %s. This file will not be completed", acceptedFile.Name, err) - - // // remove it from the filesystem - // os.Remove(acceptedFile.Path) } acceptedFile.SentBytes += uint64(wrote) diff --git a/src/protocol/headers.go b/src/protocol/headers.go index 82b34ff..f345c0c 100644 --- a/src/protocol/headers.go +++ b/src/protocol/headers.go @@ -87,3 +87,10 @@ const HeaderEndfile Header = "ENDFILE" // between a file and a directory. // ie: DIRECTORY~(dirname size in binary)(dirname)(dirsize) const HeaderDirectory Header = "DIRECTORY" + +// ALREADYHAVE +// Sent by receiver in case there is the same file that already exists. +// Sender upon receiving such packet with specified file ID must not send it. +// Body must contain a file ID. +// ie: ALREADYHAVE~(file ID in binary) +const HeaderAlreadyHave Header = "ALREADYHAVE"