golang手把手实现tcp内网穿透代理
首先还是我们需要一个http服务器,这个http服务器是我们的内网的服务器,也就是说我们需要在外网访问到这个位于内网的http服务器。假设我们内网的ip是127.0.0.1,分配的局域网ip是192.168.1.10,然后http端口是8080
那么显而易见,我们在同一内网环境是可以访问的,直接使用192.168.1.10:8000即可访问到服务器
但是如果不在同一局域网的机器就不行了,需要借助一台公网ip的服务器来做一个透传代理。
内网服务器准备
这里假设你已经安装python2或者python3,打开我们的mac终端或者windows cmd 在python2下输入python -m SimpleHTTPServer
python3下输入python -m http.server
这样我们可以快速得到一台http服务器,打开浏览器输入127.0.0.1:8000可以发现是一个文件浏览的http服务器
我们不需要很复杂的http服务器,仅仅用来做测试而已,所以这样是足够的了
服务端代码
控制客户端的监听代码
1.这里选择监听在8009端口,这个tcp服务,主要用来接受客户端的连接请求的,然后发送控制指令给到客户端,请求建立隧道连接的。这里只接受一个客户端的连接请求,如果有多余的会close掉
一旦有客户端连接到8009端口,这个tcp连接是一直保持的,为什么呢?
因为服务端需要发送控制指令给客户端,所以tcp连接必须一直保持。
然后服务端会每隔两秒发送hi这个消息给到客户端,客户端可以直接忽略掉,因为这个hi只是类似心跳机制的保证。
var cache *net.TCPConn = nil
func makeControl() {
var tcpAddr *net.TCPAddr
tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:8009")
//打开一个tcp断点监听
tcpListener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
panic(err)
}
fmt.Println("控制端口已经监听")
for {
tcpConn, err := tcpListener.AcceptTCP()
if err != nil {
panic(err)
}
fmt.Println("新的客户端连接到控制端服务进程:" + tcpConn.RemoteAddr().String())
if cache != nil {
fmt.Println("已经存在一个客户端连接!")
//直接关闭掉多余的客户端请求
tcpConn.Close()
} else {
cache = tcpConn
}
go control(tcpConn)
}
func control(conn *net.TCPConn) {
go func() {
for {
//一旦有客户端连接到服务端的话,服务端每隔2秒发送hi消息给到客户端
//如果发送不出去,则认为链路断了,清除cache连接
_, e := conn.Write(([]byte)("hi\n"))
if e != nil {
cache = nil
}
time.Sleep(time.Second * 2)
}
}()
}
对外访问的服务端口监听
假设端口是8007,这里的对外访问的服务端口监听,也就是说假设我们服务器ip是10.18.10.1的话,那么访问10.18.10.1的端口8007,就等于请求内网的127.0.0.1:8000 127.0.0.1:8000就是上面的python服务器
和上面的代码看起来很像,但是用处不一样,上面那个主要目的是控制客户端,要求它建立请求
这里的目的主要是提供真正需要tcp代理透传的服务!
func makeAccept() {
var tcpAddr *net.TCPAddr
tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:8007")
tcpListener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
panic(err)
}
defer tcpListener.Close()
for {
tcpConn, err := tcpListener.AcceptTCP()
if err != nil {
fmt.Println(err)
continue
}
fmt.Println("A client connected 8007:" + tcpConn.RemoteAddr().String())
addConnMathAccept(tcpConn)
sendMessage("new\n")
}
}
这里大家思考一下,如果真的有请求来了,也就是访问8007了,我们怎么办呢?显然我们需要把进来的流量发给127.0.0.1:8000,让它去处理就行了。
这么一想好像很简单的样子,但是好像有问题,那就是我的10.18.10.1是公网ip啊,大家都知道,只有非公网可以主动访问公网,非公网主动访问公网的意思就是好像我们日常访问百度一样。公网是不可以直接跟非公网建立tcp连接的。
那么怎么解决呢?
那就是我们需要先记录下这个进来的8007的tcp连接,然后上面不是说到我们有个tcp连接是一直hold住的,那就是8009那个,服务器每隔2秒发送hi给客户端的。
那么我们可以通过这个8009的tcp链路发送一条消息给客户端,告诉客户端赶紧和我建立一个新的tcp请求吧,为了方便描述,我把"告诉客户端赶紧和我建立一个新的tcp请求"这个新的请求标记为8008链路
这时候就可以把8007的tcp流量发送到这个新建立的tcp链路上。然后把这个新建立的tcp链路的请求发送回去,建立一个读写传输链路即可。
注意这里不能使用8009的tcp链路,8009只是我们用来沟通的链路。
理清楚后,开始编码吧
记录进来的8007的tcp连接,使用一个结构体来存储,这个结构体需要记录accept的tcp连接,也就是8007的tcp链路,和请求的时间,以及8008的链路
刚开始记录的时候8008的链路肯定是nil的,所以设置为nil即可
把它添加到map里面。使用unixNano作为临时key
type ConnMatch struct {
accept *net.TCPConn //8007 tcp链路 accept
acceptAddTime int64 //接受请求的时间
tunnel *net.TCPConn //8008 tcp链路 tunnel
}
var connListMap = make(map[string]*ConnMatch)
var lock = sync.Mutex{}
func addConnMathAccept(accept *net.TCPConn) {
//加锁防止竞争读写map
lock.Lock()
defer lock.Unlock()
now := time.Now().UnixNano()
connListMap[strconv.FormatInt(now, 10)] = &ConnMatch{accept, time.Now().Unix(), nil}
}
告诉客户端赶紧和我建立一个新的tcp请求
这里直接用上面那个cache的tcp链路发送消息即可,不需要太复杂,这里简单定义为new\n即可
........
addConnMathAccept(tcpConn)
sendMessage("new\n")
}
}
func sendMessage(message string) {
fmt.Println("send Message " + message)
if cache != nil {
_, e := cache.Write([]byte(message))
if e != nil {
fmt.Println("消息发送异常")
fmt.Println(e.Error())
}
} else {
fmt.Println("没有客户端连接,无法发送消息")
}
}
转发的tcp监听服务
这里我们来创建前面提到的8008tcp连接了,这里的8008端口,也就是前面说的发送new这个消息告诉客户端来和这个8008连接吧
func makeForward() {
var tcpAddr *net.TCPAddr
tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:8008")
tcpListener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
panic(err)
}
defer tcpListener.Close()
fmt.Println("Server ready to read ...")
for {
tcpConn, err := tcpListener.AcceptTCP()
if err != nil {
fmt.Println(err)
continue
}
fmt.Println("A client connected 8008 :" + tcpConn.RemoteAddr().String())
configConnListTunnel(tcpConn)
}
}
然后把8008链路分配到ConnMatch,这两个tcp链路是配对的
var connListMapUpdate = make(chan int)
func configConnListTunnel(tunnel *net.TCPConn) {
//加锁解决竞争问题
lock.Lock()
used := false
for _, connMatch := range connListMap {
//找到tunnel为nil的而且accept不为nil的connMatch
if connMatch.tunnel == nil && connMatch.accept != nil {
//填充tunnel链路
connMatch.tunnel = tunnel
used = true
//这里要break,是防止这条链路被赋值到多个connMatch!
break
}
}
if !used {
//如果没有被使用的话,则说明所有的connMatch都已经配对好了,直接关闭多余的8008链路
fmt.Println(len(connListMap))
_ = tunnel.Close()
fmt.Println("关闭多余的tunnel")
}
lock.Unlock()
//使用channel机制来告诉另一个方法已经就绪
connListMapUpdate <- UPDATE
}
tcp 转发,这里读取connListMapUpdate这个chain,说明map有更新,需要建立tcpForward隧道
func tcpForward() {
for {
select {
case <-connListMapUpdate:
lock.Lock()
for key, connMatch := range connListMap {
//如果两个都不为空的话,建立隧道连接
if connMatch.tunnel != nil && connMatch.accept != nil {
fmt.Println("建立tcpForward隧道连接")
go joinConn2(connMatch.accept, connMatch.tunnel)
//从map中删除
delete(connListMap, key)
}
}
lock.Unlock()
}
}
}
func joinConn2(conn1 net.TCPConn, conn2 net.TCPConn) {
f := func(local *net.TCPConn, remote *net.TCPConn) {
//defer保证close
defer local.Close()
defer remote.Close()
//使用io.Copy传输两个tcp连接,
_, err := io.Copy(local, remote)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println("join Conn2 end")
}
go f(conn2, conn1)
go f(conn1, conn2)
}
最后增加一个超时机制,因为会存在这种情况,就是当用户请求8007端口的时候,迟迟等不到配对的connMatch的tunnel链路啊,因为有可能client端挂掉了,导致server不管怎么发送"new"请求,client都无动于衷。
在浏览器看来表现就是一直转着,但是我们不能这样子。
所以当超时的时候,我们主动断掉connMatch中的accept链路即可,设置为5秒
func releaseConnMatch() {
for {
lock.Lock()
for key, connMatch := range connListMap {
//如果在指定时间内没有tunnel的话,则释放该连接
if connMatch.tunnel == nil && connMatch.accept != nil {
if time.Now().Unix()-connMatch.acceptAddTime > 5 {
fmt.Println("释放超时连接")
err := connMatch.accept.Close()
if err != nil {
fmt.Println("释放连接的时候出错了:" + err.Error())
}
delete(connListMap, key)
}
}
}
lock.Unlock()
time.Sleep(5 * time.Second)
}
}
最后把所有方法整合起来
func main() {
//监听控制端口8009
go makeControl()
//监听服务端口8007
go makeAccept()
//监听转发端口8008
go makeForward()
//定时释放连接
go releaseConnMatch()
//执行tcp转发
tcpForward()
}
客户端代码
连接到服务器的8009控制端口,随时接受服务器的控制请求,随时待命
func connectControl() {
var tcpAddr *net.TCPAddr
//这里在一台机测试,所以没有连接到公网,可以修改到公网ip
tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:8009")
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
fmt.Println("Client connect error ! " + err.Error())
return
}
fmt.Println(conn.LocalAddr().String() + " : Client connected!8009")
reader := bufio.NewReader(conn)
for {
s, err := reader.ReadString('\n')
if err != nil || err == io.EOF {
break
} else {
//接收到new的指令的时候,新建一个tcp连接
if s == "new\n" {
go combine()
}
if s == "hi" {
//忽略掉hi的请求
}
}
}
}
combine方法的代码,整合local和remote的tcp连接
func combine() {
local := connectLocal()
remote := connectRemote()
if local != nil && remote != nil {
joinConn(local, remote)
} else {
if local != nil {
err := local.Close()
if err!=nil{
fmt.Println("close local:" + err.Error())
}
}
if remote != nil {
err := remote.Close()
if err!=nil{
fmt.Println("close remote:" + err.Error())
}
}
}
}
func joinConn(local net.TCPConn, remote net.TCPConn) {
f := func(local *net.TCPConn, remote *net.TCPConn) {
defer local.Close()
defer remote.Close()
_, err := io.Copy(local, remote)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println("end")
}
go f(local, remote)
go f(remote, local)
}
connectLocal 连接到python的8000端口!
func connectLocal() *net.TCPConn {
var tcpAddr *net.TCPAddr
tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:8000")
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
fmt.Println("Client connect error ! " + err.Error())
return nil
}
fmt.Println(conn.LocalAddr().String() + " : Client connected!8000")
return conn
}
connectRemote 连接到服务端的8008端口!
func connectRemote() *net.TCPConn {
var tcpAddr *net.TCPAddr
tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:8008")
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
fmt.Println("Client connect error ! " + err.Error())
return nil
}
fmt.Println(conn.LocalAddr().String() + " : Client connected!8008")
return conn;
}
全部整合起来就是
func main() {
connectControl()
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
help rx pharmacy discount card vipps certified pharmacy viagra pharmacy assistant course online
http://inpharm24.com/# e pharmacy in india
pharmacy in india best indian pharmacy or pharmacy in india online
https://lcu.hlcommission.org/lcu/pages/auth/forgotPassword.aspx?Returnurl=http://inpharm24.com buy viagra online in india
best online pharmacy india pharmacy council of india and pharmacy franchises in india pharmacy store in india
doctor of pharmacy india: InPharm24 - drugs from india
pharmacy bangkok viagra top mail order pharmacies or mexican pharmacy advair
https://maps.google.sc/url?sa=j&url=https://pharmexpress24.shop online pharmacy spironolactone
pharmacy online degree viagra from vipps pharmacy and buy concerta online pharmacy arimidex online pharmacy
can you buy codeine in mexico: Pharm Mex - most trusted canadian online pharmacy
are mexican antibiotics safe Pharm Mex cabo pharmacy
sun pharmacy india: aster pharmacy india - online pharmacy india ship to usa
india pharmacy no prescription: pharmacy website in india - pharmacy india online
sun pharmacy india indian pharmacy buy medicines online in india
los algodones purple pharmacy prices: buy painkillers online - buy pain killers online
india pharmacy cialis: medicine online india - buy viagra online in india
https://pharmexpress24.shop/# Doxycycline
pfizer lipitor pharmacy finasteride indian pharmacy or synthroid people’s pharmacy
http://clients1.google.com.nf/url?q=https://pharmexpress24.com::: b12 injections online pharmacy
Cephalexin escitalopram oxalate online pharmacy and good neighbor pharmacy naproxen Brand Levitra
apotheke academy medicine from india or buy medicine online
http://www.ra2.od.ua/engine/redirect.php?url=http://inpharm24.com overseas pharmacy india
meds from india online pharmacy india and pharmacy in india medications from india
voltaren emulgel online pharmacy: giant food store phoenixville pharmacy - oxymorphone online pharmacy
order medicines online india pharmacy delivery to usa best online pharmacy in india
online pharmacy india: best online pharmacy in india - pharmacy india online
best indian pharmacy: medicine online india - online pharmacy in india
lexapro pharmacy coupons: pharmacy first fluconazole - nexium pharmacy coupon
can i buy zepbound in mexico cancun mexico pharmacy price list meds.com.mx
online pharmacy india: india pharmacy viagra - overseas pharmacy india
https://pharmexpress24.shop/# b12 injections online pharmacy
clozapine pharmacy directory: Detrol - tylenol 3 pharmacy name
online pharmacy india ship to usa InPharm24 online pharmacy company in india
celebrex online pharmacy: Pharm Express 24 - rx outreach pharmacy
reputable online pharmacies: prednisone mexican pharmacy - accutane mexican pharmacy
ozempic mexican pharmacy: online mexican pharmacy wegovy - order zolpidem from mexican pharmacy
ex officio member of pharmacy council of india: medicines online india - online medical store india
india pharmacy ship to usa InPharm24 retail pharmacy market in india
https://pharmexpress24.com/# target pharmacy refills online
medications from india: medications from india - history of pharmacy in india
drugs from mexico to us: can you order prescriptions online - prednisone mexican pharmacy
medicine online order InPharm24 india pharmacy viagra
buy medication from india: pharmacy council of india - buy medicine from india
mexican pharmacy t3: buy meds online - mexican pharmacy diet pills
https://inpharm24.shop/# buy drugs from india
india rx online pharmacy india order medicines online
https://pharmexpress24.com/# computer rx pharmacy software
can i use a mexican prescription in the us: farmacia online estados unidos - penicillin cream from mexico
tadalafil 5mg sans ordonnance est-ce que je peux acheter du monuril sans ordonnance ? vaccin dtp pharmacie sans ordonnance
lucen 40 mg prezzo: augmentin sciroppo prezzo - killitam gocce prezzo
mГ©dicament sans ordonnance: cytotec sans ordonnance pharmacie algerie - combantrin sans ordonnance
acheter viagra sans ordonnance pharmacie paris Pharmacie Express liniment gifrer 900ml
acheter previcox sans ordonnance spedra sans ordonnance pharmacie france or malarone generique
https://www.google.nu/url?q=https://pharmacieexpress.com viagra in france
testosterone en pharmacie sans ordonnance stress medicament sans ordonnance and ordonnance substitut nicotinique brosse Г dent gum ortho
puntura pappataci: mirena spirale prezzo - paracetamolo coop
http://pharmacieexpress.com/# achat tramadol en ligne sans ordonnance
comprar cuatrocrem crema online farmacia online como montar or donde puedo comprar viagra sin receta
https://www.domaininfofree.com/domain-traffic/confiapharma.com farmacia online mas economica
farmacia labandeira online donde puedo comprar fluoxetina sin receta and farmacia online a domicilio aciclovir pastillas comprar sin receta
farmacia francia online: ozempic price - a cosa serve tachifene
zirtec gocce bambini formistin prezzo topster supposte prezzo
farmacia online redcare: se puede comprar voltaren pastillas sin receta - se puede comprar cialis sin receta mГ©dica
zitromax 200 mg/5 ml bambini iopidine collirio or rosuvastatina 10 mg prezzo
https://maps.google.rs/url?sa=t&url=https://farmaciasubito.com farmacia online consegna veloce
cerenia compresse spididol 400 ogni quante ore and oki bustine 80 mg online indoxen 25 mg prezzo
tadalafil sans ordonnance en pharmacie prix cialis 5 mg or lariam prix
http://lonevelde.lovasok.hu/out_link.php?url=https://pharmacieexpress.shop sildenafil prix
rГ©diger une ordonnance mГ©dicale sildГ©nafil prix france and sterdex Г©quivalent sans ordonnance solupred ordonnance
kipling pillola: Farmacia Subito - ecoval scalp fluid prezzo
antibiotique chlamydia sans ordonnance aderma gel douche scalpel pharmacie sans ordonnance
imovane 7 5: foster 100/6 prezzo - glucophage 500 prezzo
https://confiapharma.com/# glutaraldehido farmacia online
se puede comprar clotrimazol ovulos sin receta farmacia online casa or farmacia ourense online
https://www.trueweb.eu/?a=link&url=https://confiapharma.com regaxidil 5 farmacia online
comprar farmacos sin receta farmacia online mascarilla infantil and se puede comprar viagra en argentina sin receta puedo comprar misoprostol sin receta en argentina
infection urinaire pharmacie sans ordonnance: Pharmacie Express - anesthГ©siant dentaire pharmacie sans ordonnance
quark principio attivo: Farmacia Subito - laurea triennale farmacia online
viagra generique france creme antibiotique sans ordonnance laxatif en pharmacie sans ordonnance
enoxaparina 4.000 prezzo tobral pomata prezzo or zoely prezzo 3 blister prezzo
https://toolbarqueries.google.com.ph/url?q=https://farmaciasubito.com farmacia online prezzi bassi
loperamide hexal gramplus supposte bambini raffreddore and farmacia linfa palermo zarelis 75 prezzo
test grippe pharmacie sans ordonnance ozempic sans ordonnance or brosse interdentaire gum
http://auto-otziv.ru/r.php?url=http://pharmacieexpress.shop acheter augmentin sans ordonnance
parfum caudalie rose des vignes lavement pharmacie sans ordonnance and pharmacie pilule sans ordonnance antibiotique amoxicilline sans ordonnance
urorec 8 mg prezzo: jadiza pillola prezzo - dermatar unguento prezzo
mascarilla ffp2 comprar farmacia online: venta online de productos de farmacia - farmacia online mascarillas ffp2
viagra homme [url=http://pharmacieexpress.com/#]pharmacie aphte sans ordonnance[/url] birodogyl sans ordonnance
mederma controindicazioni: pantorc 20 prezzo - flixotide spray
https://confiapharma.shop/# comprar clovate sin receta
farmacia online puerto rico [url=https://confiapharma.com/#]luaterra farmacia online[/url] farmacia online topasel
clenil spray puff: nausil gocce foglietto illustrativo - nifedicor gocce vendita online
cupon farmacia online: Confia Pharma - comprar zovirax sin receta
mycostatin prezzo mutuabile dicloreum 100 mg or trulicity 0 75
https://cse.google.com.cu/url?sa=t&url=https://farmaciasubito.com punture di pappataci immagini
[url=https://www.google.com.sl/url?q=https://farmaciasubito.com]pigitil 800[/url] celecoxib 200 mg prezzo and [url=http://korean.travel.plus/space-uid-7002.html]rosumibe 5/10[/url] tinset gocce prezzo
farmacia online trankimazin: se puede comprar viagra en farmacias sin receta en españa - farmacia online estudiar
inava sensibilité: Pharmacie Express - viagra achat
lyrica 75 mg 56 capsule prezzo: plavix 75 prezzo - viagra prezzo
remboursement pharmacie sans ordonnance: Pharmacie Express - pulmoserum sans ordonnance
farmacia tijuana online: Confia Pharma - donde comprar azitromicina sin receta
acheter Kamagra sans ordonnance: kamagra pas cher - acheter Kamagra sans ordonnance
xucq8f
ipbe31
p1qyl8
hsuikv
Beautiful phone case, you must have it
Scenic Phone Case - Coastal Village Design by Yuri Khrushch Matte
https://www.amazon.com/Scenic-Phone-Case-Coastal-Khrushch/dp/B0F21T224J/ref=sr_1_21?dib=eyJ2IjoiMSJ9.6dIUBwPr7A22XYTvxJJm6Ev1_pATlzqJi_H_l9ubLwRerCwuqCVQyMqZ2CznqFVVbdO6Lf8j0Ayhvsj9WPODW-nJOdxjrWUwvx_cYkTOvlS0U6ZKouRkNUpd3fYZVNzeNiRM3oPyS3A2EWu5OL8Y9GPW5QdbIfl6GQlkXy1de5n0smX5xxh2-sUQXT6R6HdYux0ZS2mbxVoCZqpRkxse7wFSX8b8V9_z9O2EGFfwvgA.g5FRPxV9o0gUhPJttrSUVUtuKh2HJYuqmZY_GmEFp-8&dib_tag=se&keywords=phone&m=A38FMUB1PWNJ6C&qid=1745910289&s=merchant-items&sr=1-21&xpid=rl1xchb9OouGZ&th=1
Beautiful phone case, you must have it
Floral Poppy Tough Phone Case for Nature Lovers
Designed by Yuri Khrushch
https://www.amazon.com/Floral-Nature-Lovers-Designed-Khrushch/dp/B0F23G49N7/ref=sr_1_70?dib=eyJ2IjoiMSJ9.Xf5iOyUHsmeP3jHzPCt0Vhw1r97YV3if2mIbQzmBBqHSHqdW7Ew-xEnibR9AUlO-d3TdyZv4n8vatISd0z-akHeuNMuiQuqcilUHXFLIzyPAZNDjXKWUvuO1oGbM9ql_yPsdBwU1OkyCwcojnlwI3WtP2oR6BHaV4S0g9AAOr9w.yz_i1Ao5pH6iqS4vFV-ar2Dv_LS5K0NJDlAAQl_F5Wg&dib_tag=se&keywords=phone&m=A38FMUB1PWNJ6C&qid=1745773517&refresh=1&s=merchant-items&sr=1-70&xpid=c7WLCG0JGMl11
yl4e1y
https://kamagraprix.com/# kamagra oral jelly
kamagra 100mg prix
1asybm
5fph5u
vmif0q
tn7s77
pq59qg
1c3m00
orl3fp