Unbewohnte
4 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