Browse Source

[node] documentation

main
Unbewohnte 3 years ago
parent
commit
5b1d964cad
  1. 8
      src/fsys/dir.go
  2. 8
      src/node/errors.go
  3. 87
      src/node/node.go
  4. 3
      src/node/packets.go
  5. 28
      src/node/transfer.go

8
src/fsys/dir.go

@ -14,15 +14,11 @@ type Directory struct {
Size uint64 Size uint64
Files []*File Files []*File
Directories []*Directory Directories []*Directory
Checksum string // Set manually
} }
var ErrorNotDirectory error = fmt.Errorf("not a directory") var ErrorNotDirectory error = fmt.Errorf("not a directory")
// // gets a child directory
// func getDirChild(path string, parentDir *Directory, recursive bool) (*Directory, error) {
// }
func GetDir(path string, recursive bool) (*Directory, error) { func GetDir(path string, recursive bool) (*Directory, error) {
absPath, err := filepath.Abs(path) absPath, err := filepath.Abs(path)
if err != nil { if err != nil {
@ -53,7 +49,6 @@ func GetDir(path string, recursive bool) (*Directory, error) {
return nil, err return nil, err
} }
// var totalSize uint64 = 0
var innerDirs []*Directory var innerDirs []*Directory
var innerFiles []*File var innerFiles []*File
for _, entry := range entries { for _, entry := range entries {
@ -94,7 +89,6 @@ func GetDir(path string, recursive bool) (*Directory, error) {
directory.Directories = innerDirs directory.Directories = innerDirs
directory.Files = innerFiles directory.Files = innerFiles
// directory.Size
return &directory, nil return &directory, nil
} }

8
src/node/errors.go

@ -1,8 +0,0 @@
package node
import "fmt"
var (
ErrorNotConnected error = fmt.Errorf("not connected")
ErrorSentAll error = fmt.Errorf("sent the whole file")
)

87
src/node/node.go

@ -7,7 +7,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
"time" "time"
"fmt" "fmt"
@ -19,46 +18,42 @@ import (
"github.com/Unbewohnte/ftu/protocol" "github.com/Unbewohnte/ftu/protocol"
) )
// node-controlling states
type NodeInnerStates struct { type NodeInnerStates struct {
Stopped bool Stopped bool // the way to exit the mainloop in case of an external error or a successful end of a transfer
Connected bool AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files
AllowedToTransfer bool
} }
// Network specific settings
type Net struct { type Net struct {
ConnAddr string ConnAddr string // address to connect to. Does not include port
Conn net.Conn Conn net.Conn // the core TCP connection of the node. Self-explanatory
Port uint Port uint // a port to connect to/listen on
EncryptionKey []byte EncryptionKey []byte // if != nil - incoming packets will be decrypted with it and outcoming packets will be encrypted
} }
// Both sending-side and receiving-side information
type TransferInfo struct { type TransferInfo struct {
Ready bool // is the other node ready to receive another piece Ready bool // is the other node ready to receive another piece
ServingPath string // path to the thing that will be sent ServingPath string // path to the thing that will be sent
Recursive bool Recursive bool // recursively send directory
AcceptedFiles []*fsys.File AcceptedFiles []*fsys.File // files that`ve been accepted to be received
DownloadsPath string DownloadsPath string // where to download
} }
// Sender and receiver in one type ! // Sender and receiver in one type !
type Node struct { type Node struct {
PacketPipe chan *protocol.Packet PacketPipe chan *protocol.Packet // a way to receive incoming packets from another goroutine
ErrorPipe chan error IsSending bool // sending or a receiving node
Mutex *sync.Mutex
IsSending bool
Net *Net Net *Net
State *NodeInnerStates State *NodeInnerStates
TransferInfo *TransferInfo TransferInfo *TransferInfo
} }
// Creates a new node // Creates a new either a sending or receiving node with specified options
func NewNode(options *NodeOptions) (*Node, error) { func NewNode(options *NodeOptions) (*Node, error) {
mutex := new(sync.Mutex)
node := Node{ node := Node{
PacketPipe: make(chan *protocol.Packet, 100), PacketPipe: make(chan *protocol.Packet, 100),
ErrorPipe: make(chan error, 100),
Mutex: mutex,
IsSending: options.IsSending, IsSending: options.IsSending,
Net: &Net{ Net: &Net{
Port: options.WorkingPort, Port: options.WorkingPort,
@ -69,7 +64,6 @@ func NewNode(options *NodeOptions) (*Node, error) {
State: &NodeInnerStates{ State: &NodeInnerStates{
AllowedToTransfer: false, AllowedToTransfer: false,
Stopped: false, Stopped: false,
Connected: false,
}, },
TransferInfo: &TransferInfo{ TransferInfo: &TransferInfo{
ServingPath: options.ServerSide.ServingPath, ServingPath: options.ServerSide.ServingPath,
@ -81,14 +75,15 @@ func NewNode(options *NodeOptions) (*Node, error) {
return &node, nil return &node, nil
} }
func (node *Node) connect(addr string, port uint) error { // Connect node to another listening one with a pre-defined address&&port
if port == 0 { func (node *Node) connect() error {
port = node.Net.Port if node.Net.Port == 0 {
node.Net.Port = 7270
} }
fmt.Printf("Connecting to %s:%d...\n", addr, port) fmt.Printf("Connecting to %s:%d...\n", node.Net.ConnAddr, node.Net.Port)
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", addr, port), time.Second*5) conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", node.Net.ConnAddr, node.Net.Port), time.Second*5)
if err != nil { if err != nil {
return err return err
} }
@ -96,13 +91,13 @@ func (node *Node) connect(addr string, port uint) error {
fmt.Printf("Connected\n") fmt.Printf("Connected\n")
node.Net.Conn = conn node.Net.Conn = conn
node.State.Connected = true
return nil return nil
} }
// Notify the other node and close the connection
func (node *Node) disconnect() error { func (node *Node) disconnect() error {
if node.State.Connected && node.Net.Conn != nil { if node.Net.Conn != nil {
// notify the other node and close the connection // notify the other node and close the connection
err := protocol.SendPacket(node.Net.Conn, protocol.Packet{ err := protocol.SendPacket(node.Net.Conn, protocol.Packet{
Header: protocol.HeaderDisconnecting, Header: protocol.HeaderDisconnecting,
@ -117,13 +112,12 @@ func (node *Node) disconnect() error {
} }
node.State.Stopped = true node.State.Stopped = true
node.State.Connected = false
} }
return nil return nil
} }
// Waits for connection on a pre-defined port // Wait for a connection on a pre-defined port
func (node *Node) waitForConnection() error { func (node *Node) waitForConnection() error {
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", node.Net.Port)) listener, err := net.Listen("tcp", fmt.Sprintf(":%d", node.Net.Port))
if err != nil { if err != nil {
@ -139,7 +133,6 @@ func (node *Node) waitForConnection() error {
fmt.Printf("New connection from %s\n", connection.RemoteAddr().String()) fmt.Printf("New connection from %s\n", connection.RemoteAddr().String())
node.Net.Conn = connection node.Net.Conn = connection
node.State.Connected = true
return nil return nil
} }
@ -150,6 +143,7 @@ func (node *Node) Start() {
case true: case true:
// SENDER // SENDER
// retrieve necessary information, wait for connection
localIP, err := addr.GetLocal() localIP, err := addr.GetLocal()
if err != nil { if err != nil {
panic(err) panic(err)
@ -180,7 +174,7 @@ func (node *Node) Start() {
// listen for incoming packets // listen for incoming packets
go receivePackets(node.Net.Conn, node.PacketPipe) go receivePackets(node.Net.Conn, node.PacketPipe)
// send fileoffer // send info on file/directory
go sendFilePacket(node.Net.Conn, file, node.Net.EncryptionKey) go sendFilePacket(node.Net.Conn, file, node.Net.EncryptionKey)
// mainloop // mainloop
@ -190,6 +184,7 @@ func (node *Node) Start() {
break break
} }
// receive incoming packets and decrypt them if necessary
incomingPacket, ok := <-node.PacketPipe incomingPacket, ok := <-node.PacketPipe
if !ok { if !ok {
node.State.Stopped = true node.State.Stopped = true
@ -202,8 +197,10 @@ func (node *Node) Start() {
} }
} }
// react based on a header of a received packet
switch incomingPacket.Header { switch incomingPacket.Header {
case protocol.HeaderReady: case protocol.HeaderReady:
// the other node is ready to receive file data
node.TransferInfo.Ready = true node.TransferInfo.Ready = true
case protocol.HeaderAccept: case protocol.HeaderAccept:
@ -211,18 +208,20 @@ func (node *Node) Start() {
fmt.Printf("Transfer allowed. Sending...\n") fmt.Printf("Transfer allowed. Sending...\n")
case protocol.HeaderDisconnecting: case protocol.HeaderReject:
node.State.Stopped = true node.State.Stopped = true
fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr()) fmt.Printf("Transfer rejected. Disconnecting...")
case protocol.HeaderReject: case protocol.HeaderDisconnecting:
node.State.Stopped = true node.State.Stopped = true
fmt.Printf("Transfer rejected. Disconnecting...") fmt.Printf("%s disconnected\n", node.Net.Conn.RemoteAddr())
} }
if node.State.AllowedToTransfer || node.TransferInfo.Ready { // if allowed to transfer and the other node is ready to receive packets - send one piece
// and wait for it to be ready again
if node.State.AllowedToTransfer && node.TransferInfo.Ready {
err = sendPiece(file, node.Net.Conn, node.Net.EncryptionKey) err = sendPiece(file, node.Net.Conn, node.Net.EncryptionKey)
if err != nil { if err != nil {
if err == ErrorSentAll { if err == ErrorSentAll {
@ -264,7 +263,7 @@ func (node *Node) Start() {
// RECEIVER // RECEIVER
// connect to the sending node // connect to the sending node
err := node.connect(node.Net.ConnAddr, node.Net.Port) err := node.connect()
if err != nil { if err != nil {
fmt.Printf("Could not connect to %s:%d\n", node.Net.ConnAddr, node.Net.Port) fmt.Printf("Could not connect to %s:%d\n", node.Net.ConnAddr, node.Net.Port)
os.Exit(-1) os.Exit(-1)
@ -280,6 +279,7 @@ func (node *Node) Start() {
break break
} }
// receive incoming packets and decrypt them if necessary
incomingPacket, ok := <-node.PacketPipe incomingPacket, ok := <-node.PacketPipe
if !ok { if !ok {
break break
@ -291,9 +291,11 @@ func (node *Node) Start() {
} }
} }
// react based on a header of a received packet
switch incomingPacket.Header { switch incomingPacket.Header {
case protocol.HeaderFile: case protocol.HeaderFile:
// process an information about a singe file. Accept or reject the transfer
go func() { go func() {
file, err := decodeFilePacket(incomingPacket) file, err := decodeFilePacket(incomingPacket)
if err != nil { if err != nil {
@ -404,6 +406,7 @@ func (node *Node) Start() {
} }
} }
// notify the other one that this node is ready
err = protocol.SendPacket(node.Net.Conn, protocol.Packet{ err = protocol.SendPacket(node.Net.Conn, protocol.Packet{
Header: protocol.HeaderReady, Header: protocol.HeaderReady,
}) })
@ -412,6 +415,10 @@ func (node *Node) Start() {
} }
case protocol.HeaderEndfile: case protocol.HeaderEndfile:
// one of the files has been received completely,
// compare checksums and check if it is the last
// file in the transfer
// (TODO)
fileIDReader := bytes.NewReader(incomingPacket.Body) fileIDReader := bytes.NewReader(incomingPacket.Body)
var fileID uint64 var fileID uint64
err := binary.Read(fileIDReader, binary.BigEndian, &fileID) err := binary.Read(fileIDReader, binary.BigEndian, &fileID)

3
src/node/packets.go

@ -4,6 +4,7 @@ package node
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt"
"net" "net"
"github.com/Unbewohnte/ftu/fsys" "github.com/Unbewohnte/ftu/fsys"
@ -34,6 +35,8 @@ func sendEncryptionKey(connection net.Conn, encrKey []byte) error {
return nil return nil
} }
var ErrorNotConnected error = fmt.Errorf("not connected")
// Reads packets from connection in an endless loop, sends them to the channel, if encrKey is not nil - // Reads packets from connection in an endless loop, sends them to the channel, if encrKey is not nil -
// decrypts packet`s body beforehand // decrypts packet`s body beforehand
func receivePackets(connection net.Conn, packetPipe chan *protocol.Packet) error { func receivePackets(connection net.Conn, packetPipe chan *protocol.Packet) error {

28
src/node/transfer.go

@ -3,6 +3,7 @@ package node
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt"
"io" "io"
"net" "net"
"os" "os"
@ -76,10 +77,37 @@ func sendDirectoryPacket(connection net.Conn, dir *fsys.Directory) error {
if connection == nil { if connection == nil {
return ErrorNotConnected return ErrorNotConnected
} }
// DIRECTORY~(dirname size in binary)(dirname)(dirsize)(checksumLengthInBinary)(checksum)
dirPacketBuffer := new(bytes.Buffer)
// dirname
dirnameLength := uint64(len(dir.Name))
err := binary.Write(dirPacketBuffer, binary.BigEndian, &dirnameLength)
if err != nil {
return err
}
dirPacketBuffer.Write([]byte(dir.Name))
// dirsize
err = binary.Write(dirPacketBuffer, binary.BigEndian, dir.Size)
if err != nil {
return err
}
// checksum
checksumLength := uint64(len(dir.Checksum))
err = binary.Write(dirPacketBuffer, binary.BigEndian, &checksumLength)
if err != nil {
return err
}
dirPacketBuffer.Write([]byte(dir.Checksum))
return nil return nil
} }
var ErrorSentAll error = fmt.Errorf("sent the whole file")
// sends a piece of file to the connection; The next calls will send // sends a piece of file to the connection; The next calls will send
// another piece util the file has been fully sent. If encrKey is not nil - encrypts each packet with // another piece util the file has been fully sent. If encrKey is not nil - encrypts each packet with
// this key // this key

Loading…
Cancel
Save