Why does net.Conn.close() seem to be closing at the wrong time? - go

I'm trying to read and write some commands from a TCP client. I want to close a connection after the last function has been executed but for some reason, it seems like the server disconnects the connection in the middle of the function even when explicitly placed afterward.
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"strconv"
"strings"
"time"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
}
go handleConn(conn)
conn.Close()
}
}
func handleConn(someconnection net.Conn) {
func1(someconnection)
func2(someconnection) //connection drops in the middle of executing this part
}
func func2(someconnection net.Conn) {
//send message(a string)
_, err := io.WriteString(someconnection, dosomething)
if err != nil {
log.Fatal(err)
}
//await reply
//send another message
_, err = io.WriteString(someconnection, dosomething)
if err != nil {
log.Fatal(err)
}
//await reply
//send another message, connection tends to close somewhere here
_, err = io.WriteString(someconnection, dosomething)
if err != nil {
log.Fatal(err)
}
//await,send
_, err = io.WriteString(someconnection, do something)
if err != nil {
log.Fatal(err)
}
//await, read and print message
c := bufio.NewReader(someconnection)
buff1 := make([]byte, maxclientmessagelength)
buff1, err = c.ReadBytes(delimiter)
fmt.Printf("\n%s\n", buff1)
_, err = io.WriteString(someconnection, dosomething)
if err != nil {
log.Fatal(err)
}
}
That means the client trying to communicate backward simply isn't able to communicate but the program runs to the end.
Update 1:
Made some progress by placing the deferred close statement to when the connection was first acquired.
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
}
defer conn.Close()
go handleConn(conn)
}}
Now it doesn't necessarily close within the second I hope it to close but at least it now runs all the way through.

Goroutines are asynchronous so after calling handleConn here:
go handleConn(conn)
conn.Close()
the main function continues to execute and closes the connection.
Try just calling the handleConn function regularly (without the go).

The conn.Close needs to de done AFTER handleConn has done its work. You could communicate the back to the main thread using channels but that would be too complex (and also block execution of main thread). This is how it should be done
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
}
go handleConn(conn)
// REMOVE BELOW LINE
// conn.Close()
}
}
Add conn.Close inside handleConn
func handleConn(someconnection net.Conn) {
// ADD BELOW LINE
defer someconnection.Close()
func1(someconnection)
func2(someconnection)
}
This makes sure conn.Close is called AFTER func1 and func2 are done executing

Related

How do I make it such that a TCP connection will timeout if the connection doesn't receive a response every second?

I'm trying to create a TCP server that will timeout if the client does not respond within the span of every second.
I tried:
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
conn.SetDeadline(time.Now().Add(timeout))
if err != nil {
log.Print(err)
}
go handleConn(conn)
}
}
where the timeout is a single second but the disconnects immediately, not even waiting for a reply.
What you want can be achieved by setting socket options on your listener. Tweak the values as per your needs
Note that this is its own KeepAlive and does not depend on incoming/outgoing data by application
func enableTCPKeepAlive(listener *net.TCPListener) error {
rawConn, err := listener.SyscallConn()
if err != nil {
return err
}
cfg := config.TLSServerConfig()
rawConn.Control(
func(fdPtr uintptr) {
// got socket file descriptor. Setting parameters.
fd := int(fdPtr)
//Idle time before sending probe.
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, cfg.TCPKAIdleTime)
if err != nil {
return err
}
//Number of probes.
err := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, cfg.TCPKANumProbes)
if err != nil {
return err
}
//Wait time after an unsuccessful probe.
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, cfg.TCPKAInterval)
if err != nil {
return err
}
// go syscall doesn't have the constant 0x12 (18) for TCP_USER_TIMEOUT.
// 0x12 value referenced from linux kernel source code header:
// include/uapi/linux/tcp.h
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, 0x12, cfg.TCPKAUserTimeout)
if err != nil {
return err
}
})
return nil
}
There are more options available than the ones I have mentioned above.
Call this function on your listener before the for loop.
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
err = enableTCPKeepAlive(listener)
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
conn.SetDeadline(time.Now().Add(timeout))
if err != nil {
log.Print(err)
}
go handleConn(conn)
}
}
The problem is almost always in code that is not posted here. The function obviously works like a charme:
package main
import (
"crypto/rand"
"log"
"net"
"time"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go func() {
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
return
}
go func(c net.Conn) {
defer c.Close()
conn.SetDeadline(time.Now().Add(2 * time.Second))
if err != nil {
log.Print(err)
return
}
buf := make([]byte, 1<<19) // 512 KB
for {
_, err := conn.Read(buf)
if err != nil {
log.Print(err)
break
}
}
}(conn)
}
}()
payload := make([]byte, 1<<20)
_, err = rand.Read(payload) // generate a random payload
if err != nil {
log.Print(err)
}
conn, err := net.Dial("tcp", listener.Addr().String())
if err != nil {
log.Fatal(err)
}
log.Println("Connected to server.")
time.Sleep(5 * time.Second)
_, err = conn.Write(payload)
if err != nil {
log.Print(err)
}
listener.Close()
}

Sending binaries or strings by a client socket

I'm studying networks, and I'm doing a tcp server with Go. One of the challenges I'm studying is to send binaries or strings by a socket client to a server, save the server response to a txt, and compare it to the original data that was sent.
The problem is that the binaries do not arrive completely on the server.
Server
package main
import (
"fmt"
"io"
"log"
"net"
)
func main() {
l, err := net.Listen("tcp", ":8000")
if nil != err {
log.Println(err)
}
defer l.Close()
for {
conn, err := l.Accept()
if nil != err {
log.Println(err)
continue
}
defer conn.Close()
go ConnHandler(conn)
}
}
func ConnHandler(conn net.Conn) {
recvBuf := make([]byte, 4096)
for {
n, err := conn.Read(recvBuf)
if nil != err {
if io.EOF == err {
log.Println(err)
return
}
log.Println(err)
return
}
if 0 < n {
data := recvBuf[:n]
fmt.Println(string(data))
}
}
}
Client
package main
import (
"fmt"
"log"
"net"
)
func main() {
conn, err := net.Dial("tcp", ":8000")
if nil != err {
log.Println(err)
}
var s string
fmt.Scanln(&s)
conn.Write([]byte(s))
conn.Close()
}
I'm generating the binaries using the command on linux:
head -c100000 /dev/urandom > binary_message.txt
I run the server:
./server > result.txt
And I send this data by the client using:
./client < binary_data.txt
In the end the file binary_data.txt have 98KB but the result .txt only has 0KB.
The problem is with scanning the binary from input. You didn't see it because the errors were ignored and not printed or otherwise handled. fmt.Scanln returns an error (so does the Write function). You should always check for possible errors happening.
I rewrote the client to load the file from disk itself as I don't think using stdin is a good fit for binary data.
package main
import (
"flag"
"io"
"log"
"net"
"os"
)
var fileName = flag.String("file", "", "file to send")
func main() {
flag.Parse()
conn, err := net.Dial("tcp", ":8000")
if nil != err {
log.Println(err)
}
defer conn.Close()
f, err := os.Open(*fileName)
if err != nil {
log.Println(err)
return
}
defer f.Close()
b := make([]byte, 1024)
for {
n, err := f.Read(b)
if err != nil {
if err == io.EOF {
log.Println("Done sending")
return
}
log.Println(err)
return
}
if _, err := conn.Write(b[:n]); err != nil {
log.Println(err)
return
}
}
}
You can use it with:
go run . -file=binary_message.txt
or if you have built the binary:
./client -file=binary_message.txt
I suggest you do the same for the server. Open a file for writing and write the binary data into that file. Use a flag to pass in the filename to write to. That will be cleaner than piping stdout to a file.

Simple server client communication not working

This seemingly simple example is not working as expected and I feel bad for asking but here goes:
There's a client that retries connecting to the server, sends a message, and then waits for a response:
func client() {
var conn net.Conn
var err error
// retry server until it is up
for {
conn, err = net.Dial("tcp", ":8081")
if err == nil {
break
}
log.Println(err)
time.Sleep(time.Second)
}
// write to server
_, err = conn.Write([]byte("request"))
if err != nil {
log.Println(err)
return
}
// block & read from server
var buf []byte
n, err := conn.Read(buf)
if err != nil {
log.Println(err)
return
}
log.Printf("From server: %s\n", buf[:n])
}
It connects to a server which for each connection, reads and interprets the sent data, and sends a response if needed:
func server() {
ln, _ := net.Listen("tcp", ":8081")
for {
conn, _ := ln.Accept()
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
var buf []byte
n, err := conn.Read(buf)
if err != nil {
return
}
log.Printf("Server got: %s\n", buf)
if string(buf[:n]) == "request" {
_, _ = conn.Write([]byte("response"))
}
}
All driven by the main function:
func main() {
go client()
server()
}
Error handling is omitted for brevity. The expected behavior is that the client will connect to the server and send the message "request" and then block on the read. The server receives "request" and sends the message "response" back to the same connection. The client unblock, prints the received message and exits. Instead, when the program is run, the following is printed:
2019/09/01 22:24:02 From server:
2019/09/01 22:24:02 Server got:
Suggesting that no data was exchanged, and that the client did not block.
The looping in client is strange!
The looping not make sense if read/write is out.
But the error is only this:
//var buf []byte <--- this read 0 bytes
buf := make([]byte, 1024)
n, err := conn.Read(buf)
A proposal for you:
package main
import (
"log"
"net"
"time"
)
func client() {
var conn net.Conn
var err error
// retry server until it is up
for {
log.Printf("Connecting...")
conn, err = net.Dial("tcp", ":8082")
if err != nil {
log.Println(err)
break
}
time.Sleep(time.Second)
// write to server
log.Printf("Writing...")
_, err = conn.Write([]byte("request"))
if err != nil {
log.Println(err)
return
}
// block & read from server
log.Printf("Reading...")
var buf []byte
n, err := conn.Read(buf)
if err != nil {
log.Println(err)
return
}
log.Printf("From server: %s\n", buf[:n])
}
}
func server() {
ln, _ := net.Listen("tcp", ":8082")
for {
conn, _ := ln.Accept()
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return
}
log.Printf("Server got: [%d bytes] %s\n", n, buf)
if string(buf[:n]) == "request" {
_, _ = conn.Write([]byte("response"))
}
conn.Close()
}
func main() {
go client()
server()
}

how to repeat shutting down and establish go routine?

every one,I am new to golang.I wanna get the data from log file generated by my application.cuz roll-back mechanism, I met some problem.For instance,my target log file is chats.log,it will be renamed to chats.log.2018xxx and a new chats.log will be created.so my go routine that read log file will fail to work.
so I need detect the change and shutdown the previous go routine and then establish the new go routine.
I looked for modules that can help me,and I found
func ExampleNewWatcher(fn string, createnoti chan string, wg sync.WaitGroup) {
wg.Add(1)
defer wg.Done()
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event := <-watcher.Events:
if event.Op == fsnotify.Create && event.Name==fn{
createnoti <- "has been created"
}
case err := <-watcher.Errors:
log.Println("error:", err)
}
}
}()
err = watcher.Add("./")
if err != nil {
log.Fatal(err)
}
<-done
}
I use fsnotify to detech the change,and make sure the event of file is my log file,and then send some message to a channel.
this is my worker go routine:
func tailer(fn string,isfollow bool, outchan chan string, done <-chan interface{},wg sync.WaitGroup) error {
wg.Add(1)
defer wg.Done()
_, err := os.Stat(fn)
if err != nil{
panic(err)
}
t, err := tail.TailFile(fn, tail.Config{Follow:isfollow})
if err != nil{
panic(err)
}
defer t.Stop()
for line := range t.Lines{
select{
case outchan <- line.Text:
case <- done:
return nil
}
}
return nil
}
I using tail module to read the log file,and I add a done channel to it to shutdown the cycle(I don't know whether I put it in the right way)
And I will send every log content to a channel to consuming it.
So here is the question:how should I put it together?
ps: Actually,I can use some tool to do this job.like apache-flume,but all of those tools need dependency.
Thank you a lot!
Here is a complete example that reloads and rereads the file as it changes or gets deleted and recreated:
package main
import (
"github.com/fsnotify/fsnotify"
"io/ioutil"
"log"
)
const filename = "myfile.txt"
func ReadFile(filename string) string {
data, err := ioutil.ReadFile(filename)
if err != nil {
log.Println(err)
}
return string(data)
}
func main() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
err = watcher.Add("./")
if err != nil {
log.Fatal(err)
}
for {
select {
case event := <-watcher.Events:
if event.Op == fsnotify.Create && event.Name == filename {
log.Println(ReadFile(filename))
}
case err := <-watcher.Errors:
log.Println("error:", err)
}
}
}
Note this doesn't require goroutines, channels or a WaitGroup. Better to keep things simple and reserve those for when they're actually needed.

Golang TCPConn Gob Communication

I'm having issues with the gob protocol (or maybe networking in general, where my knowledge is weak), and I don't understand why the following code does not work properly. It is just supposed to be a simple example of maintaining an open TCP connection, and sending multiple gobs through it. The code will send, and receive, but often corrupts its data.
Thank you in advance.
package main
import (
"encoding/gob"
"fmt"
"net"
"strconv"
"time"
)
type Message struct {
Msg string
}
func main() {
gob.Register(new(Message))
clientAddr, err := net.ResolveTCPAddr("tcp", "localhost:12346")
if err != nil {
fmt.Println(err)
}
serverAddr, err := net.ResolveTCPAddr("tcp", "localhost:12345")
if err != nil {
fmt.Println(err)
}
serverListener, err := net.ListenTCP("tcp", serverAddr)
if err != nil {
fmt.Println(err)
}
conn, err := net.DialTCP("tcp", clientAddr, serverAddr)
if err != nil {
fmt.Println(err)
}
serverConn, err := serverListener.AcceptTCP()
if err != nil {
fmt.Println(err)
}
done := false
go func() {
for !done {
recieveMessage(serverConn)
}
}()
for i := 1; i < 1000; i++ {
sent := Message{strconv.Itoa(i)}
sendMessage(sent, conn)
}
time.Sleep(time.Second)
done = true
}
func sendMessage(msg Message, conn *net.TCPConn) {
enc := gob.NewEncoder(conn)
err := enc.Encode(msg)
if err != nil {
fmt.Println(err)
}
}
func recieveMessage(conn *net.TCPConn) {
msg := new(Message)
dec := gob.NewDecoder(conn) // Will read from network.
err := dec.Decode(msg)
if err != nil {
fmt.Println(err)
}
fmt.Println("Client recieved:", msg.Msg)
}
The problem is that the decoder can buffer data from the next message. When this happens, the next new decoder starts in the middle of a message. The fix is to use a single encoder and decoder.
func main() {
...
dec := gob.NewDecoder(conn) // Will read from network.
enc := gob.NewEncoder(serverConn)
go func() {
for !done {
recieveMessage(dec)
}
}()
for i := 1; i < 1000; i++ {
sent := Message{strconv.Itoa(i)}
sendMessage(sent, enc)
}
...
}
func sendMessage(msg Message, enc *gob.Encoder) {
err := enc.Encode(msg)
if err != nil {
fmt.Println(err)
}
}
func recieveMessage(dec *gob.Decoder) {
msg := new(Message)
err := dec.Decode(msg)
if err != nil {
fmt.Println(err)
}
fmt.Println("Client recieved:", msg.Msg)
}
Run it in the playground

Resources