my net house

WAHEGURU….!

Create a Ridiculously Simple FileServer in Golang

Having light weight tool like Goalng in your ToolChain which is As easy as to Adapt like Python and Powerful than C or C++ you get to play with every aspect of  computer-Science.

You just need two Files, one is server.go and other would be client.go

In the server.go we will open connection on particular port to accept the connections, Most of the capabilities are inside the “net” package provided by Golang.
All you need to know that how to open connection and how to Create buffer size and how to write that buffer into the connection, All you need to look for few docs
for net package and you are great to go with one of your first FileServer of Golang.


package main

import (
“fmt”
“io”
“net”
“os”
“strconv”
)

//Define the size of how big the chunks of data will be send each time
const BUFFERSIZE = 1024

func main() {
//Create a TCP listener on localhost with porth 27001
server, err := net.Listen(“tcp”, “localhost:27001”)
if err != nil {
fmt.Println(“Error listetning: “, err)
os.Exit(1)
}
defer server.Close()
fmt.Println(“Server started! Waiting for connections…”)
//Spawn a new goroutine whenever a client connects
//for {
connection, err := server.Accept()
if err != nil {
fmt.Println(“Error: “, err)
os.Exit(1)
}
fmt.Println(“Client Connected”)
//go sendFileToClient(connection)
//sendFileToClient(connection)

defer connection.Close()
//Open the file that needs to be send to the client
file, err := os.Open(“dummyfile.dat”)
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
//Get the filename and filesize
fileInfo, err := file.Stat()
if err != nil {
fmt.Println(err)
return
}
fileSize := fillString(strconv.FormatInt(fileInfo.Size(), 10), 10)
fileName := fillString(fileInfo.Name(), 64)
//Send the file header first so the client knows the filename and how long it has to read the incomming file
fmt.Println(“Sending filename and filesize!”)
//Write first 10 bytes to client telling them the filesize
connection.Write([]byte(fileSize))
//Write 64 bytes to client containing the filename
connection.Write([]byte(fileName))
//Initialize a buffer for reading parts of the file in
sendBuffer := make([]byte, BUFFERSIZE)
//Start sending the file to the client
fmt.Println(“Start sending file!”)
for {
_, err = file.Read(sendBuffer)
if err == io.EOF {
//End of file reached, break out of for loop
break
}
connection.Write(sendBuffer)
}
fmt.Println(“File has been sent, closing connection!”)
//}
}

//This function is to ‘fill’
func fillString(retunString string, toLength int) string {
for {
lengtString := len(retunString)
if lengtString < toLength {
retunString = retunString + ":"
continue
}
break
}
return retunString
}

Note:
Fill String Method: Fill String Method has good amount of significance, Each time When Server and Client code will be able to communicate
with each other one need to look for “Exact amount of file Size and FileName should be received to client, Only then we can do good amount of Sync between both the Ends”

Now let’s have closer look with Golang client to find out how to receive file from server, Which is quite simple, Now we have Buffered file size and that we will red from
connection, We have file name in Byte string as well, which we will also read from server and create the file of same name,,once reading is finished we will just save thatByte string into the file and good to go with all the things.

package main

import (
“fmt”
“io”
“net”
“os”
“strconv”
“strings”
)

//Define that the binairy data of the file will be sent 1024 bytes at a time
const BUFFERSIZE = 1024

func main() {
connection, err := net.Dial(“tcp”, “localhost:27001”)
if err != nil {
panic(err)
}
defer connection.Close()
fmt.Println(“Connected to server, start receiving the file name and file size”)
//Create buffer to read in the name and size of the file
bufferFileName := make([]byte, 64)
bufferFileSize := make([]byte, 10)

//Get the filesize
connection.Read(bufferFileSize)
//Strip the ‘:’ from the received size, convert it to a int64
fileSize, _ := strconv.ParseInt(strings.Trim(string(bufferFileSize), “:”), 10, 64) // Look carefully for 10 and 64 here. We have max of 10 bytes of fileSize and then we will convert string to int64

//Get the filename
connection.Read(bufferFileName)
//Strip the ‘:’ once again but from the received file name now
fileName := strings.Trim(string(bufferFileName), “:”)

newFile, err := os.Create(fileName)
if err != nil {
panic(err)
}
defer newFile.Close()
//Create a variable to store in the total amount of data that we received already
var receivedBytes int64

//Start writing in the file
for {
if (fileSize – receivedBytes) < BUFFERSIZE {
io.CopyN(newFile, connection, (fileSize – receivedBytes))
//Empty the remaining bytes that we don't need from the network buffer
connection.Read(make([]byte, (receivedBytes+BUFFERSIZE)-fileSize))
//We are done writing the file, break out of the loop
break
}
io.CopyN(newFile, connection, BUFFERSIZE)
//Increment the counter
receivedBytes += BUFFERSIZE
}
fmt.Println("Received file completely!")
}

 

Now in the above server.go you need to make Client requests Asynchronous, So let's make Go-Routine.

package main

import (
"fmt"
"io"
"net"
"os"
"strconv"
)

//Define the size of how big the chunks of data will be send each time
const BUFFERSIZE = 1024

func main() {
//Create a TCP listener on localhost with porth 27001
server, err := net.Listen("tcp", "localhost:27001")
if err != nil {
fmt.Println("Error listetning: ", err)
os.Exit(1)
}
defer server.Close()
fmt.Println("Server started! Waiting for connections…")
//Spawn a new goroutine whenever a client connects
for {
connection, err := server.Accept()
if err != nil {
fmt.Println("Error: ", err)
os.Exit(1)
}
fmt.Println("Client connected")
go sendFileToClient(connection)
}

}

//This function is to 'fill'
func fillString(retunString string, toLength int) string {
for {
lengtString := len(retunString)
if lengtString < toLength {
retunString = retunString + ":"
continue
}
break
}
return retunString
}

func sendFileToClient(connection net.Conn) {
fmt.Println("A client has connected!")
defer connection.Close()
//Open the file that needs to be send to the client
file, err := os.Open("dummyfile.dat")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
//Get the filename and filesize
fileInfo, err := file.Stat()
if err != nil {
fmt.Println(err)
return
}
//fileSize := strconv.FormatInt(fileInfo.Size(), 10)
fileSize := fillString(strconv.FormatInt(fileInfo.Size(), 10), 10)
//fileName := fileInfo.Name()
fileName := fillString(fileInfo.Name(), 64)
//Write first 10 bytes to client telling them the filesize
connection.Write([]byte(fileSize))
//Write 64 bytes to client containing the filename
connection.Write([]byte(fileName))
//Initialize a buffer for reading parts of the file in
sendBuffer := make([]byte, BUFFERSIZE)
//Start sending the file to the client
fmt.Println("Start sending file!")
for {
_, err = file.Read(sendBuffer)
if err == io.EOF {
//End of file reached, break out of for loop
break
}
connection.Write(sendBuffer)
}
fmt.Println("File has been sent, closing connection!")
return
}
 

Look for the function sendFileToClient() where we have went through Separate Process to write filesize, Filename and filebuffer to connection. One more thing to look carefully for the following code:


for {
connection, err := server.Accept()
if err != nil {
fmt.Println("Error: ", err)
os.Exit(1)
}
fmt.Println("Client connected")
go sendFileToClient(connection)
}

This is infinite “for loop” so for any client request and each time Go routine will be fired up. That’s it! Stay Tuned for more Fun!! and More Go!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: