在我们日常运维中,特别是在云服务器环境下,其使用基本上都是弹性网卡,所以使用ip a
只能获取其内网地址,若想要获取其公网ip
,除了上云平台查看机器信息以外,只有通过其他服务来协助返回公网地址了。
我们应该如何获取公网地址
前些天,我们已经看了http
相关基础报文,我们可以知晓,从http
报文而言来看,我们是无法获取其来访者IP的,那么我们究竟应当如何获取呢?
那在报文中,哪里有记录来访者IP呢? 答案是TCP/IP
4层协议中的网络层中有定义源IP地址和目标IP地址,其报文架构大致如下:
下图来源于: rfc791 网址: www.rfc-editor.org/rfc/rfc791
我们能够直接在应用中获取其IP层的数据么?答案是不能的,因为网络包来了,若只有IP层的数据,这是给哪个进程的呢? 所以这个时候就需要引入我们的传输层的TCP
报文协议了。
下图来源于: rfc793 网址: www.rfc-editor.org/rfc/rfc793.…
可以看到,在IP
报文的基础上,TCP
报文又定义了源端口 和 目标端口,端口是为了解决不同主机和不通进程之间的通信所建立的规则,结合起来可以形成一个四元组,即: 源IP地址,源端口号 | 目标IP地址,目标端口号,这个我们也称之为是TCP套接字四元组。
所以,回到问题,返回来访者IP地址,我们就将其四元组的信息返回回去就可以了。
编写返回来访者IP的web服务
根据上述信息,我们可以直接编写一个tcp
的demo
,返回源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) }
我们将其程序执行后,可得直接如下
我们可以来分析下,该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)
,以便于我们稍后观察。即程序如下:
我们将转移阵地到linux
环境下测试代码。
启动服务器后,我们使用curl 127.0.0.1:8082
来访问其数据。
会得到客户端的端口号,我们将在其没有没有close
连接的时候,我们使用netstat -an | grep 39440
可以查询TCP
连接信息。
其中-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
地址,我们尝试一下。
若是放在公网上,则显示的是公网的地址信息。
关于反向代理如何获取地址
反向代理是一个很有意思的事情,我们通过如上方法,若经过代理,我们所获取的remoteIP
则是代理服务器的ip
,而非客户端Ip
,流程如下图。
那么如何解决这个问题呢? 很显然,修改网络层报文,这个是很不现实的,也是不可能的,所以我们可以另辟蹊径,由于我们做的是web
服务器,所以我们可以在代理服务器上设置将客户端的remoteIP
添加到请求报文中去,一同发给真实web
服务器,而服务器拿到后,则不从TCP
套接字获取IP
,而直接从http
报文获取IP
即可,这个只要代理服务器和真实web
服务器将相关规则约定好即可,我们拿nginx
举例:
例如我们配置如上,那么在真实web
服务器就应该使用请求头为X-Real-IP
来获取客户端IP
地址。
总结
返回来访者IP
地址,在很多业务场景,尤其是目前云服务器中。在获取过程中,我们可以将其分为2种,第一种是web
服务器直接面对客户端,第二种是经过一层代理服务器,不管怎么样,我们都需要从tcp
请求中获取客户端的地址,经过某种方式,从而返回给客户端,怎么样,好玩吧,快来试试吧。