Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A server that also acts as a client #345

Open
KevinRoebert opened this issue Mar 13, 2022 · 7 comments
Open

A server that also acts as a client #345

KevinRoebert opened this issue Mar 13, 2022 · 7 comments
Assignees
Labels
help wanted Extra attention is needed question Further information is requested

Comments

@KevinRoebert
Copy link

Please fill out the following system information before opening an issue:

  • OS (e.g. Ubuntu 18.04): MacOS Monterey 12.2.1
  • Go version (e.g. Go 1.13): go1.17.8 darwin/arm64
  • gnet version (e.g. v1.0.0): v2.0.1

What is your question about gnet?
Is there a way in gnet to start a UPD server and send a message to another server from the same FD without the server sending a message before.

An example: There is UDP server A and UDP server B. A and B have never communicated with each other before. A now wants to send a message to B.

How would I do something like this in gnet? Is it possible at all?

It is important that the port of UDP server A does not change when sending the message. So it is not possible to just create a new client, which then sends the message to B.

@KevinRoebert KevinRoebert added help wanted Extra attention is needed question Further information is requested labels Mar 13, 2022
@panjf2000
Copy link
Owner

Couldn't you just create a UDP socket to server B in server A and bind that socket with the port you want?

@KevinRoebert
Copy link
Author

KevinRoebert commented Mar 13, 2022

Couldn't you just create a UDP socket to server B in server A and bind that socket with the port you want?

Maybe, but how? gnet.NewClient() and gnet.Client.Dial() does not allow to bind to the socket of the UDP server A.

Example code:

package main

import (
	"fmt"
	"log"

	"github.com/panjf2000/gnet/v2"
)

type udpServer struct {
	gnet.BuiltinEventEngine
}

func (es *udpServer) OnTraffic(c gnet.Conn) gnet.Action {
	data, _ := c.Next(-1)
	log.Printf("Got message: %s", data)
	
	return gnet.None
}

func (es *udpServer) OnBoot(eng gnet.Engine) gnet.Action {
	// Here I want to send a UDP message to B from the socket of `udpServer`

	return gnet.None
}

func main() {	
	udp := new(udpServer)

	log.Fatal(gnet.Run(udp, fmt.Sprintf("udp://:%d", 9000), gnet.WithMulticore(true), gnet.WithReusePort(false)))
}

@panjf2000
Copy link
Owner

client.Dial() and use the returned gnet.Conn to send data, or you can just use the standard net to create a UDP socket.

@KevinRoebert
Copy link
Author

I think there is still a little misunderstanding. It is not an option to create a new socket. This would mean that the outgoing UDP packets would have a different sender port than the UDP server A.

So I need to send UDP packets from UDP server A to remain addresses. Thus, the receiver can answer the message, and this answer also arrives again at the UDP server A.

@panjf2000
Copy link
Owner

It is not an option to create a new socket.
Is there a way to send network messages without using sockets?

Besides, I don't think you are allowed to reuse an in-use port of an existing server as the source port of sender.

@HustCoderHu
Copy link

u want it working like this ?
ServerC (port m) -- msg --> ServerA (port n) -- relay --> ServerB port(port p) -- rsp --> ServerA (port n)

u need to figure out a way send pkts to different peer from one local Conn object.

Does one udp Conn support customized peer addr on each asyncWrite ?

@civilizeddev
Copy link

@KevinRoebert

I understand what you want. In fact, by using the builtin net package in Go to directly bind a UDP socket, one can read messages that are being listened to, or send them to another target. That might have allowed you to do what you want. However, the reason we use gnet is probably because it's more convenient than implementing directly with the builtin net package.

In gnet, internally, there is a file descriptor for a UDP socket bound to the server's port address in the event loop. In fact, it would be great if the gnet maintainer provided a convenient interface for this.

The workaround I found is as follows.

import (
	"net"
	"os"

	"github.com/panjf2000/gnet"
)

type server struct {
	*gnet.EventServer
	conn *net.PacketConn
}

func (s *server) OnInitComplete(svr gnet.Server) (action gnet.Action) {
	fd, err := svr.DupFd()
	if err != nil {
		log.Fatal(err)
	}
	file := os.NewFile(uintptr(fd), "udp")
	conn, err := net.FilePacketConn(file)
	if err != nil {
		log.Fatal(err)
	}
        // net.UDPConn, actually
	s.conn = &conn
	
	log.Printf("Listening %s://%s", svr.Addr.Network(), svr.Addr)
	return
}

...

And then it can be used elsewhere.

...

func (s *server) SendTo(p []byte, addr net.Addr) error {
	if _, err := (*d.conn).WriteTo(p, addr); err != nil {
		return err
	}
	return nil
}

In fact, because I am in a bit of a hurry, I had to make it work somehow. For the time being, I will use it this way, but as I mentioned before, I hope that gnet can make it more elegantly.

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants