RPC using UDP in GO - go

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.

Related

Make reverse TCP connection accept any amount of connections (like a normal TCP server)

I'm trying to create a reverse proxy to a CONNECT-based HTTP proxy. The user who wants to use the proxy just treats machine A as an HTTP proxy. It works the following way:
machine B opens a TCP socket to machine A.
On machine A, a TCP socket is exposed on a port and all the incoming data is tunneled to machine B (io.Copy).
On machine B, all the data is tunneled to the local HTTP server and the socket to machine A.
Essentially this is a reverse-proxy behind an HTTP proxy. The reason it's this complex is because the HTTP proxy is behind NAT (on machine B) and therefore not accessible directly. The use case is being able to host an HTTP proxy behind a NAT.
Machine A tunnel (Go):
package main
import (
"log"
"net"
)
func Conn(c *net.TCPConn) string {
return c.RemoteAddr().String() + " (" + c.LocalAddr().String() + ")"
}
func ProxifyConns(recipientConn, donorConn *net.TCPConn) {
log.Println("Proxying", ConnrecipientConn), "and", Conn(donorConn))
go func() {
_, err := io.Copy(recipientConn, donorConn)
if err != nil {
utils.StacktraceError(err)
}
recipientConn.Close()
}()
go func() {
_, err := io.Copy(donorConn, recipientConn)
if err != nil {
utils.StacktraceError(err)
}
recipientConn.Close()
}()
}
func main() {
// Open the donor listener
donorsAddr, err := net.ResolveTCPAddr("tcp4", ":11000")
if err != nil {
utils.StacktraceErrorAndExit(err)
}
listenerDonors, err := net.ListenTCP("tcp", donorsAddr)
if err != nil {
utils.StacktraceErrorAndExit(err)
}
defer listenerDonors.Close()
log.Println("Listening for donors on", listenerDonors.Addr())
// Open the recipient listener
recipientsAddr, err := net.ResolveTCPAddr("tcp4", ":10000")
if err != nil {
utils.StacktraceErrorAndExit(err)
}
listenerRecipients, err := net.ListenTCP("tcp", recipientsAddr)
if err != nil {
utils.StacktraceErrorAndExit(err)
}
defer listenerRecipients.Close()
log.Println("Listening for recipients on", listenerRecipients.Addr())
// Handle donor connections
donorConns := make(chan *net.TCPConn)
go func() {
for {
donorConn, err := listenerDonors.AcceptTCP()
donorConn.SetKeepAlive(true)
if err != nil {
utils.StacktraceErrorAndExit(err)
return
}
log.Println("New donor connection from", Conn(donorConn))
donorConns <- donorConn
}
}()
// Handle recipient connections
for {
recipientConn, err := listenerRecipients.AcceptTCP()
recipientConn.SetKeepAlive(true)
if err != nil {
utils.StacktraceErrorAndExit(err)
return
}
log.Println("New recipient connection from", Conn(recipientConn))
donorConn := <-donorConns
proxy.ProxifyConns(recipientConn, donorConn)
}
}
Machine B tunnel (Node.js):
import net, { AddressInfo } from 'net';
import http from 'http';
import golgi from 'golgi';
export const startHttpProxy = () => {
const server = http.createServer();
let proxyServer: http.Server = golgi(server);
// Listening to 0 assigns a random OS-assigned port
proxyServer = proxyServer.listen(0);
return proxyServer;
};
export const startDonorSocket = () => {
const proxyServer = startHttpProxy();
const proxyServerSocket = new net.Socket();
proxyServerSocket.connect(
(proxyServer.address() as AddressInfo).port,
'127.0.0.1'
);
const donorSocket = new net.Socket();
donorSocket.setKeepAlive(true);
donorSocket.connect(11000, '2.226.102.14', () => {
proxyServerSocket.pipe(donorSocket);
donorSocket.pipe(proxyServerSocket);
});
};
Unfortunately this works when tunneling to one TCP address but not when tunneling to more. If I open many Machine B tunnels (Node.js code), it works. What I mean is that a donor connection (Node.js) is "consumed" ever time it is taken by a recipient (HTTP proxy user) because a persistent TCP tunnel is made on it.
I wonder is there is a way to make this work for any amount of TCP connections, not just one. My only idea right now is to create more TCP donor connections every time a connection is consumed but I wonder if there is a simpler solution.
When you do
go func() {
for {
donorConn, err := listenerDonors.AcceptTCP()
donorConn.SetKeepAlive(true)
if err != nil {
utils.StacktraceErrorAndExit(err)
return
}
log.Println("New donor connection from", Conn(donorConn))
donorConns <- donorConn
}
}()
You start processing the first TCP connection. This code blocks on donorConns <- donorConn. Until this send to channel finishes the loop won't go into the second iteration (and the next TCP connection won't be accepted).
You do a very similar second loop
// Handle recipient connections
for {
recipientConn, err := listenerRecipients.AcceptTCP()
recipientConn.SetKeepAlive(true)
if err != nil {
utils.StacktraceErrorAndExit(err)
return
}
log.Println("New recipient connection from", Conn(recipientConn))
donorConn := <-donorConns
proxy.ProxifyConns(recipientConn, donorConn)
}
which requires donorConn := <-donorConns to complete (from the first loop) and requires proxy.ProxifyConns(recipientConn, donorConn) to complete.
I'm not sure how you intend the whole thing to work, but, most likely, you need a very minor change:
go proxy.ProxifyConns(recipientConn, donorConn)

Emulate net connection without entering net dial

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

How to make net.Dial in Go reconnect if connection is lost?

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.

Golang: Mixing Gin with an UDP server

I'm trying to use both a UDP server to listen continuously to datagrams and a http server, but the string "UDP server up and listening on port..." and command "server.Run()" are never reached.
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net"
)
func handleUDPConnection(conn *net.UDPConn) {
buffer := make([]byte, 8096)
n, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
log.Fatal(err)
} else {
fmt.Println("UDP client: ", addr)
fmt.Println("Received from UDP client: ", string(buffer[:n]))
}
}
func main() {
server := gin.Default()
host, port := "localhost", "41234"
udpAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%s", host, port))
if err != nil {
log.Fatal(err)
}
conn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
server.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "pong"})
})
for {
handleUDPConnection(conn)
}
fmt.Sprintf("UDP server up and listening on port %s\n", port)
server.Run()
}
How can I make it work?
There is an infinite loop in your code.
for {
handleUDPConnection(conn)
}
This will repetedly call the handleUDPConnection function until the program exits without ever moving on to
fmt.Sprintf("UDP server up and listening on port %s\n", port)
server.Run()
Perhaps you want to deal with the connections in a go thread. This would be something more like this:
//define an exit variable
keepListening := true
//spawn a go routine (starts the function on another thread*)
go func() {
for keepListening {
handleUDPConnection(conn)
}
}()
//notify the user that the server is listening
fmt.Sprintf("UDP server up and listening on port %s\n", port)
//run the server (I assume this function call is blocking
server.Run()
//stop the go routine when the server is done running
keepListening = false
Hope this helps!
*a goroutine is not a thread. It can be useful/simple to think of it like that, but they are distinctly different. Here's an article explaining some of the differences and advantages.

Golang Server close the connection of the client : websocket

i have a problem with my golang server in which i'm using websockets.
The server opens the connection and the client could connect to it, but the problem is that when the server starts sending the data to the client, the client connection is closed after a small period of time. i suggest that the problem is with the server and not with the client because i tried to connect to the server with another web client, and it's the same issue. I didn't understand the cause ! Can someone help me?
server.go:
func Echo(ws *websocket.Conn) {
fmt.Println("Echoing")
for {
msg := MessageReceived{Name: "OrderCommand", Nbmsg: 3}
if err := websocket.JSON.Send(ws, msg); err != nil {
fmt.Println("Can't send")
break
}
//os.Exit(0)
}
}
func checkError(err error) {
if err != nil {
Log("Fatal error ", err.Error())
os.Exit(1)
}
}
func main() {
http.HandleFunc("/save", saveHandler)
http.Handle("/", websocket.Handler(Echo))
err:= http.ListenAndServe(":8081", nil)
checkError(err)
}
and client.go:
import (
"code.google.com/p/go.net/websocket"
"fmt"
"log"
)
func main() {
origin := "http://localhost/"
url := "ws://localhost:8081/echo"
ws, err := websocket.Dial(url, "", origin)
if err != nil {
log.Fatal(err)
}
var msg = make([]byte, 512)
var n int
if n, err = ws.Read(msg); err != nil {
log.Fatal(err)
}
fmt.Printf("Received: %s.\n", msg[:n])
}
Your problem, as others have pointed out, is that you must receive a message as well.
Currently, when someone connects, your program will step into the for-loop and start bombarding the client with messages. Probably not the intended behaviour of an echo server.
First you want to Receive a message, then Send a reply:
func Echo(ws *websocket.Conn) {
fmt.Println("Echoing")
msg := new(MessageReceived)
for {
// The server blocks here until a message from the client is received
websocket.JSON.Receive(ws, &msg)
fmt.Printf("Received message: %+v\n", msg)
// Reencode the same message and send it back
if err := websocket.JSON.Send(ws, msg); err != nil {
fmt.Println("Can't send echo")
break
}
}
}
A full working version can be found at Playground: http://play.golang.org/p/nQ3fJ5Nb0I
Since it uses websockets, you must compile it on your local computer.
Why using ws.Read when you can use websocket.JSON.Receive to deserialize the message?
Here are the server: http://play.golang.org/p/NZ6VJ4daGm
and the client: http://play.golang.org/p/rkJVKGhrGk (that I have changed to receive 10 messages before exiting).
The string "Can't send" will be printed by the server once the client closes the websocket connection.

Resources