Browse Source

Added checksum check

main 1.0.2
Unbewohnte 4 years ago
parent
commit
65ca89a407
  1. 1
      .gitignore
  2. 20
      README.md
  3. 80
      checksum/checksum.go
  4. 198
      client/client.go
  5. 48
      main.go
  6. 15
      protocol/protocol.go
  7. 10
      receiver/file.go
  8. 233
      receiver/receiver.go
  9. 17
      sender/file.go
  10. 57
      sender/sender.go

1
.gitignore vendored

@ -1,3 +1,4 @@
FTU
FTU.exe
release/
downloads/

20
README.md

@ -17,7 +17,7 @@ Learning
## How does this work ?
In order to transfer one file on one computer to another - they need to establish a connection.
In order to establish a connection - there needs to be a 1) server (the owner of the file), waiting for connections, and a 2) client, who will try to connect to a server. If the requirements are met - a client will connect to a server and the packet exchange will begin.
In order to establish a connection - there needs to be a 1) sender (server) (the owner of the file), waiting for connections, and a 2) receiver (client), who will try to connect to a sender (server). If the requirements are met - a client will connect to a server and the packet exchange will begin.
The server and the client needs to communicate with packets according to certain rules, given by a [protocol](https://github.com/Unbewohnte/FTU/tree/main/protocol).
@ -32,18 +32,18 @@ Thus, with a connection and a way of communication, the server will send a filei
### Flags
- `-server` (bool) - if true - creates a server (also need to provide a `-sharefile` flag in that case), if false - creates a client
- `-port` (int) - specifies a port; if `-server` == true - listens on that port, else - connects to given port
- `addr` (string) - specifies an address to connect to (used when `-server=false`)
- `-sharefile` (string) - specifies path to a file you want to share (used in pair with `-server=true`), if given a valid path - a server will offer to share this file to a client
- `-sending` (bool) - if true - creates a server (sender) (also need to provide a `-sharefile` flag in that case), if false - creates a client (receiver)
- `-port` (int) - specifies a port; if `-sending` == true - listens on that port, else - connects to given port
- `addr` (string) - specifies an address to connect to (used when `-sending=false`)
- `-sharefile` (string) - specifies path to a file you want to share (used in pair with `-sending=true`), if given a valid path - a server will offer to share this file to a client
- `-downloadto` (string) - specifies path to a folder where the client wants to store downloaded file
### Examples
- `./FTU -server=true -sharefile="/home/some_path_here/FILETOSHARE.zip"` - creates a server that will share `FILETOSHARE.zip` on port `8080`
- `./FTU -server=true -sharefile="/home/some_path_here/FILETOSHARE.zip" - port=727` - same as before, but on port `727`
- `./FTU -server=false -downloadto="/home/some_path_here/Downloads/" -addr="localhost"` - creates a client that will try to connect to `localhost` on port `8080` and if successful - downloads a file to given path
- `./FTU -server=false -downloadto="/home/some_path_here/Downloads/" -addr=145.125.53.212 -port=8888` - same as before, but will try to connect to `145.125.53.212` on port `8888`
- `./FTU -sending=true -sharefile="/home/some_path_here/FILETOSHARE.zip"` - creates a server that will share `FILETOSHARE.zip` on port `8080`
- `./FTU -sending=true -sharefile="/home/some_path_here/FILETOSHARE.zip" - port=727` - same as before, but on port `727`
- `./FTU -sending=false -downloadto="/home/some_path_here/Downloads/" -addr="localhost"` - creates a client that will try to connect to `localhost` on port `8080` and if successful - downloads a file to given path
- `./FTU -sending=false -downloadto="/home/some_path_here/Downloads/" -addr=145.125.53.212 -port=8888` - same as before, but will try to connect to `145.125.53.212` on port `8888`
---
@ -54,7 +54,7 @@ Thus, with a connection and a way of communication, the server will send a filei
- If `MAXFILEDATASIZE` is bigger than appr. 1024 - the packets on the other end will not be unmarshalled due to error ??; FIXED - [ ]
- Lack of proper error-handling; somewhat FIXED - [x]
- Lack of information about the process of transferring (ETA, lost packets, etc.); FIXED - [ ]
- No way to verify if the transferred file is not corrupted; FIXED - [ ]
- No way to verify if the transferred file is not corrupted; FIXED via checksum- [x]
- No encryption; FIXED - [ ]
## Good points

80
checksum/checksum.go

@ -0,0 +1,80 @@
package checksum
import (
"crypto/sha256"
"fmt"
"io"
"os"
)
type CheckSum [32]byte
// returns a checksum of given file. NOTE, that it creates checksum
// not of a full file (from all file bytes), but from separate byte blocks.
// This is done as an optimisation because the file can be very large in size.
// The general idea:
// BOF... CHUNK -> STEP -> CHUNK... EOF
// checksum := sha256.Sum256(ALLCHUNKS)
// GetPartialCheckSum is default method used to get a file checksum by sender and receiver
func GetPartialCheckSum(file *os.File) (CheckSum, error) {
// "capturing" CHUNKSIZE bytes and then skipping STEP bytes before the next chunk until the last one
const CHUNKS uint = 100
const CHUNKSIZE uint = 100
const STEP uint = 250
fileStats, err := file.Stat()
if err != nil {
return [32]byte{}, fmt.Errorf("could not get the stats: %s", err)
}
fileSize := fileStats.Size()
if fileSize < int64(CHUNKS*CHUNKSIZE+STEP*(CHUNKS-1)) {
// file is too small, doing full checksum
checksum, err := getFullCheckSum(file)
if err != nil {
return [32]byte{}, err
}
return checksum, nil
}
var capturedChunks string
var read uint64 = 0
for i := 0; uint(i) < CHUNKS; i++ {
buffer := make([]byte, CHUNKSIZE)
r, _ := file.ReadAt(buffer, int64(read))
capturedChunks += string(buffer)
read += uint64(r)
read += uint64(STEP)
}
checksum := sha256.Sum256([]byte(capturedChunks))
return checksum, nil
}
// returns a sha256 checksum of given file
func getFullCheckSum(file *os.File) (CheckSum, error) {
filebytes, err := io.ReadAll(file)
if err != nil {
return [32]byte{}, fmt.Errorf("could not read the file: %s", err)
}
checksum := sha256.Sum256(filebytes)
return checksum, nil
}
// simply compares 2 given checksums. If they are equal - returns true
func AreEqual(checksum1, checksum2 CheckSum) bool {
var i int = 0
for _, checksum1Byte := range checksum1 {
checksum2Byte := checksum2[i]
if checksum1Byte != checksum2Byte {
return false
}
i++
}
return true
}

198
client/client.go

@ -1,198 +0,0 @@
package client
import (
"fmt"
"net"
"os"
"path/filepath"
"strings"
"github.com/Unbewohnte/FTU/protocol"
)
// Representation of a tcp client
type Client struct {
DownloadsFolder string
Connection net.Conn
IncomingPackets chan protocol.Packet
Stopped bool
ReadyToReceive bool
PacketCounter uint64
}
// Creates a new client with default fields
func NewClient(downloadsFolder string) *Client {
os.MkdirAll(downloadsFolder, os.ModePerm)
info, err := os.Stat(downloadsFolder)
if err != nil {
panic(err)
}
if !info.IsDir() {
panic("Downloads folder is not a directory")
}
incomingPacketsChan := make(chan protocol.Packet, 5)
var PacketCounter uint64 = 0
fmt.Println("Created a new client")
return &Client{
DownloadsFolder: downloadsFolder,
Connection: nil,
IncomingPackets: incomingPacketsChan,
Stopped: false,
ReadyToReceive: false,
PacketCounter: PacketCounter,
}
}
// Closes the connection
func (c *Client) Disconnect() {
c.Connection.Close()
}
// Closes the connection, warns the server and exits the mainloop
func (c *Client) Stop() {
disconnectionPacket := protocol.Packet{
Header: protocol.HeaderDisconnecting,
}
protocol.SendPacket(c.Connection, disconnectionPacket)
c.Stopped = true
c.Disconnect()
}
// Connects to a given address over tcp. Sets a connection to a client
func (c *Client) Connect(addr string) error {
fmt.Printf("Trying to connect to %s...\n", addr)
connection, err := net.Dial("tcp", addr)
if err != nil {
return fmt.Errorf("could not connect to %s: %s", addr, err)
}
c.Connection = connection
fmt.Println("Connected to ", c.Connection.RemoteAddr())
return nil
}
// Handles the fileinfo packet. The choice of acceptance is given to the user
func (c *Client) HandleFileOffer(fileinfoPacket protocol.Packet) error {
// inform the user about the file
fmt.Printf("Incoming fileinfo packet:\nFilename: %s\nFilesize: %.3fMB\nAccept ? [Y/N]: ",
fileinfoPacket.Filename, float32(fileinfoPacket.Filesize)/1024/1024)
// get and process the input
var input string
fmt.Scanln(&input)
input = strings.TrimSpace(input)
input = strings.ToLower(input)
// reject the file
if input != "y" {
rejectionPacket := protocol.Packet{
Header: protocol.HeaderReject,
}
err := protocol.SendPacket(c.Connection, rejectionPacket)
if err != nil {
return fmt.Errorf("could not send a rejection packet: %s", err)
}
c.ReadyToReceive = false
return nil
}
// accept the file
acceptancePacket := protocol.Packet{
Header: protocol.HeaderAccept,
}
err := protocol.SendPacket(c.Connection, acceptancePacket)
if err != nil {
return fmt.Errorf("could not send an acceptance packet: %s", err)
}
// can and ready to receive file packets
c.ReadyToReceive = true
return nil
}
// Handles the download by writing incoming bytes into the file
func (c *Client) WritePieceOfFile(filePacket protocol.Packet) error {
c.ReadyToReceive = false
// open|create a file with the same name as the filepacket`s file name
file, err := os.OpenFile(filepath.Join(c.DownloadsFolder, filePacket.Filename), os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
if err != nil {
return err
}
// just write the filedata
file.Write(filePacket.FileData)
file.Close()
c.PacketCounter++
c.ReadyToReceive = true
return nil
}
// Listens in an endless loop; reads incoming packages and puts them into channel
func (c *Client) ReceivePackets() {
for {
incomingPacket, err := protocol.ReadFromConn(c.Connection)
if err != nil {
// in current implementation there is no way to receive a working file even if only one packet is missing
fmt.Printf("Error reading a packet: %s\nExiting...", err)
os.Exit(-1)
}
c.IncomingPackets <- incomingPacket
}
}
// The "head" of the client. Similarly as in server`s logic "glues" everything together.
// Current structure allows the client to receive any type of packet
// in any order and react correspondingly
func (c *Client) MainLoop() {
go c.ReceivePackets()
for {
if c.Stopped {
// exit the mainloop
break
}
// 1) send -> 2) handle received if necessary
// send a packet telling server to send another piece of file
if c.ReadyToReceive {
readyPacket := protocol.Packet{
Header: protocol.HeaderReady,
}
protocol.SendPacket(c.Connection, readyPacket)
c.ReadyToReceive = false
}
// no incoming packets ? Skipping the packet handling part
if len(c.IncomingPackets) == 0 {
continue
}
// take the packet and handle depending on the header
incomingPacket := <-c.IncomingPackets
// handling each packet header differently
switch incomingPacket.Header {
case protocol.HeaderFileInfo:
go c.HandleFileOffer(incomingPacket)
case protocol.HeaderFileData:
go c.WritePieceOfFile(incomingPacket)
case protocol.HeaderDisconnecting:
// the server is ded, no need to stay alive as well
fmt.Println("Done. Got ", c.PacketCounter, " packets in total")
c.Stopped = true
c.Stop()
}
}
}

48
main.go

@ -6,28 +6,28 @@ import (
"os"
"strings"
"github.com/Unbewohnte/FTU/client"
"github.com/Unbewohnte/FTU/server"
"github.com/Unbewohnte/FTU/receiver"
"github.com/Unbewohnte/FTU/sender"
)
// flags
var PORT *int = flag.Int("port", 8080, "Specifies a port for a server")
var SERVERADDR *string = flag.String("addr", "", "Specifies an IP for connection")
var ISSERVER *bool = flag.Bool("server", false, "Server or a client")
var DOWNLOADSFOLDER *string = flag.String("downloadto", "", "Specifies where the client will store downloaded file")
var SHAREDFILE *string = flag.String("sharefile", "", "Specifies what file server will serve")
var PORT *int = flag.Int("port", 8080, "Specifies a port for a sender")
var SENDERADDR *string = flag.String("addr", "", "Specifies an IP for connection")
var SENDING *bool = flag.Bool("sending", false, "Send or receive")
var DOWNLOADSFOLDER *string = flag.String("downloadto", "", "Specifies where the receiver will store downloaded file")
var SHAREDFILE *string = flag.String("sharefile", "", "Specifies what file sender will send")
// helpMessage
var HELPMSG string = `
"-port", default: 8080, Specifies a port for a server
"-port", default: 8080, Specifies a port for a sender
"-addr", default: "", Specifies an IP for connection
"-server", default: false, Share file or connect and receive one ?
"-downloadto", default: "", Specifies where the client will store downloaded file
"-sharefile", default: "", Specifies what file server will share`
"-sending", default: false, Send or receive
"-downloadto", default: "", Specifies where the receiver will store downloaded file
"-sharefile", default: "", Specifies what file sender will send`
// Input-validation
func checkFlags() {
if *ISSERVER {
if *SENDING {
if strings.TrimSpace(*SHAREDFILE) == "" {
fmt.Println("No file specified !\n", HELPMSG)
os.Exit(1)
@ -36,8 +36,8 @@ func checkFlags() {
fmt.Println("Invalid port !\n", HELPMSG)
os.Exit(1)
}
} else if !*ISSERVER {
if strings.TrimSpace(*SERVERADDR) == "" {
} else if !*SENDING {
if strings.TrimSpace(*SENDERADDR) == "" {
fmt.Println("Invalid IP address !\n", HELPMSG)
os.Exit(1)
}
@ -55,18 +55,18 @@ func init() {
}
func main() {
if *ISSERVER {
// 1) create server -> 2) wait for a client ->|
// 3) send handshake packet -> 4) if accepted - upload file
server := server.NewServer(*PORT, *SHAREDFILE)
server.WaitForConnection()
server.MainLoop()
if *SENDING {
// 1) create sender -> 2) wait for a connection ->|
// 3) send fileinfo packet -> 4) if accepted - upload file
sender := sender.NewSender(*PORT, *SHAREDFILE)
sender.WaitForConnection()
sender.MainLoop()
} else {
// 1) create client -> 2) try to connect to a server -> 3) wait for a handshake ->|
// 1) create receiver -> 2) try to connect to a sender -> 3) wait for a fileinfo packet ->|
// 4) accept or refuse -> 5) download|don`t_download file
client := client.NewClient(*DOWNLOADSFOLDER)
client.Connect(fmt.Sprintf("%s:%d", *SERVERADDR, *PORT))
client.MainLoop()
receiver := receiver.NewReceiver(*DOWNLOADSFOLDER)
receiver.Connect(fmt.Sprintf("%s:%d", *SENDERADDR, *PORT))
receiver.MainLoop()
}
}

15
protocol/protocol.go

@ -6,6 +6,8 @@ import (
"net"
"strconv"
"strings"
"github.com/Unbewohnte/FTU/checksum"
)
// a package that describes how server and client should communicate
@ -19,18 +21,19 @@ type Header string
const HeaderFileData Header = "FILEDATA"
const HeaderFileInfo Header = "FILEINFO"
const HeaderReject Header = "FILE_REJECT"
const HeaderAccept Header = "FILE_ACCEPT"
const HeaderReject Header = "FILEREJECT"
const HeaderAccept Header = "FILEACCEPT"
const HeaderReady Header = "READY"
const HeaderDisconnecting Header = "BYE!"
// Packet structure.
// A Packet without a header is an invalid packet
type Packet struct {
Header Header `json:"Header"`
Filename string `json:"Filename"`
Filesize uint64 `json:"Filesize"`
FileData []byte `json:"Filedata"`
Header Header `json:"Header"`
Filename string `json:"Filename"`
Filesize uint64 `json:"Filesize"`
FileCheckSum checksum.CheckSum `json:"CheckSum"`
FileData []byte `json:"Filedata"`
}
// converts valid packet bytes into `Packet` struct

10
receiver/file.go

@ -0,0 +1,10 @@
package receiver
import "github.com/Unbewohnte/FTU/checksum"
// Receiver`s file struct. Used internally by receiver
type File struct {
Filename string
Filesize uint64
CheckSum checksum.CheckSum
}

233
receiver/receiver.go

@ -0,0 +1,233 @@
package receiver
import (
"fmt"
"net"
"os"
"path/filepath"
"strings"
"github.com/Unbewohnte/FTU/checksum"
"github.com/Unbewohnte/FTU/protocol"
)
// Representation of a receiver
type Receiver struct {
DownloadsFolder string
Connection net.Conn
IncomingPackets chan protocol.Packet
FileToDownload *File
Stopped bool
ReadyToReceive bool
PacketCounter uint64
}
// Creates a new client with default fields
func NewReceiver(downloadsFolder string) *Receiver {
os.MkdirAll(downloadsFolder, os.ModePerm)
downloadsFolderInfo, err := os.Stat(downloadsFolder)
if err != nil {
panic(err)
}
if !downloadsFolderInfo.IsDir() {
panic("Downloads folder is not a directory")
}
incomingPacketsChan := make(chan protocol.Packet, 5)
var PacketCounter uint64 = 0
fmt.Println("Created a new client")
return &Receiver{
DownloadsFolder: downloadsFolder,
Connection: nil,
IncomingPackets: incomingPacketsChan,
Stopped: false,
ReadyToReceive: false,
PacketCounter: PacketCounter,
}
}
// Closes the connection
func (r *Receiver) Disconnect() {
r.Connection.Close()
}
// Closes the connection, warns the sender and exits the mainloop
func (r *Receiver) Stop() {
disconnectionPacket := protocol.Packet{
Header: protocol.HeaderDisconnecting,
}
protocol.SendPacket(r.Connection, disconnectionPacket)
r.Stopped = true
r.Disconnect()
}
// Connects to a given address over tcp. Sets a connection to a corresponding field in receiver
func (r *Receiver) Connect(addr string) error {
fmt.Printf("Trying to connect to %s...\n", addr)
connection, err := net.Dial("tcp", addr)
if err != nil {
return fmt.Errorf("could not connect to %s: %s", addr, err)
}
r.Connection = connection
fmt.Println("Connected to ", r.Connection.RemoteAddr())
return nil
}
// Handles the fileinfo packet. The choice of acceptance is given to the user
func (r *Receiver) HandleFileOffer(fileinfoPacket protocol.Packet) error {
// inform the user about the file
// fmt.Printf("Incoming fileinfo packet:\nFilename: %s\nFilesize: %.3fMB\nCheckSum: %s\nAccept ? [Y/N]: ",
// fileinfoPacket.Filename, float32(fileinfoPacket.Filesize)/1024/1024, fileinfoPacket.FileCheckSum)
fmt.Printf(`
Incoming fileinfo packet:
| Filename: %s
| Filesize: %.3fMB
| Checksum: %x
|
| Download ? [Y/N]: `,
fileinfoPacket.Filename, float32(fileinfoPacket.Filesize)/1024/1024, fileinfoPacket.FileCheckSum,
)
// get and process the input
var input string
fmt.Scanln(&input)
input = strings.TrimSpace(input)
input = strings.ToLower(input)
// reject the file
if input != "y" {
rejectionPacket := protocol.Packet{
Header: protocol.HeaderReject,
}
err := protocol.SendPacket(r.Connection, rejectionPacket)
if err != nil {
return fmt.Errorf("could not send a rejection packet: %s", err)
}
r.ReadyToReceive = false
return nil
}
// accept the file
r.FileToDownload = &File{
Filename: fileinfoPacket.Filename,
Filesize: fileinfoPacket.Filesize,
CheckSum: fileinfoPacket.FileCheckSum,
}
acceptancePacket := protocol.Packet{
Header: protocol.HeaderAccept,
}
err := protocol.SendPacket(r.Connection, acceptancePacket)
if err != nil {
return fmt.Errorf("could not send an acceptance packet: %s", err)
}
// can and ready to receive file packets
r.ReadyToReceive = true
return nil
}
// Handles the download by writing incoming bytes into the file
func (r *Receiver) WritePieceOfFile(filePacket protocol.Packet) error {
r.ReadyToReceive = false
// open|create a file with the same name as the filepacket`s file name
file, err := os.OpenFile(filepath.Join(r.DownloadsFolder, filePacket.Filename), os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
if err != nil {
return err
}
// just write the filedata
file.Write(filePacket.FileData)
file.Close()
r.PacketCounter++
r.ReadyToReceive = true
return nil
}
// Listens in an endless loop; reads incoming packages and puts them into channel
func (r *Receiver) ReceivePackets() {
for {
incomingPacket, err := protocol.ReadFromConn(r.Connection)
if err != nil {
// in current implementation there is no way to receive a working file even if only one packet is missing
fmt.Printf("Error reading a packet: %s\nExiting...", err)
os.Exit(-1)
}
r.IncomingPackets <- incomingPacket
}
}
// The "head" of the receiver. Similarly as in server`s logic "glues" everything together.
// Current structure allows the receiver to receive any type of packet
// in any order and react correspondingly
func (r *Receiver) MainLoop() {
go r.ReceivePackets()
for {
if r.Stopped {
// exit the mainloop
break
}
// send a packet telling sender to send another piece of file
if r.ReadyToReceive {
readyPacket := protocol.Packet{
Header: protocol.HeaderReady,
}
protocol.SendPacket(r.Connection, readyPacket)
r.ReadyToReceive = false
}
// no incoming packets ? Skipping the packet handling part
if len(r.IncomingPackets) == 0 {
continue
}
// take the packet and handle depending on the header
incomingPacket := <-r.IncomingPackets
// handling each packet header differently
switch incomingPacket.Header {
case protocol.HeaderFileInfo:
go r.HandleFileOffer(incomingPacket)
case protocol.HeaderFileData:
go r.WritePieceOfFile(incomingPacket)
case protocol.HeaderDisconnecting:
// the sender has completed its mission,
// checking hashes and exiting
fmt.Println("Got ", r.PacketCounter, " packets in total")
fmt.Println("Checking checksums...")
file, err := os.Open(filepath.Join(r.DownloadsFolder, r.FileToDownload.Filename))
if err != nil {
fmt.Printf("error while opening downloaded file for checking: %s\n", err)
os.Exit(-1)
}
realCheckSum, err := checksum.GetPartialCheckSum(file)
if err != nil {
fmt.Printf("error perfoming partial checksum: %s\n", err)
os.Exit(-1)
}
fmt.Printf("\n%x ----- %x\n", r.FileToDownload.CheckSum, realCheckSum)
if !checksum.AreEqual(realCheckSum, r.FileToDownload.CheckSum) {
fmt.Println("Downloaded file is corrupted !")
}
r.Stop()
}
}
}

17
server/file.go → sender/file.go

@ -1,11 +1,13 @@
package server
package sender
import (
"fmt"
"os"
"github.com/Unbewohnte/FTU/checksum"
)
// Struct that represents the served file. Used internally in the server
// Struct that represents the served file. Used internally in the sender
type File struct {
path string
Filename string
@ -14,9 +16,10 @@ type File struct {
LeftBytes uint64
SentPackets uint64
Handler *os.File
CheckSum checksum.CheckSum
}
// Prepares a file for serving. Used for preparing info before sending a handshake
// Prepares a file for serving. Used for preparing info before sending a fileinfo packet by sender
func getFile(path string) (*File, error) {
info, err := os.Stat(path)
if err != nil {
@ -24,8 +27,13 @@ func getFile(path string) (*File, error) {
}
handler, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("couldn`t be able to open the file: %s", err)
return nil, fmt.Errorf("wasn`t able to open the file: %s", err)
}
checksum, err := checksum.GetPartialCheckSum(handler)
if err != nil {
return nil, fmt.Errorf("could not get a partial file checksum: %s", err)
}
return &File{
path: path,
Filename: info.Name(),
@ -33,5 +41,6 @@ func getFile(path string) (*File, error) {
SentBytes: 0,
LeftBytes: uint64(info.Size()),
Handler: handler,
CheckSum: checksum,
}, nil
}

57
server/server.go → sender/sender.go

@ -1,4 +1,4 @@
package server
package sender
import (
"fmt"
@ -37,8 +37,8 @@ func GetRemoteIP() (string, error) {
return string(ip), nil
}
// The main server struct
type Server struct {
// The main sender struct
type Sender struct {
Port int
FileToTransfer *File
Listener net.Listener
@ -48,8 +48,8 @@ type Server struct {
Stopped bool
}
// Creates a new server with default fields
func NewServer(port int, filepath string) *Server {
// Creates a new sender with default fields
func NewSender(port int, filepath string) *Sender {
fileToTransfer, err := getFile(filepath)
if err != nil {
panic(err)
@ -70,8 +70,8 @@ func NewServer(port int, filepath string) *Server {
panic(err)
}
fmt.Printf("Created a new server at %s:%d (remote)\n%s:%d (local)\n", remoteIP, port, localIP, port)
return &Server{
fmt.Printf("Created a new sender at %s:%d (remote)\n%s:%d (local)\n", remoteIP, port, localIP, port)
return &Sender{
Port: port,
FileToTransfer: fileToTransfer,
Listener: listener,
@ -81,8 +81,8 @@ func NewServer(port int, filepath string) *Server {
}
}
// Closes the connection, warns about it the client and exits the mainloop
func (s *Server) Stop() {
// Closes the connection, warns about it the receiver and exits the mainloop
func (s *Sender) Stop() {
disconnectionPacket := protocol.Packet{
Header: protocol.HeaderDisconnecting,
}
@ -96,12 +96,12 @@ func (s *Server) Stop() {
}
// Closes current connection
func (s *Server) Disconnect() {
func (s *Sender) Disconnect() {
s.Connection.Close()
}
// Accepts one connection
func (s *Server) WaitForConnection() {
func (s *Sender) WaitForConnection() {
connection, err := s.Listener.Accept()
if err != nil {
fmt.Printf("Could not accept a connection: %s", err)
@ -112,16 +112,17 @@ func (s *Server) WaitForConnection() {
}
// Closes the listener. Used only when there is still no connection from `AcceptConnections`
func (s *Server) StopListening() {
func (s *Sender) StopListening() {
s.Listener.Close()
}
// Sends a packet with all information about a file to current connection
func (s *Server) SendOffer() error {
func (s *Sender) SendOffer() error {
err := protocol.SendPacket(s.Connection, protocol.Packet{
Header: protocol.HeaderFileInfo,
Filename: s.FileToTransfer.Filename,
Filesize: s.FileToTransfer.Filesize,
Header: protocol.HeaderFileInfo,
Filename: s.FileToTransfer.Filename,
Filesize: s.FileToTransfer.Filesize,
FileCheckSum: s.FileToTransfer.CheckSum,
})
if err != nil {
return fmt.Errorf("could not send an information about the file: %s", err)
@ -130,8 +131,8 @@ func (s *Server) SendOffer() error {
return nil
}
// Sends one file packet to the client
func (s *Server) SendPiece() error {
// Sends one file packet to the receiver
func (s *Sender) SendPiece() error {
// if no data to send - exit
if s.FileToTransfer.LeftBytes == 0 {
fmt.Printf("Done. Sent %d file packets\n", s.FileToTransfer.SentPackets)
@ -172,7 +173,7 @@ func (s *Server) SendPiece() error {
}
// Listens in an endless loop; reads incoming packages and puts them into channel
func (s *Server) ReceivePackets() {
func (s *Sender) ReceivePackets() {
for {
incomingPacket, err := protocol.ReadFromConn(s.Connection)
if err != nil {
@ -184,14 +185,14 @@ func (s *Server) ReceivePackets() {
}
}
// The "head" of the server. "Glues" all things together.
// Current structure allows the server to receive any type of packet
// The "head" of the sender. "Glues" all things together.
// Current structure allows the sender to receive any type of packet
// in any order and react correspondingly
func (s *Server) MainLoop() {
func (s *Sender) MainLoop() {
go s.ReceivePackets()
// send an information about the shared file to the client
// send an information about the shared file to the receiver
s.SendOffer()
for {
@ -199,7 +200,6 @@ func (s *Server) MainLoop() {
// exit the mainloop
break
}
// send, then process received packets
// no incoming packets ? Skipping the packet handling part
if len(s.IncomingPackets) == 0 {
@ -212,26 +212,27 @@ func (s *Server) MainLoop() {
switch incomingPacket.Header {
case protocol.HeaderAccept:
fmt.Printf("Client has accepted the transfer !\n")
fmt.Printf("The transfer has been accepted !\n")
// allowed to send file packets
s.CanTransfer = true
case protocol.HeaderReject:
fmt.Println("Client has rejected the transfer")
fmt.Println("Transfer has been rejected")
s.Stop()
// client is ready to receive the next file packet
// receiver is ready to receive the next file packet, sending...
case protocol.HeaderReady:
if !s.CanTransfer {
break
}
err := s.SendPiece()
if err != nil {
fmt.Printf("Could not send a piece of file: %s", err)
fmt.Printf("could not send a piece of file: %s", err)
os.Exit(-1)
}
case protocol.HeaderDisconnecting:
// receiver is dropping the file transfer ?
s.Stop()
}
}
Loading…
Cancel
Save