I'm trying to write a simple tcp server and client program.
When I run both the server and client code below, the client will simply receive the time message from server and exit, and the server continues to accept a new connection. My expected program behaviour is I want the server to also receive the "hello world" message from the client and and close the connection and so does client by using the "responseConnection" function.
But the problem is when I started the client, the server seems to be stuck at the conn.Read function inside "responseConnection" function and has error "EOF exit status 1" when I stopped client first, which is really ambiguous to debug. When I stopped server first, client will receive the time data from server.
If you have any ideas why, please help to answer as I'm totally a newbie to Golang. Thank you so much!
server.go
package main
import (
"fmt"
"io"
"log"
"net"
"time"
)
type connection struct {
host string
port string
network string
}
func checkError(err error) {
if err != nil {
log.Fatalln(err)
}
}
func responseConnection(conn net.Conn) {
defer conn.Close() // <-- When responseConnection() is used, add this
buf := make([]byte, 0, 4096)
tmp := make([]byte, 256)
for {
n, err := conn.Read(tmp)
if err != nil {
checkError(err)
if err != io.EOF {
fmt.Println("Error reading: ", err)
}
// fmt.Println(err)
break
}
buf = append(buf, tmp[:n]...)
}
fmt.Println("Data from client ========> ", string(buf))
// c <- buf
}
func handleConnection(conn net.Conn) {
defer conn.Close() // <- When responseConnection() is used, remove this
dayTime := time.Now().String()
conn.Write([]byte(dayTime))
// responseConnection(conn)
fmt.Println("The end of handleConnection")
}
func main() {
localConn := connection{
host: "",
port: "5555",
network: "tcp",
}
// servicePort := ":5555"
tcpAddr, err := net.ResolveTCPAddr(localConn.network, ":"+localConn.port)
checkError(err)
l, err := net.ListenTCP("tcp", tcpAddr)
fmt.Println("Server starts listening on port", localConn.port)
checkError(err)
for {
fmt.Println("Accepting a new connection....")
conn, err := l.Accept()
checkError(err)
handleConnection(conn)
// responseConnection(conn)
}
}
client.go
package main
import (
"bytes"
"fmt"
"io"
"log"
"net"
"os"
)
func checkError(err error) {
if err != nil {
log.Fatalln(err)
}
}
var buf bytes.Buffer
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0])
os.Exit(1)
}
service := os.Args[1]
tcpAddr, err := net.ResolveTCPAddr("tcp", service)
checkError(err)
conn, err := net.DialTCP("tcp", nil, tcpAddr)
checkError(err)
message := "Hello world from client"
conn.Write([]byte(message))
io.Copy(&buf, conn)
buf.Cap()
fmt.Printf("Data from server =======>: %s\n Buffer length: %d\n", buf.String(), buf.Len())
defer conn.Close()
}
I think your problem is quite the standard one: not paying attention to the fact TCP does not implement message boundaries and merely transports two opaque streams of bytes over a connection.
This means, when you send a string of bytes "Hello world from client" to a connected socket (an established TCP connection) the other end of the connection have no idea where the client's message ends unless the client somehow conveys that itself; there simply are no means to demarcate individual messages using TCP itself.
Enter "application-level protocols": unless the data exchange task for which you intend to use TCP naturally transfer a single "message" — imagine a server which dumps the contents of a single file into each connected client and closes the connection — you have to invent some way for the client to tell the server where each of the messages it sends actually ends.
Consider your example: in the reading procedure you basically have a loop wihch repeatedly reads chunks of data from a socket, with a single exit condition: reaching end-of-file on that socket. A EOF is only reported when the remote side — the client in our case — closes its side of the conection, and the client never does that: it sends a string and then waits for the sever to send something back, but the server never replies because it never finishes reading.
There are multiple ways to solve the problem.
Invent a custom protocol (say, in the TLV family) which would implement message framing.
Say, in its simplest form, the protocol could be defined as a single unsigned byte containing the length of the following message, in bytes.
The server would then have a two-step process for reading each of the client's messages:
Come up with a message delimiter such as ASCII LF character — wich can be encoded as \n
in Go's string literals, — and make the server continue reading until it encounters an LF; once it has spotted an LF, it knows it should process the message then start reading another.
Go has a convenient type bufio.Reader
right in its standard package which can read from any io.Reader
individual lines separated by LFs.
Have a more involved message framing such as sending JSON documents using JSON streaming.
An oft-overseen feature of the decoder implemented by the stock encoding/json
package is that it's fine with decoding streams of JSON objects, consider this as an example.
The possibilities are actually many, so I were just scratching the surface, and I think you should get the idea.