Unbewohnte
3 years ago
9 changed files with 421 additions and 277 deletions
@ -0,0 +1,20 @@ |
|||||||
|
// This file contains global constants of the protocol
|
||||||
|
package protocol |
||||||
|
|
||||||
|
// MAXPACKETSIZE.
|
||||||
|
// How many bytes can contain one packet (header + body) at maximum
|
||||||
|
// (packets with size bigger than MAXPACKETSIZE are invalid and will not be sent)
|
||||||
|
const MAXPACKETSIZE uint = 1024 // the same problem as in the previous versions: if the packet is big enough - the conn.Read()
|
||||||
|
// will result in some sort of error where it does not read the intended amount of bytes (less, in fact),
|
||||||
|
// which is strange, but I guess that
|
||||||
|
// I just do something wrong in my code
|
||||||
|
|
||||||
|
// PACKETSIZEDELIMETER.
|
||||||
|
// Character that delimits one and the other sides of the next incoming packet.
|
||||||
|
// ie: |packet_size_here|packet_here, where "|" is PACKETSIZEDELIMETER
|
||||||
|
const PACKETSIZEDELIMETER string = "|" |
||||||
|
|
||||||
|
// HEADERDELIMETER.
|
||||||
|
// Character that delimits header of the packet from the body of the packet.
|
||||||
|
// ie: FILEINFO~img.png
|
||||||
|
const HEADERDELIMETER string = "~" |
@ -0,0 +1,77 @@ |
|||||||
|
// This file describes various headers of the protocol and how to use them
|
||||||
|
package protocol |
||||||
|
|
||||||
|
type Header string |
||||||
|
|
||||||
|
// Headers
|
||||||
|
|
||||||
|
//// In the following below examples "|" is PACKETSIZEDELIMETER and "~" is HEADERDELIMETER
|
||||||
|
|
||||||
|
// FILENAME.
|
||||||
|
// This header is sent only by sender. The packet with this header
|
||||||
|
// must contain a name of the transported file in BODY.
|
||||||
|
// ie: |18|FILENAME~image.png
|
||||||
|
const HeaderFilename Header = "FILENAME" |
||||||
|
|
||||||
|
// FILESIZE.
|
||||||
|
// This header is sent only by sender. The packet with this header
|
||||||
|
// must contain a size of the transported file in its BODY.
|
||||||
|
// ie: |15|FILESIZE~512442
|
||||||
|
const HeaderFileSize Header = "FILESIZE" |
||||||
|
|
||||||
|
// CHECKSUM.
|
||||||
|
// Just like before, this header must be sent in a packet only by sender,
|
||||||
|
// BODY must contain a checksum of the transported file.
|
||||||
|
// ie: |74|CHECKSUM~1673f585148148d0c105af0d55646d6cbbf37e33a7366d3b72d8c5caca13434a
|
||||||
|
const HeaderChecksum Header = "CHECKSUM" |
||||||
|
|
||||||
|
// DOYOACCEPT.
|
||||||
|
// Sent by sender after all the information about the transfered file has been sent.
|
||||||
|
// Receiving a packet with this header means that there will be no more additional information about the
|
||||||
|
// file and the sender is waiting for response (acceptance or rejection of the file).
|
||||||
|
// ie: |13|DOYOUACCEPT?~
|
||||||
|
const HeaderAcceptance Header = "DOYOUACCEPT?" |
||||||
|
|
||||||
|
// FILEBYTES.
|
||||||
|
// Sent only by sender. The packet`s body must contain
|
||||||
|
// a portion of transported file`s bytes.
|
||||||
|
// ie: |70|FILEBYTES~fj2pgfjek;hjg02yg082qyuhg83hvuahjvlhsaoughuihgp9earhguhergh\n
|
||||||
|
const HeaderFileBytes Header = "FILEBYTES" |
||||||
|
|
||||||
|
// FILEREJECT.
|
||||||
|
// Sent only by receiver if the user has decided to not download the file.
|
||||||
|
// The BODY may or may not be empty (preferably empty, of course), in any way, it will not be
|
||||||
|
// used in any way.
|
||||||
|
// ie: |11|FILEREJECT~
|
||||||
|
const HeaderReject Header = "FILEREJECT" |
||||||
|
|
||||||
|
// FILEACCEPT.
|
||||||
|
// The opposite of the previous FILEREJECT. Send by receiver when
|
||||||
|
// the user has agreed to download the file.
|
||||||
|
// ie: |11|FILEACCEPT~
|
||||||
|
const HeaderAccept Header = "FILEACCEPT" |
||||||
|
|
||||||
|
// DONE.
|
||||||
|
// Sent by sender. Warns the receiver that the file transfer has been done and
|
||||||
|
// there is no more information to give.
|
||||||
|
// ie: |5|DONE~
|
||||||
|
// Usually after the packet with this header has been sent, the receiver will send
|
||||||
|
// another packet back with header BYE!, telling that it`s going to disconnect
|
||||||
|
const HeaderDone Header = "DONE" |
||||||
|
|
||||||
|
// READY.
|
||||||
|
// Sent by receiver when it hass read and processed the last
|
||||||
|
// FILEBYTES packet. The sender does not allowed to "spam" FILEBYTES
|
||||||
|
// packets without the permission of receiver.
|
||||||
|
// ie: |7|READY!~
|
||||||
|
const HeaderReady Header = "READY" |
||||||
|
|
||||||
|
// BYE!.
|
||||||
|
// Packet with this header can be sent both by receiver and sender.
|
||||||
|
// It`s used when the sender or the receiver are going to disconnect
|
||||||
|
// and will not be able to communicate.
|
||||||
|
// (Usually it`s when the error has happened, OR, in a good situation, after the DONE header
|
||||||
|
// has been sent by sender, warning receiver that there is no data to send)
|
||||||
|
// The BODY is better to be empty.
|
||||||
|
// ie: |5|BYE!~
|
||||||
|
const HeaderDisconnecting Header = "BYE!" |
@ -0,0 +1,127 @@ |
|||||||
|
// This file describes the general packet structure and provides methods to work with them before|after the transportation
|
||||||
|
|
||||||
|
// Examples of packets, ready for transportation in pseudo-code:
|
||||||
|
// []byte(|34|FILEDATA~fe2[gkr3j930f]fwpglkrt[o])
|
||||||
|
// []byte(|57|FILENAME~theBestFileNameEver_Existed_in'''theUniverse.txt)
|
||||||
|
// general structure:
|
||||||
|
// PACKETSIZEDELIMETER packetsize PACKETSIZEDELIMETER packet.Header HEADERDELIMETER packet.Body (without spaces between)
|
||||||
|
package protocol |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"net" |
||||||
|
"strconv" |
||||||
|
) |
||||||
|
|
||||||
|
// Internal representation of packet before|after the transportation
|
||||||
|
type Packet struct { |
||||||
|
Header Header |
||||||
|
Body []byte |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a size of the given packet as if it would be sent and presented in bytes.
|
||||||
|
// ie: FILESIZE~[49 49 56 55 56 53 50 49 54]
|
||||||
|
// DOES COUNT THE PACKETSIZEDELIMETER
|
||||||
|
func MeasurePacketSize(packet Packet) uint64 { |
||||||
|
packetBytes := new(bytes.Buffer) |
||||||
|
packetBytes.Write([]byte(packet.Header)) |
||||||
|
packetBytes.Write([]byte(HEADERDELIMETER)) |
||||||
|
packetBytes.Write(packet.Body) |
||||||
|
|
||||||
|
return uint64(packetBytes.Len()) |
||||||
|
} |
||||||
|
|
||||||
|
// Converts packet bytes into Packet struct
|
||||||
|
func BytesToPacket(packetbytes []byte) Packet { |
||||||
|
var header Header |
||||||
|
var body []byte |
||||||
|
|
||||||
|
for counter, b := range packetbytes { |
||||||
|
if string(b) == HEADERDELIMETER { |
||||||
|
header = Header(packetbytes[0:counter]) |
||||||
|
body = packetbytes[counter+1:] |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
return Packet{ |
||||||
|
Header: header, |
||||||
|
Body: body, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Sends given packet to connection, following all the protocol`s rules.
|
||||||
|
// ALL packets MUST be sent by this method
|
||||||
|
func SendPacket(connection net.Conn, packetToSend Packet) error { |
||||||
|
packetSize := MeasurePacketSize(packetToSend) |
||||||
|
|
||||||
|
if packetSize > uint64(MAXPACKETSIZE) { |
||||||
|
return fmt.Errorf("invalid packet: HEADER: %s BODY: %s: EXCEEDED MAX PACKETSIZE", packetToSend.Header, packetToSend.Body) |
||||||
|
} |
||||||
|
|
||||||
|
// packetsize between delimeters (ie: |17|)
|
||||||
|
packetSizeBytes := []byte(strconv.Itoa(int(packetSize))) |
||||||
|
|
||||||
|
// creating a buffer and writing the whole packet into it
|
||||||
|
packet := new(bytes.Buffer) |
||||||
|
|
||||||
|
packet.Write([]byte(PACKETSIZEDELIMETER)) |
||||||
|
packet.Write(packetSizeBytes) |
||||||
|
packet.Write([]byte(PACKETSIZEDELIMETER)) |
||||||
|
|
||||||
|
packet.Write([]byte(packetToSend.Header)) |
||||||
|
packet.Write([]byte(HEADERDELIMETER)) |
||||||
|
packet.Write(packetToSend.Body) |
||||||
|
|
||||||
|
// write the result (ie: |17|FILENAME~file.png)
|
||||||
|
connection.Write(packet.Bytes()) |
||||||
|
|
||||||
|
// for debug purposes (ᗜˬᗜ)
|
||||||
|
// fmt.Printf("SENDING PACKET: %s%s%s%s%s%s\n",
|
||||||
|
// []byte(PACKETSIZEDELIMETER), packetSizeBytes, []byte(PACKETSIZEDELIMETER),
|
||||||
|
// []byte(packetToSend.Header), []byte(HEADERDELIMETER), packetToSend.Body)
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Reads a packet from given connection.
|
||||||
|
// ASSUMING THAT THE PACKETS ARE SENT BY `SendPacket` function !!!!
|
||||||
|
func ReadFromConn(connection net.Conn) (Packet, error) { |
||||||
|
var err error |
||||||
|
var delimeterCounter int = 0 |
||||||
|
var packetSizeStrBuffer string = "" |
||||||
|
var packetSize int = 0 |
||||||
|
|
||||||
|
for { |
||||||
|
buffer := make([]byte, 1) |
||||||
|
connection.Read(buffer) |
||||||
|
|
||||||
|
if string(buffer) == PACKETSIZEDELIMETER { |
||||||
|
delimeterCounter++ |
||||||
|
|
||||||
|
// the first delimeter has been found, skipping the rest of the loop
|
||||||
|
if delimeterCounter == 1 { |
||||||
|
continue |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// the last delimeter, the next read will be the packet itself, so breaking
|
||||||
|
if delimeterCounter == 2 { |
||||||
|
break |
||||||
|
} |
||||||
|
|
||||||
|
packetSizeStrBuffer += string(buffer) |
||||||
|
} |
||||||
|
|
||||||
|
packetSize, err = strconv.Atoi(packetSizeStrBuffer) |
||||||
|
if err != nil { |
||||||
|
return Packet{}, fmt.Errorf("could not convert packetsizeStr into int: %s", err) |
||||||
|
} |
||||||
|
|
||||||
|
// have a packetsize, now reading the whole packet
|
||||||
|
packetBuff := make([]byte, packetSize) |
||||||
|
connection.Read(packetBuff) |
||||||
|
|
||||||
|
packet := BytesToPacket(packetBuff) |
||||||
|
|
||||||
|
return packet, nil |
||||||
|
} |
@ -1,167 +0,0 @@ |
|||||||
package protocol |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"net" |
|
||||||
"strconv" |
|
||||||
"strings" |
|
||||||
|
|
||||||
"github.com/Unbewohnte/FTU/checksum" |
|
||||||
) |
|
||||||
|
|
||||||
// a package that describes how server and client should communicate
|
|
||||||
|
|
||||||
const MAXPACKETSIZE int = 2048 // whole packet
|
|
||||||
const MAXFILEDATASIZE int = 512 // only `FileData` | MUST be less than `MAXPACKETSIZE`
|
|
||||||
const PACKETSIZEDELIMETER string = "|" |
|
||||||
|
|
||||||
// Headers
|
|
||||||
type Header string |
|
||||||
|
|
||||||
const HeaderFileData Header = "FILEDATA" |
|
||||||
const HeaderFileInfo Header = "FILEINFO" |
|
||||||
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"` |
|
||||||
FileCheckSum checksum.CheckSum `json:"CheckSum"` |
|
||||||
FileData []byte `json:"Filedata"` |
|
||||||
} |
|
||||||
|
|
||||||
// converts valid packet bytes into `Packet` struct
|
|
||||||
func ReadPacketBytes(packetBytes []byte) (Packet, error) { |
|
||||||
var packet Packet |
|
||||||
err := json.Unmarshal(packetBytes, &packet) |
|
||||||
if err != nil { |
|
||||||
return Packet{}, fmt.Errorf("could not unmarshal packet bytes: %s", err) |
|
||||||
} |
|
||||||
return packet, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Converts `Packet` struct into []byte
|
|
||||||
func EncodePacket(packet Packet) ([]byte, error) { |
|
||||||
packetBytes, err := json.Marshal(packet) |
|
||||||
if err != nil { |
|
||||||
return nil, fmt.Errorf("could not marshal packet bytes: %s", err) |
|
||||||
} |
|
||||||
return packetBytes, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Measures the packet length
|
|
||||||
func MeasurePacket(packet Packet) (uint64, error) { |
|
||||||
packetBytes, err := EncodePacket(packet) |
|
||||||
if err != nil { |
|
||||||
return 0, fmt.Errorf("could not measure the packet: %s", err) |
|
||||||
} |
|
||||||
return uint64(len(packetBytes)), nil |
|
||||||
} |
|
||||||
|
|
||||||
// Checks if given packet is valid, returns a boolean and an explanation message
|
|
||||||
func IsValidPacket(packet Packet) (bool, string) { |
|
||||||
packetSize, err := MeasurePacket(packet) |
|
||||||
if err != nil { |
|
||||||
return false, "Measurement error" |
|
||||||
} |
|
||||||
if packetSize > uint64(MAXPACKETSIZE) { |
|
||||||
return false, "Exceeded MAXPACKETSIZE" |
|
||||||
} |
|
||||||
if len(packet.FileData) > MAXFILEDATASIZE { |
|
||||||
return false, "Exceeded MAXFILEDATASIZE" |
|
||||||
} |
|
||||||
|
|
||||||
if strings.TrimSpace(string(packet.Header)) == "" { |
|
||||||
return false, "Empty header" |
|
||||||
} |
|
||||||
return true, "" |
|
||||||
} |
|
||||||
|
|
||||||
// Sends a given packet to connection using a special sending format
|
|
||||||
// ALL packets MUST be sent by this method
|
|
||||||
func SendPacket(connection net.Conn, packet Packet) error { |
|
||||||
isvalid, msg := IsValidPacket(packet) |
|
||||||
if !isvalid { |
|
||||||
return fmt.Errorf("this packet is invalid !: %v; The error: %v", packet, msg) |
|
||||||
} |
|
||||||
|
|
||||||
packetSize, err := MeasurePacket(packet) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// write packetsize between delimeters (ie: |727|{"HEADER":"PING"...})
|
|
||||||
connection.Write([]byte(fmt.Sprintf("%s%d%s", PACKETSIZEDELIMETER, packetSize, PACKETSIZEDELIMETER))) |
|
||||||
|
|
||||||
// write the packet itself
|
|
||||||
packetBytes, err := EncodePacket(packet) |
|
||||||
if err != nil { |
|
||||||
return fmt.Errorf("could not send a packet: %s", err) |
|
||||||
} |
|
||||||
connection.Write(packetBytes) |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// Reads a packet from given connection.
|
|
||||||
// ASSUMING THAT THE PACKETS ARE SENT BY `SendPacket` function !!!!
|
|
||||||
func ReadFromConn(connection net.Conn) (Packet, error) { |
|
||||||
var err error |
|
||||||
var delimeterCounter int = 0 |
|
||||||
var packetSizeStrBuffer string = "" |
|
||||||
var packetSize int = 0 |
|
||||||
|
|
||||||
for { |
|
||||||
// reading byte-by-byte
|
|
||||||
buffer := make([]byte, 1) |
|
||||||
connection.Read(buffer) // no fixed time limit, so no need to check for an error
|
|
||||||
|
|
||||||
// found a delimeter
|
|
||||||
if string(buffer) == PACKETSIZEDELIMETER { |
|
||||||
delimeterCounter++ |
|
||||||
|
|
||||||
// the first delimeter has been found, skipping the rest of the loop
|
|
||||||
if delimeterCounter == 1 { |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// found the first delimeter, skip was performed, now reading an actual packetsize
|
|
||||||
if delimeterCounter == 1 { |
|
||||||
// adding a character of the packet size to the `string buffer`; ie: | <- read, reading now -> 1 23|PACKET_HERE
|
|
||||||
packetSizeStrBuffer += string(buffer) |
|
||||||
|
|
||||||
} else if delimeterCounter == 2 { |
|
||||||
// found the last delimeter, thus already read the whole packetsize
|
|
||||||
// converting from string to int
|
|
||||||
packetSize, err = strconv.Atoi(packetSizeStrBuffer) |
|
||||||
if err != nil { |
|
||||||
return Packet{}, fmt.Errorf("could not convert packet size into integer: %s", err) |
|
||||||
} |
|
||||||
// packet size has been found, breaking from the loop
|
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// have a packetsize, now reading the whole packet
|
|
||||||
packetBuffer := make([]byte, packetSize) |
|
||||||
connection.Read(packetBuffer) |
|
||||||
|
|
||||||
packet, err := ReadPacketBytes(packetBuffer) |
|
||||||
if err != nil { |
|
||||||
return Packet{}, err |
|
||||||
} |
|
||||||
|
|
||||||
isvalid, msg := IsValidPacket(packet) |
|
||||||
if isvalid { |
|
||||||
return packet, nil |
|
||||||
} |
|
||||||
|
|
||||||
return Packet{}, fmt.Errorf("received an invalid packet. Reason: %s", msg) |
|
||||||
} |
|
Loading…
Reference in new issue