diff --git a/Makefile b/Makefile index 70ab6cc..a8d2fe6 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,10 @@ all: client server +re: clean client server + +clean: + rm client.o server.o + client: go build -o client.o client.go diff --git a/README.md b/README.md index 48c8fc0..8ed60d1 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,12 @@ The client uses the following flags -l - list all quotes -r [i] - remove; may be run with or without argument -ip - specify alternate server destination +-s - specify the size of the receipt buffer +``` +The server uses the following flags +``` sh +-h - display help message +-s - specify the size of the receipt buffer ``` ### How It Works @@ -66,7 +72,6 @@ The source code is in the same folder, ```ekho-seq.txt```, and can be uploaded t ### To Be Implemented -- Multiple transactions for large quote file - Log all uses/transaction data each day - Request, send, and receive the logfile for any day - User-specific quotes/quotefiles diff --git a/client.go b/client.go index 7149dee..cc0253e 100644 --- a/client.go +++ b/client.go @@ -1,10 +1,10 @@ // ============================================================================= // Auth: alex // File: client.go -// Revn: 10-05-2023 2.0 +// Revn: 06-14-2024 3.0 // Func: ask server for a message of the day quote // -// TODO: document +// TODO: remove ioutil import // expand on /ba/ failures // add more flags? // log errors in logfile @@ -27,6 +27,12 @@ //*10-05-2023: /remove/ works // removed -d flag // commented +// 06-12-2024: began added PWFT to /la/ +// 06-13-2024: finished adding PWFT to /la/ +// commented PWFT stuff +// STARTED BYTE-BASED PWFT REWRITE +//*06-14-2024: finished byte-based PWFT rewrite +// commented // // ============================================================================= @@ -34,11 +40,12 @@ package main import ( "io" // io.EOF + "io/ioutil" // ReadFile "flag" // BoolVar, StringVar, Parse "fmt" // Println "math/rand" // NewSource, New, Intn, *rand.Rand "net" // ResolveTCPAddr, DialTCP, conn.Write,Read - "os" // Exit + "os" // Exit, Create "strconv" // Atoi, Itoa "strings" // Split "time" // Now, UnixNano @@ -118,7 +125,10 @@ func draw() string { // get server response and print func handle( conn net.Conn ) { - var buffer [512]byte // create buffer to hold response + // create buffer to hold response + buffer := make( []byte, tsize ) + + //var buffer [32]byte // create buffer to hold response // read n bytes from server into buffer ( byte slice ) n, err := conn.Read( buffer[:] ) check( err ) // check for errors @@ -149,13 +159,51 @@ func handle( conn net.Conn ) { fmt.Println( msg[1] ) // whole-ass just print quote resp = "te@success" // set transaction end message case "la": // /la/, list answer - // split recv'd message over newline to create quote list - quotes := strings.Split( msg[1], "\n" ) - for p, v := range quotes { // iterate over quotes - // print num of quote, followed by that quote - fmt.Println( p + 1, "\t", v ) + if len( msg ) > 2 { // multistage send condition + if msg[1] == "X" { // final message + // TODO update + // read entire temp storage file as byte array + tfile, err := ioutil.ReadFile( "temp.q" ) + check( err ) // check read for errors + // cast file to string, concat with recv'd string + tstr := string( tfile ) + msg[2] + // split concat over newlines, separating quotes + tlist := strings.Split( tstr, "\n" ) + // iterate over array, printing each quote + for p, v := range tlist { + fmt.Println( p + 1, "\t", v ) + } + // delete temp storage file + err = os.Remove( "temp.q" ) + check( err ) // check rm for errors + resp = "te@X" // successful response message + } else { // first or intermediate message + // open temp quote storage file, creating if + // necessary, with write-only permissions, in + // append mode, wr u perm, r g/w perm + f, err := os.OpenFile( "temp.q", os.O_WRONLY | os.O_APPEND | os.O_CREATE, 0644 ) + check( err ) // check file open for errors + // write received message to file, keeping track + // of bytes written + n, err = f.WriteString( msg[2] ) + check( err ) // check file write for errors + // cast bytes already written field to int + b, err := strconv.Atoi( msg[1] ) + check( err ) // check cast for errors + // add bytes just written to bytes already + // written, cast back to string, and append to + // response variable + resp = "lr@" + strconv.Itoa( b + n ) + } + } else { + // split recv'd message over \n, create quote list + quotes := strings.Split( msg[1], "\n" ) + for p, v := range quotes { // iterate over quotes + // print num of quote, followed by that quote + fmt.Println( p + 1, "\t", v ) + } + resp = "te@success" // set transaction end message } - resp = "te@success" // set transaction end message case "aa": // /aa/, add answer // if server says add succeeded if msg[1] == "success" { @@ -236,7 +284,7 @@ var add bool // add new quotes to quote file ( on server ) var remove bool // remove quotes from quote file ( on server ) var list bool // request entire quote file from server var dest string // provide a new destination ip and port - +var tsize int // size of transmission buffer func main() { @@ -245,6 +293,7 @@ func main() { flag.BoolVar( &remove, "r", false, "remove quote from list" ) flag.BoolVar( &list, "l", false, "print quote list" ) flag.StringVar( &dest, "ip", ":1300", "ip:port of server" ) + flag.IntVar( &tsize, "s", 256, "transmission size ( bytes )" ) flag.Parse() // process flags service := dest // declare server ip:port @@ -261,7 +310,7 @@ func main() { var command string switch { // empty switch statement is basically an if case list: // if list flag is active - command = "lr@dumvar" // list request + command = "lr@init" // list request // TODO come up with a useful argument, instead of dumvar case add: // if add flag is active // if arguments were specified diff --git a/server.go b/server.go index e58d40b..816caf7 100644 --- a/server.go +++ b/server.go @@ -1,11 +1,13 @@ // ============================================================================= -// Auth: Alex Celani +// Auth: alex // File: server.go -// Revn: 10-17-2023 3.0 -// Func: host connection, reply to speak.go +// Revn: 06-14-2024 4.0 +// Func: host MOTD connection // -// TODO: add more /codes/ to handle -// add flags? +// TODO: catch keyboard int. signal +// remove ioutil import +// add more /codes/ to handle +// add flags, like specify port // write usage and errors to logfile // send logfile to client on request // ============================================================================= @@ -35,12 +37,19 @@ // removed implicit newline at end of qfile to stop ghost // line bug in /la/ // removed dead debug print in fread() +// 05-26-2024: began writing PWFT update +// added flag to specify transmission buffer size +// 06-12-2024: "successfully" wrote PWFT +// 06-13-2024: removed debug print statements +// commented +//*06-14-2024: byte count-based PWFT rewrite // // ============================================================================= package main import ( + "flag" // IntVar, Parse "fmt" // Println "io" // io.EOF "io/ioutil" // ReadFile @@ -102,7 +111,7 @@ func fread() string { // bugs where a blank item was introduced into qlist and calling // /la/ would produce a blank line on the client side that didn't // really exist - qfile = qfile[:len( qfile ) - 1] + //qfile = qfile[:len( qfile ) - 1] // cast bytes to string, split string over newline into array qlist = strings.Split( string( qfile ), "\n" ) // deal with the trailing @@ -125,7 +134,8 @@ func writeFile( f string ) { func handle( conn net.Conn ) { // create buffer to hold read message - var buffer [256]byte + buffer := make( []byte, tsize ) + //var buffer [256]byte // read n bytes from client n, err := conn.Read( buffer[:] ) checkN( err, conn ) // make sure read worked @@ -167,10 +177,42 @@ func handle( conn net.Conn ) { } } case "lr": // /lr/, list request - // just concat entire list to send - //resp = "la@" + qfile[:len( qfile ) - 1] // list answer - resp = "la@" + qfile // list answer - // TODO what happens if this is longer than 256 char? + resp = "la@" // begin crafting response + // file too big for single transmission + // initial header size is 5, la@1@, so counting that into + // the transmission size, the file must be longer than 251 + // ( default ) bytes to reach this block of code + if len( qfile ) > ( tsize - 5 ) { + if command[1] == "init" { // first request + // add message number, delim, and the first + // transmittable bytes + resp = resp + "0@" + qfile[:( tsize - 5 )] + } else { // subsequent requests + // convert byte count to int + bytes, err := strconv.Atoi( command[1] ) + check( err ) // make sure convert worked + // replace tx'd bytes and delim to new header + resp = resp + command[1] + "@" + rlen := len( resp ) // capture header length + // slices are always of size "txsize - header + // length", so find end byte + end := bytes + ( tsize - rlen ) + // if end goes beyond the bounds of the list + if end > len( qfile ) { + // replace byte count with X to signal + // final message, concat the rest of the file, + // beginning with the start index + resp = "la@X@" + qfile[bytes:] + } else { + // get slice from start index to end, concat + // with response + resp = resp + qfile[num:end] + } + } + } else { // file is smaller than the tx size + // just concat entire list to send + resp = resp + qfile // list answer + } case "ar": // /ar/, add request // append new quote to list qlist = append( qlist, command[1] ) @@ -178,7 +220,6 @@ func handle( conn net.Conn ) { qfile = qfile + "\n" + command[1] writeFile( qfile ) resp = "aa@success" // add answer - // TODO write to file case "r1": // /r1/, remove request 1 // same as list, just send whole file resp = "r1@" + qfile // remove request 1 @@ -251,11 +292,16 @@ func handle( conn net.Conn ) { // globals var qlist []string // list of quotes var qfile string // file object +var tsize int // size of transmission buffer // main, create port and wait for connection func main() { + // flag for default transmission size + flag.IntVar( &tsize, "s", 256, "transmission size ( bytes )" ) + flag.Parse() + qfile = fread() // read quote file, init ( global ) list of quotes service := ":1300" // create service on ip and port