本帖最后由 xmdhs 于 2022-6-29 16:30 编辑
获取和增加局域网服务器列表
众所周知,如果有局域网内有了对局域网开放的游戏,就会显示在多人游戏菜单中,服务器列表里的最底部。
原理
这种点了对局域网开放,就能让局域网内所有其他客户端都能看到的技术,是用到了名为组播的东西。通过加入组播组,所有发向这个组播地址的消息,都会接收到。这样就能做到自动发现对局域网开放的游戏。
而通过从组中监听,和向组发送信息,就能做到不少有趣的事情。
比如说,自动的获取游戏对局域网开放的端口,然后自动的进行端口映射。亦或是向服务器列表中自动的添加内容。
接收
minecraft 对局域网开放后,会每隔 1.5 秒向组播地址 224.0.2.60:4445 发送数据包,数据包内容为 utf-8 格式的字符串,内容如下
复制代码
通过解析这个数据包,即可获知对局域网开放的游戏。
具体需要做的,是加入组播,然后接收消息,具体需要做的不同的编程语言也不同,可以自行搜索,这里给出 Go 的示例代码。
相关的代码示例
复制代码
二进制
windows
server.zip
(741.36 KB, 下载次数: 7)
运行后,打开游戏,进入存档,点对局域网开放,就能在这个程序中看到相应的内容了。
发送
如果需要给服务器列表添加内容,需要做的事就简单不少,只需要向 224.0.2.60:4445 发送 udp 数据包即可,内容和上面提到的一样。
演示代码
复制代码
二进制
windows
client.zip
(696.73 KB, 下载次数: 2)
运行后,打开游戏,点击多人联机,底部就会有一堆虚假的局域网服务器了。
获取和增加局域网服务器列表
众所周知,如果有局域网内有了对局域网开放的游戏,就会显示在多人游戏菜单中,服务器列表里的最底部。

原理
这种点了对局域网开放,就能让局域网内所有其他客户端都能看到的技术,是用到了名为组播的东西。通过加入组播组,所有发向这个组播地址的消息,都会接收到。这样就能做到自动发现对局域网开放的游戏。
而通过从组中监听,和向组发送信息,就能做到不少有趣的事情。
比如说,自动的获取游戏对局域网开放的端口,然后自动的进行端口映射。亦或是向服务器列表中自动的添加内容。
接收
minecraft 对局域网开放后,会每隔 1.5 秒向组播地址 224.0.2.60:4445 发送数据包,数据包内容为 utf-8 格式的字符串,内容如下
- [MOTD]motd[/MOTD][AD]port[/AD]
- 例子:
- [MOTD]新的世界[/MOTD][AD]6432[/AD]
通过解析这个数据包,即可获知对局域网开放的游戏。
具体需要做的,是加入组播,然后接收消息,具体需要做的不同的编程语言也不同,可以自行搜索,这里给出 Go 的示例代码。
相关的代码示例
- package main
- import (
- "context"
- "errors"
- "fmt"
- "net"
- "golang.org/x/net/ipv4"
- )
- func main() {
- err := Listen(context.TODO(), "224.0.2.60:4445", "0.0.0.0", func(u *net.UDPAddr, b []byte) {
- fmt.Println(string(b), u.String())
- })
- if err != nil {
- panic(err)
- }
- }
- const maxDatagramSize = 8024
- func Listen(cxt context.Context, address, laddress string, handler func(*net.UDPAddr, []byte)) error {
- msgCh := make(chan readMsg, 10)
- errCh := make(chan error, 10)
- addr, err := net.ResolveUDPAddr("udp4", address)
- if err != nil {
- return fmt.Errorf("Listen: %w", err)
- }
- il, err := net.Interfaces()
- if err != nil {
- return fmt.Errorf("Listen: %w", err)
- }
- if laddress != "0.0.0.0" {
- lip := net.ParseIP(laddress)
- var itf *net.Interface
- B:
- for _, v := range il {
- addr, _ := v.Addrs()
- for _, vv := range addr {
- if ipnet, ok := vv.(*net.IPNet); ok && ipnet.IP.Equal(lip) {
- itf = &v
- break B
- }
- }
- }
- if itf == nil {
- return fmt.Errorf("Listen: %w", ErrNotItf)
- }
- il = []net.Interface{*itf}
- }
- cxt, cancel := context.WithCancel(cxt)
- defer cancel()
- for _, v := range il {
- if v.Flags&net.FlagMulticast != net.FlagMulticast || v.Flags&net.FlagUp != net.FlagUp {
- continue
- }
- v := v
- go read(cxt, &v, addr, errCh, msgCh)
- }
- for {
- select {
- case err := <-errCh:
- return fmt.Errorf("Listen: %w", err)
- case msg := <-msgCh:
- handler(msg.addr, msg.msg)
- case <-cxt.Done():
- return nil
- }
- }
- }
- var ErrNotItf = errors.New("not find interface")
- func read(cxt context.Context, itf *net.Interface, addr *net.UDPAddr, eCh chan<- error, msgCh chan<- readMsg) {
- cxt, cancel := context.WithCancel(cxt)
- defer cancel()
- conn, err := net.ListenMulticastUDP("udp", itf, addr)
- doErr := func(err error) {
- select {
- case <-cxt.Done():
- case eCh <- fmt.Errorf("read: %w", err):
- }
- }
- if err != nil {
- doErr(err)
- return
- }
- defer conn.Close()
- go func() {
- <-cxt.Done()
- conn.Close()
- }()
- conn.SetReadBuffer(maxDatagramSize)
- pc := ipv4.NewPacketConn(conn)
- if err := pc.SetMulticastLoopback(true); err != nil {
- doErr(err)
- return
- }
- for {
- buffer := make([]byte, maxDatagramSize)
- numBytes, src, err := conn.ReadFromUDP(buffer)
- if err != nil {
- doErr(err)
- return
- }
- select {
- case msgCh <- readMsg{
- addr: src,
- msg: buffer[:numBytes],
- }:
- case <-cxt.Done():
- return
- }
- }
- }
- type readMsg struct {
- addr *net.UDPAddr
- msg []byte
- }
二进制
windows

运行后,打开游戏,进入存档,点对局域网开放,就能在这个程序中看到相应的内容了。
发送
如果需要给服务器列表添加内容,需要做的事就简单不少,只需要向 224.0.2.60:4445 发送 udp 数据包即可,内容和上面提到的一样。
演示代码
- package main
- import (
- "fmt"
- "math/rand"
- "net"
- "os"
- "strconv"
- "sync"
- "time"
- )
- func main() {
- for i := 0; i < 100; i++ {
- port := r.Intn(9999) + 1
- go sendPing(randStr(10, []byte(atext)), strconv.Itoa(port))
- }
- os.Stdin.Read(make([]byte, 1))
- }
- type arand struct {
- *rand.Rand
- *sync.Mutex
- }
- var r = arand{
- Rand: rand.New(rand.NewSource(time.Now().Unix())),
- Mutex: &sync.Mutex{},
- }
- const atext = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%`
- func randStr(n int, words []byte) string {
- b := make([]byte, n)
- r.Lock()
- for i := range b {
- b[i] = words[r.Intn(len(words))]
- }
- r.Unlock()
- return string(b)
- }
- func sendPing(motd, port string) {
- dstAddr, err := net.ResolveUDPAddr("udp", "224.0.2.60:4445")
- if err != nil {
- panic(err)
- }
- srcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
- conn, err := net.ListenUDP("udp", srcAddr)
- if err != nil {
- fmt.Println(err)
- }
- msg := []byte(fmt.Sprintf(`[MOTD]%s[/MOTD][AD]%s[/AD]`, motd, port))
- for {
- _, err := conn.WriteToUDP(msg, dstAddr)
- if err != nil {
- panic(err)
- }
- time.Sleep(2 * time.Second)
- }
- }
二进制
windows

运行后,打开游戏,点击多人联机,底部就会有一堆虚假的局域网服务器了。
我看不懂,但我大受震撼.jpg

确实大受震撼,一个都看不懂但我觉得很牛逼