当前位置:   article > 正文

Go语言upnp和socket编程_socket upnp

socket upnp

到互联网公司实习,公司提了一个需求,处在内网的服务器运行一段时间后会出问题,经检查是UPnP失效。因此要求我设计一个测试程序实现如下功能
在内网中运行一台server,利用UPnP协议发现设备(DiscoverDevice)和添加端口映射(AddPortMapping),并监听TCP某个端口,等待外网client发送消息hello,然后在server端打印该消息。最后判断是否是UPnP导致问题还是服务本身存在问题。
程序用go语言实现

import (
	"fmt"
	"os"
     "net"
    "time"
	"github.com/huin/goupnp"
	"github.com/huin/goupnp/dcps/internetgateway1"
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

导入依赖包,没什么好说的。

UPnP部分
第一步 发现

func GetExternalIPAddress() {
	clients, errors, err := internetgateway1.NewWANIPConnection1Clients()
	extIPClients := make([]GetExternalIPAddresser, len(clients))
	for i, client := range clients {
		extIPClients[i] = client
	}
	DisplayExternalIPResults(extIPClients, errors, err)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

利用discovered WANIPConnection services来发现设备和外部ip地址,
internetgateway1.NewWANIPConnection1Clients()这块可参考https://github.com/huin/goupnp/blob/master/dcps/internetgateway1/internetgateway1.go
发现设备是UPnP的一个阶段,具体可参考:https://blog.csdn.net/jiuaiwo1314/article/details/7656427
或者UPNP协议原理

DisplayExternalIPResults用来打印设备信息

func DisplayExternalIPResults(clients []GetExternalIPAddresser, errors []error, err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error discovering service with UPnP: ", err)
		return
	}

	if len(errors) > 0 {
		fmt.Fprintf(os.Stderr, "Error discovering %d services:\n", len(errors))
		for _, err := range errors {
			fmt.Println("  ", err)
		}
	}

	fmt.Fprintf(os.Stderr, "Successfully discovered %d services:\n", len(clients))
	for _, client := range clients {
		device := &client.GetServiceClient().RootDevice.Device

		fmt.Fprintln(os.Stderr, "  Device:", device.FriendlyName)
		if addr, err := client.GetExternalIPAddress(); err != nil {
			fmt.Fprintf(os.Stderr, "    Failed to get external IP address: %v\n", err)
		} else {
			fmt.Fprintf(os.Stderr, "    External IP address: %v\n", addr)
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

第二步是描述。
通过URL,下载XML文件,并从中找到有关设备的类型,服务类型,控制URL,时间触发URL等。同样分两步,首先下载描述文件。第二步解析该XML文件

for _, client := range clients {
		device := &client.GetServiceClient().RootDevice.Device

		fmt.Fprintln(os.Stderr, "  Device:", device.FriendlyName)
		if addr, err := client.GetExternalIPAddress(); err != nil {
			fmt.Fprintf(os.Stderr, "    Failed to get external IP address: %v\n", err)
		} else {
			fmt.Fprintf(os.Stderr, "    External IP address: %v\n", addr)
		}
                          srv := client.GetServiceClient().Service
		fmt.Println(device.FriendlyName, " :: ", srv.String())
		scpd, err := srv.RequestSCPD()
                             if err != nil {
			fmt.Printf("  Error requesting service SCPD: %v\n", err)
		} else {
			fmt.Println("  Available actions:")
			for _, action := range scpd.Actions {
				fmt.Printf("  * %s\n", action.Name)
				for _, arg := range action.Arguments {
					var varDesc string
					if stateVar := scpd.GetStateVariable(arg.RelatedStateVariable); stateVar != nil {
						varDesc = fmt.Sprintf(" (%s)", stateVar.DataType.Name)
					}
					fmt.Printf("    * [%s] %s%s\n", arg.Direction, arg.Name, varDesc)
				}
			}
                            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

调srv.RequestSCPD()完成描述阶段

UPnP的最后一步了,添加端口映射

if scpd == nil || scpd.GetAction("AddPortMapping") != nil  {
			err :=  client.AddPortMapping("", 5000, "TCP", 5001, "192.168.1.2", true, "Test port mapping", 0)
			fmt.Println("AddPortMapping: ", err)
		}
  • 1
  • 2
  • 3
  • 4

ip地址不一定设置成192.168.1.2,根据自己主机的ip而定,端口号也可以设置成别的端口。

socket部分

func testsocket() {
server := "192.168.1.20:5001"
    netListen, err := net.Listen("tcp", server)//内网server监听5001端口
    if err != nil{
        Log("connect error: ", err)
        os.Exit(1)
    }
    Log("Waiting for Client ...")
    for{
        conn, err := netListen.Accept()   //等待外网client发消息
        if err != nil{
            Log(conn.RemoteAddr().String(), "Fatal error: ", err)
            continue
        }
        conn.SetReadDeadline(time.Now().Add(time.Duration(10)*time.Second)) 
        Log(conn.RemoteAddr().String(), "connect success!")
        go handleConnection(conn)//处理长连接,因为程序要一直运行,反复处理打印client发送的消息
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

长连接,在Server和Socket之间设计通讯机制,当两者之间没有信息交互时,双方便会定时发送数据包(心跳),以维持连接状态。

func handleConnection(conn net.Conn) {
    buffer := make([]byte, 1024)
    for {
        n, err := conn.Read(buffer)
        if err != nil {     
            return
        }
 
        Data := buffer[:n]
        message := make(chan byte)
      
        go HeartBeating(conn, message, 4)心跳计时
        
        go GravelChannel(Data, message)//检测每次Client是否有数据传来
 
        Log(time.Now().Format("2006-01-02 15:04:05.0000000"), conn.RemoteAddr().String(), string(buffer[:n]))
    }
    defer conn.Close()
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
func GravelChannel(bytes []byte, mess chan byte) {
    for _, v := range bytes{
        mess <- v
    }
    close(mess)
}

func HeartBeating(conn net.Conn, bytes chan byte, timeout int) {
//心跳计时,根据GravelChannel判断Client是否在设定时间内发来信息
    select {
    case fk := <- bytes:
        Log(conn.RemoteAddr().String(), "heartbeat", string(fk), "times")
        conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
        break
 
        case <- time.After(5 * time.Second):
            Log("conn dead now")
            conn.Close()
    }
}

func Log(v ...interface{}) {
    fmt.Println(v...)
    return
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/475176
推荐阅读
相关标签
  

闽ICP备14008679号