使用go做一个返回公网IP的web服务

简介: 使用go做一个返回公网IP的web服务

在我们日常运维中,特别是在云服务器环境下,其使用基本上都是弹性网卡,所以使用ip a只能获取其内网地址,若想要获取其公网ip,除了上云平台查看机器信息以外,只有通过其他服务来协助返回公网地址了。



我们应该如何获取公网地址


前些天,我们已经看了http相关基础报文,我们可以知晓,从http报文而言来看,我们是无法获取其来访者IP的,那么我们究竟应当如何获取呢?


那在报文中,哪里有记录来访者IP呢? 答案是TCP/IP4层协议中的网络层中有定义源IP地址和目标IP地址,其报文架构大致如下:


下图来源于: rfc791 网址: www.rfc-editor.org/rfc/rfc791

image.png


我们能够直接在应用中获取其IP层的数据么?答案是不能的,因为网络包来了,若只有IP层的数据,这是给哪个进程的呢? 所以这个时候就需要引入我们的传输层的TCP报文协议了。

下图来源于: rfc793 网址: www.rfc-editor.org/rfc/rfc793.…


image.png

可以看到,在IP报文的基础上,TCP报文又定义了源端口 和 目标端口,端口是为了解决不同主机和不通进程之间的通信所建立的规则,结合起来可以形成一个四元组,即: 源IP地址,源端口号 | 目标IP地址,目标端口号,这个我们也称之为是TCP套接字四元组


所以,回到问题,返回来访者IP地址,我们就将其四元组的信息返回回去就可以了。



编写返回来访者IP的web服务


根据上述信息,我们可以直接编写一个tcpdemo,返回源IP数据。

package main
import (
  "fmt"
  "net"
  "time"
)
func main() {
  l , _ := net.Listen("tcp","0.0.0.0:8082")
  conn , _ := l.Accept()
  fmt.Println("来访者IP:" , conn.RemoteAddr().String())
  time.Sleep(3 * time.Second)
}


我们将其程序执行后,可得直接如下


image.png


我们可以来分析下,该TCP套接字四元组信息是什么,首先,服务器端口是8082,而服务器IP监听所有的网卡,根据客户端的信息,暂时定义为127.0.0.1 , 而客户端端口为54953,客户端IP为127.0.0.1,所以可以这样理解为


源IP地址 源端口号 目标IP地址 目标端口号
127.0.0.1 54953 127.0.0.1 8082

这里的54953端口是客户端临时使用的,使用完后会撤销回去,怎么? 你不认为这2个确实建立了连接? 那我们可以测试下,使用netstat命令可以查看TCP连接信息,我们暂时将代码中time.Sleep(3 * time.Second) 给修改为time.Sleep(30 * time.Second) ,以便于我们稍后观察。即程序如下:

image.png



我们将转移阵地到linux环境下测试代码。

启动服务器后,我们使用curl 127.0.0.1:8082来访问其数据。

image.png

会得到客户端的端口号,我们将在其没有没有close连接的时候,我们使用netstat -an | grep 39440可以查询TCP连接信息。

image.png


其中-a参数是显示网卡所有的连接信息,-n是显示ip地址,而非主机名。


那我们要返回其来访者IP地址,并将其打造为web服务,那也简单,我们仅需嵌套一层http报文就行了。


我们将其程序改写如下即可。

package main
import (
  "fmt"
  "net"
  "strings"
)
func main() {
  l , err := net.Listen("tcp","0.0.0.0:8082");if err != nil {
    panic(err)
  }
  for {
    conn , err := l.Accept();if err != nil {
      continue
    }
    ip := strings.Split(conn.RemoteAddr().String(),":")[0]
    conn.Write([]byte(fmt.Sprintf("HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s",len(ip),ip)))
  }
}


如上代码,在服务器启动后,使用curl IP:8082后可以得到IP地址,我们尝试一下。

image.png

若是放在公网上,则显示的是公网的地址信息。



关于反向代理如何获取地址


反向代理是一个很有意思的事情,我们通过如上方法,若经过代理,我们所获取的remoteIP则是代理服务器的ip,而非客户端Ip,流程如下图。


image.png


那么如何解决这个问题呢? 很显然,修改网络层报文,这个是很不现实的,也是不可能的,所以我们可以另辟蹊径,由于我们做的是web服务器,所以我们可以在代理服务器上设置将客户端的remoteIP添加到请求报文中去,一同发给真实web服务器,而服务器拿到后,则不从TCP套接字获取IP,而直接从http报文获取IP即可,这个只要代理服务器和真实web服务器将相关规则约定好即可,我们拿nginx举例:


image.png

例如我们配置如上,那么在真实web服务器就应该使用请求头为X-Real-IP来获取客户端IP地址。



总结


返回来访者IP地址,在很多业务场景,尤其是目前云服务器中。在获取过程中,我们可以将其分为2种,第一种是web服务器直接面对客户端,第二种是经过一层代理服务器,不管怎么样,我们都需要从tcp请求中获取客户端的地址,经过某种方式,从而返回给客户端,怎么样,好玩吧,快来试试吧。


相关文章
|
1月前
|
网络协议 Java Nacos
nacos常见问题之在web界面 上下线服务时报错 400如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
30 0
|
1月前
|
监控 Serverless 测试技术
Serverless 应用引擎常见问题之做的web服务计费如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
407 3
|
1月前
|
JSON API 数据库
解释如何在 Python 中实现 Web 服务(RESTful API)。
解释如何在 Python 中实现 Web 服务(RESTful API)。
26 0
|
5天前
|
API Go
使用Go语言通过API获取代理IP并使用获取到的代理IP
使用Go语言通过API获取代理IP并使用获取到的代理IP
|
23天前
|
数据采集 Java API
python并发编程: Python使用线程池在Web服务中实现加速
python并发编程: Python使用线程池在Web服务中实现加速
18 3
python并发编程: Python使用线程池在Web服务中实现加速
|
1月前
javaWeb服务详解(含源代码,测试通过,注释) ——web.xml
javaWeb服务详解(含源代码,测试通过,注释) ——web.xml
7 0
|
1月前
|
XML JSON API
通过Flask框架创建灵活的、可扩展的Web Restful API服务
通过Flask框架创建灵活的、可扩展的Web Restful API服务
|
1月前
|
安全 搜索推荐 前端开发
如何在本地安装Flask并将其web界面发布到公网上远程访问协同开发
如何在本地安装Flask并将其web界面发布到公网上远程访问协同开发
|
1月前
|
SQL 机器学习/深度学习 缓存
Go语言Web应用实战与案例分析
【2月更文挑战第21天】本文将通过实战案例的方式,深入探讨Go语言在Web应用开发中的应用。我们将分析一个实际项目的开发过程,展示Go语言在构建高性能、可扩展Web应用方面的优势,并分享在开发过程中遇到的问题和解决方案,为读者提供宝贵的实战经验。
|
1月前
|
安全 中间件 Go
Go语言Web服务性能优化与安全实践
【2月更文挑战第21天】本文将深入探讨Go语言在Web服务性能优化与安全实践方面的应用。通过介绍性能优化策略、并发编程模型以及安全加固措施,帮助读者理解并提升Go语言Web服务的性能表现与安全防护能力。