I have an app in Go that's connecting to XMPP host using tcp and then xml Decoder to talk XMPP. How can I make net.Dial reconnect if tcp connection is dropped?
I am getting the following error on my error channel when the connection is dropped:
write tcp client:port->xmpp_server:5222: write: broken pipe. However I'm not sure how to properly handle it in my Dial function to make it reconnect.
// package xmpp
// Conn represents a connection
type Conn struct {
incoming *xml.Decoder
outgoing net.Conn
errchan chan error
}
// SetErrorChannel sets the channel for handling errors
func (c *Conn) SetErrorChannel(channel chan error) {
c.errchan = channel
}
// Dial dials an xmpp host
func Dial(host string) (*Conn, error) {
c := new(Conn)
var err error
c.outgoing, err = net.Dial("tcp", host+":5222")
if err != nil {
log.Printf("Can't dial %s:5222: %s", host, err)
return c, err
}
// TCP Keep Alive
err = c.outgoing.(*net.TCPConn).SetKeepAlive(true)
if err != nil {
c.errchan <- err
}
err = c.outgoing.(*net.TCPConn).SetKeepAlivePeriod(30 * time.Second)
if err != nil {
c.errchan <- err
}
c.incoming = xml.NewDecoder(c.outgoing)
log.Printf("Connected to: %s", c.outgoing.RemoteAddr())
return c, nil
}
// In a separate package
func NewXMPPClient(config) (*Client, error) {
errchannel := make(chan error)
connection, err := xmpp.Dial(host)
if err != nil {
return nil, err
}
connection.SetErrorChannel(errchannel)
// Do XMPP auth, receive messages, etc...
Figured it out. I just started to close the current tcp connection on any error in my error channel and re-create both TCP and XMPP (auth+listen) connections.
Related
this is my code
Firstly I init a tcp server
type server struct {
serverConns chan net.Conn
addr string
}
func (s *server) init() error {
s.serverConns = make(chan net.Conn)
l, err := net.Listen(network, ":8888")
if err != nil {
return err
}
s.addr = l.Addr().String()
fmt.Println(s.addr)
go func() {
for {
conn, err := l.Accept()
if err != nil {
panic(err)
}
s.serverConns <- conn
}
}()
return nil
}
Then I create a tcp connection, and close connection from server. CheckConnErr call syscall.Read to read data from connection. In my opinion, after server close connection, client receive fin packet and syscall.read will get EOF. But in this case, syscall.Read get EAGAIN, that means connection is still alive.
func TestRemoteEOF(t *testing.T) {
var s server
require.Nil(t, s.init())
// dial to server
var n net.Conn
// close connection from server
serverConn := <-s.serverConns
require.Nil(t, serverConn.Close())
buf := make([]byte, 100)
hit := time.Now()
err2 := checkConnErr(n, buf)
require.Equal(t, err2, io.EOF)
}
I also check with wireshark, the time of hit is later than fin arrive time, so what is the cause of this phenomenon?
enter image description here
enter image description here
I am learning from the book An Introduction to Programming in Go by Caleb Doxsey
In chapter 13 about servers we are given the code:
package main
import (
"encoding/gob"
"fmt"
"net"
)
func server() {
// listen on a port
ln, err := net.Listen("tcp", ":9999")
if err != nil {
fmt.Println("server, Listen", err)
return
}
for {
// accept a connection
c, err := ln.Accept()
if err != nil {
fmt.Println("server, Accept", err)
continue
}
// handle the connection
go handleServerConnection(c)
}
}
func handleServerConnection(c net.Conn) {
// receive the message
var msg string
err := gob.NewDecoder(c).Decode(&msg)
if err != nil {
fmt.Println("handleServerConnection", err)
} else {
fmt.Println("Received", msg)
}
c.Close()
}
func client() {
// connect to the server
c, err := net.Dial("tcp", "127.0.0.1:9999")
if err != nil {
fmt.Println("client, Dial", err)
return
}
// send the message
msg := "Hello World"
fmt.Println("Sending", msg)
err = gob.NewEncoder(c).Encode(msg)
if err != nil {
fmt.Println("client, NewEncoder", err)
}
c.Close()
}
func main() {
go server()
go client()
var input string
fmt.Scanln(&input)
}
Running this code I almost always receive:
client, Dial dial tcp 127.0.0.1:9999: connect: connection refused
But sometimes I receive:
Sending Hello World
Received Hello World
I have also discovered if I run just run server separately from client, and then run client on a separate file, it works as intended. Why is that?
Listen and Dial are called concurrently, and you can't predict which one executes first. If Dial executes before Listen then there is obviously nothing listening yet and that produces the error.
Call Listen in main, before starting the goroutines:
func main() {
ln, err := net.Listen("tcp", ":9999")
if err != nil {
fmt.Fatal("server, Listen", err)
}
go server(ln)
go client()
var input string
fmt.Scanln(&input)
}
func server(ln net.Listener) {
for {
// accept a connection
c, err := ln.Accept()
if err != nil {
fmt.Println("server, Accept", err)
continue
}
// handle the connection
go handleServerConnection(c)
}
}
I'm currently attempting to create a TCP service that will just log/store whatever is sent to it. I can't seem to understand why I cannot connect to my localhost using DialTCP. I keep getting
dial tcp 127.0.0.1:8080: connect: connection refused
func main() {
errCh := make(chan error)
tcpAddr, _ := net.ResolveTCPAddr("tcp", "localhost:8080")
for {
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
log.Println("Could not connect -> ", err.Error())
} else {
recordMessage(conn, errCh)
err = <-errCh
log.Println("Error", err)
conn.Close()
}
log.Println("trying again in 10 seconds..")
time.Sleep(30 * time.Second)
}
}
I looked over my Firewall settings and noting seems to be blocking it. I'm still not sure if its due to something related to my Firewall or if I'm just missing something super obvious.
Start by running this Go program in a terminal -- it listens to port 2000 but you could change it to 8080 or whatever you wish:
func main() {
// Listen on TCP port 2000 on all interfaces.
l, err := net.Listen("tcp", ":2000")
if err != nil {
log.Fatal(err)
}
defer l.Close()
for {
// Wait for a connection.
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
// Handle the connection in a new goroutine.
// The loop then returns to accepting, so that
// multiple connections may be served concurrently.
go func(c net.Conn) {
log.Println(c)
// Echo all incoming data.
io.Copy(c, c)
// Shut down the connection.
c.Close()
}(conn)
}
}
Then in a separate terminal run this simple client:
func main() {
var addr string
if len(os.Args) > 1 {
addr = os.Args[1]
} else {
addr = "localhost:2000"
}
conn, err := net.Dial("tcp", addr)
if err != nil {
log.Fatal(err)
// handle error
}
fmt.Fprintf(conn, "foobar")
conn.Close()
}
Asking it to connect to the same port. The connection should succeed and you should see the server logging something.
Now try to connect with your client.
Without writing Go, you could to these things with the nc command-line tool (netcat). nc -lv PORT creates a simple listening server on PORT, for example.
I currently am working on vendor go balancer code. I need to remove the tcp dial call and emulate a successful connection without the call. In the code below, there is
this line:
ds, err := net.Dial("tcp", backend.String());if err != nil {
log.Printf("failed to dial %s: %s", backend, err)
us.Close()
return
}
What this does is make a dial to the tcp server and then return connection
response in ds, which is defined here: https://golang.org/pkg/net/#Dial
What i need is to obtain the ds without doing the tcp dialer. I'm
trying to test the load balancer without any actual tcp calls. So,
essentially,
when we enter handleConnection wed create a net connection prior to the tcp dial and use this net conn, which should emulate 100% net connection before the tcp dialing begins.
func copy(wc io.WriteCloser, r io.Reader) { defer wc.Close()
io.Copy(wc, r)
}
func handleConnection(us net.Conn, backend BA.Backend) {
if backend == nil {
log.Printf("no backend available for connection from %s",
us.RemoteAddr())
us.Close()
return
}
host, _, _ := net.SplitHostPort(us.RemoteAddr().String())
_, ok := dbAuthTokenData[host]; if !ok {
w := bufio.NewWriter(us)
w.WriteString("InvalidCredentials")
w.Flush()
us.Close()
return
}
ds, err := net.Dial("tcp", backend.String());if err != nil {
log.Printf("failed to dial %s: %s", backend, err)
us.Close()
return
}
// Ignore errors
go copy(ds, us)
go copy(us, ds)
}
func tcpBalance(bind string, backends BA.Backends) error {
log.Println("using tcp balancing")
ln, err := net.Listen("tcp", bind)
if err != nil {
return fmt.Errorf("failed to bind: %s", err)
}
log.Printf("listening on %s, balancing %d backends", bind, backends.Len())
for {
conn, err := ln.Accept()
if err != nil {
log.Printf("failed to accept: %s", err)
continue
}
go handleConnection(conn, backends.Choose())
}
return err
}
I tried commenting out go handleConnection(conn, backends.Choose()) but that failed.
The pattern you could refactor your code is to create a Dialer interface. In your code example you are using the returned ds just as an io.ReadWriteCloser. So you don't need to implement the whole net.Conn interface. As net.Conn has the read and write method inside everything works
type Dialer interface{
Dial(network, address string) (io.ReadWriteCloser, error)
}
Now let's change your function:
func handleConnection(us net.Conn, backend BA.Backend, d Dialer) {
// ...
// Code here stays
ds, err := d.Dial("tcp", backend.String());if err != nil {
log.Printf("failed to dial %s: %s", backend, err)
us.Close()
return
}
// ...
}
That your production code works you now need to define a type netDialer which wraps the net.Dial() function. In your test you can use a testDialer which uses a bytes.Buffer.
This answers your question:
Emulate net connection without entering net dial
Unity3D networking libraries use UDP and has methods for RPC calls. I'm trying to get my server to use RPC over UDP and I'm having some trouble. Here's the basic server code I've got now:
type Args struct {
X, Y int
}
type RequestHandler struct{}
func (self *RequestHandler) Add(args *Args, reply *int) error {
*reply = args.X + args.Y
return nil
}
func main() {
addr := net.UDPAddr{ Port: 5127, IP: net.ParseIP("127.0.0.1") }
handler := new(RequestHandler)
rpc.Register(handler)
conn, err := net.ListenUDP("udp", &addr)
defer conn.Close()
if err != nil {
panic(err)
}
for {
go rpc.ServeConn(conn)
}
}
And here is the client code:
type Args struct {
X, Y int
}
func main() {
client, err := rpc.Dial("udp", "127.0.0.1:5127")
if err != nil { log.Fatal("dialing:", err) }
// Synchronous call
args := &Args{7,8}
var reply int
err = client.Call("RequestHandler.Add", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Result: %d + %d = %d", args.X, args.Y, reply)
}
When I run these, they both just hang, nothing happens. What am I doing wrong?
RPC over UDP requires special UDP aware handling due to the nature of UDP sockets.
There is no connection, just datagrams sent to an address.
For the client to get a reply, it would have to set up a listening socket and then send that to the server along with the request. The server would then reply to the clients address.
net/rpc doesn't have any special case handling for non-connection oriented transports (ie: UDP)
I don't know of any packages that implement connection-less RPC for go, so you may have to roll your own here.