I'm trying to implement non-blocking Accept()
and the best I've come so far is the following code snippet (it's a working Go v1.6.2 program):
package main
import (
"net"
"log"
"time"
)
func createClient() {
tcpConn, err := net.DialTCP("tcp4", nil, &net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 12819,
})
if err != nil {
log.Fatalln("Error connecting to the server!")
}
log.Println("Managed to dial!")
tcpConn.Close()
}
func main() {
go createClient()
l, err := net.ListenTCP("tcp4", &net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 12819,
})
if err != nil {
log.Fatalln("Can't listen on provided IP/port!")
}
defer l.Close()
if err = l.SetDeadline(time.Now().Add(time.Nanosecond)); err != nil {
log.Fatalln("Can't set appropriate deadline!")
}
tcpConn, err := l.AcceptTCP()
if err != nil {
if opError, ok := err.(*net.OpError); ok && opError.Timeout() {
log.Fatalln("Timeout error!")
}
log.Fatalln("Error while accepting connection!")
}
log.Println("Accepted new connection!")
tcpConn.Close()
}
The problem is that I always get Timeout error!
. As far as I understand that's because by the time listener's AcceptTCP()
gets called the deadline previously set will have already expired. Try changing it to time.Microsecond
and you'd probably get the same result (unless you have CPU slower than mine). Things start to change only when the deadline gets at least time.Second
. That's when I start to get Accepted new connection!
/Managed to dial!
.
So I think I nailed the problem. Any thoughts?
Accept is supposed to be blocking. You do not want to set deadline on listen / Accept, as you want it to permanently listen to new connections.
You need to put the accept inside a loop and basically start a new go routine to process the connexion and return to waiting to new client connexion (Accept).
Here is a typical server in Go:
package main
import (
"fmt"
"log"
"net"
"os"
)
func main() {
l, err := net.Listen("tcp", "localhost:12819")
if err != nil {
log.Fatalln("Can't listen on provided IP/port!")
}
defer l.Close()
for {
// Listen for an incoming connection.
conn, err := l.Accept()
if err != nil {
fmt.Println("Error accepting: ", err.Error())
os.Exit(1)
}
// Handle connections in a new goroutine.
go handleRequest(conn)
}
}
func handleRequest(conn net.Conn) {
buf := make([]byte, 1024)
if _, err := conn.Read(buf); err != nil {
fmt.Println("Error reading:", err.Error())
}
conn.Write([]byte("Message received."))
conn.Close()
}
You can connect with it with the following command from your shell:
telnet localhost 12819
Type a command and your request will be handled and processed.
As you see, the accept command is intended to be blocking on purpose. SetDeadline on server is generally used further done in the processing phase to timeout on read on data coming from client.