摘要:作者简介地址地址本章介绍代码网络中端口映射作者使用操作系统,其他平台也大同小异介绍通用即插即用。端口映射将一个外部端口映射到一个内网。
作者:Derek
简介Github地址:https://github.com/Bytom/bytom
Gitee地址:https://gitee.com/BytomBlockc...
本章介绍bytom代码P2P网络中upnp端口映射
作者使用MacOS操作系统,其他平台也大同小异UPNP介绍Golang Version: 1.8
UPNP(Universal Plug and Play)通用即插即用。UPNP端口映射将一个外部端口映射到一个内网ip:port。从而实现p2p网络从外网能够穿透网关访问到内网的bytomd节点。
UPNP协议SSDP(Simple Service Discovery Protocol 简单服务发现协议)
GENA(Generic Event Notification Architecture 通用事件通知结构)
SOAP(Simple Object Access Protocol 简单对象访问协议)
XML(Extensible Markup Language 可扩张标记语言)
p2p/upnp/upnp.go
发现网络中支持UPNP功能的设备从网络中发现支持UPNP功能的设备,并得到该设备的location和url等相关信息
type upnpNAT struct {
serviceURL string // 设备的描述文件URL,用于得到该设备的描述信息
ourIP string // 节点本地ip地址
urnDomain string // 设备类型
}
func Discover() (nat NAT, err error) {
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
if err != nil {
return
}
conn, err := net.ListenPacket("udp4", ":0")
if err != nil {
return
}
socket := conn.(*net.UDPConn)
defer socket.Close()
err = socket.SetDeadline(time.Now().Add(3 * time.Second))
if err != nil {
return
}
st := "InternetGatewayDevice:1"
// 多播请求:M-SEARCH SSDP协议定义的发现请求。
buf := bytes.NewBufferString(
"M-SEARCH * HTTP/1.1
" +
"HOST: 239.255.255.250:1900
" +
"ST: ssdp:all
" +
"MAN: "ssdp:discover"
" +
"MX: 2
")
message := buf.Bytes()
answerBytes := make([]byte, 1024)
for i := 0; i < 3; i++ {
// 向239.255.255.250:1900发送一条多播请求
_, err = socket.WriteToUDP(message, ssdp)
if err != nil {
return
}
// 如果从网络中发现UPNP设备则会从239.255.255.250:1900收到响应消息
var n int
n, _, err = socket.ReadFromUDP(answerBytes)
for {
n, _, err = socket.ReadFromUDP(answerBytes)
if err != nil {
break
}
answer := string(answerBytes[0:n])
if strings.Index(answer, st) < 0 {
continue
}
// HTTP header field names are case-insensitive.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
// 获得设备location
locString := "
location:"
answer = strings.ToLower(answer)
locIndex := strings.Index(answer, locString)
if locIndex < 0 {
continue
}
loc := answer[locIndex+len(locString):]
endIndex := strings.Index(loc, "
")
if endIndex < 0 {
continue
}
// 获得设备的描述url和设备类型
locURL := strings.TrimSpace(loc[0:endIndex])
var serviceURL, urnDomain string
serviceURL, urnDomain, err = getServiceURL(locURL)
if err != nil {
return
}
var ourIP net.IP
ourIP, err = localIPv4()
if err != nil {
return
}
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP.String(), urnDomain: urnDomain}
return
}
}
err = errors.New("UPnP port discovery failed.")
return
}
添加端口映射
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port做映射
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
// A single concatenation would break ARM compilation.
message := "
" +
"" + strconv.Itoa(externalPort)
message += " " + protocol + " "
message += "" + strconv.Itoa(internalPort) + " " +
"" + n.ourIP + " " +
"1 "
message += description +
" " + strconv.Itoa(timeout) +
" "
var response *http.Response
response, err = soapRequest(n.serviceURL, "AddPortMapping", message, n.urnDomain)
if response != nil {
defer response.Body.Close()
}
if err != nil {
return
}
// TODO: check response to see if the port was forwarded
// log.Println(message, response)
// JAE:
// body, err := ioutil.ReadAll(response.Body)
// fmt.Println(string(body), err)
mappedExternalPort = externalPort
_ = response
return
}
删除端口映射
向upnp设备发送一条http post请求,将内部网络ip:port和外部网络ip:port删除映射关系
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
message := "
" +
"" + strconv.Itoa(externalPort) +
" " + protocol + " " +
" "
var response *http.Response
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message, n.urnDomain)
if response != nil {
defer response.Body.Close()
}
if err != nil {
return
}
// TODO: check response to see if the port was deleted
// log.Println(message, response)
_ = response
return
}
获取映射后的公网地址
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
info, err := n.getExternalIPAddress()
if err != nil {
return
}
addr = net.ParseIP(info.externalIpAddress)
return
}
func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) {
message := "
" +
" "
var response *http.Response
response, err = soapRequest(n.serviceURL, "GetExternalIPAddress", message, n.urnDomain)
if response != nil {
defer response.Body.Close()
}
if err != nil {
return
}
var envelope Envelope
data, err := ioutil.ReadAll(response.Body)
reader := bytes.NewReader(data)
xml.NewDecoder(reader).Decode(&envelope)
info = statusInfo{envelope.Soap.ExternalIP.IPAddress}
if err != nil {
return
}
return
}
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/24258.html
摘要:作者简介地址地址本章介绍代码网络中地址簿作者使用操作系统,其他平台也大同小异介绍用于存储网络中保留最近的对端节点地址在下,默认的地址簿路径存储在地址簿格式地址类型在中存储的地址有两种标识新地址,不可靠地址未成功连接过。 作者:Derek 简介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomB...
摘要:只有当触发了或才能终止进程退出。退出时执行如下操作会将挖矿功能停止,网络停止等操作。 作者:Derek 简介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 本章介绍bytom代码启动、节点初始化、及停止的过程 作者使用MacOS操作系统,其他平台也大同小异Golang V...
摘要:首先读取请求内容,解析请求,接着匹配相应的路由项,随后调用路由项的回调函数来处理。每一个路由项由请求方法和回调函数组成将监听地址作为参数,最终执行开始服务于外部请求创建对象首先,实例化对象。我们可以看到一条项由和对应的回调函数组成。 作者:Derek 简介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com...
摘要:函数总共操作有两步从缓存中查询值,如果查到则返回如果为从缓存中查询到则回调回调函数。回调函数会将从磁盘上获得到块信息存储到缓存中并返回该块的信息。回调函数实际上调取的是下的,它会从磁盘中获取信息并返回。 作者:Derek 简介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc......
摘要:函数总共操作有两步从缓存中查询值,如果查到则返回如果为从缓存中查询到则回调回调函数。回调函数会将从磁盘上获得到块信息存储到缓存中并返回该块的信息。回调函数实际上调取的是下的,它会从磁盘中获取信息并返回。 简介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 本章介绍Dere...
阅读 3341·2021-09-10 10:51
阅读 3543·2021-08-31 09:38
阅读 1862·2019-08-30 15:54
阅读 3276·2019-08-29 17:22
阅读 3414·2019-08-26 13:53
阅读 2138·2019-08-26 11:59
阅读 3453·2019-08-26 11:37
阅读 3484·2019-08-26 10:47