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