golang 简单的实现内 网 穿 透,用户访问本地服务。

golang 简单的实现内 网 穿 透,用户访问本地服务。,第1张

概述一、功能描述: 客户端通过访问外网服务器上指定端口,间接访问自已本地的内网服务。 二、原理图如下: 三、实现代码如下: server.go代码: client.go代码: 四、测试 1、先把serve

一、功能描述:

客户端通过访问外网服务器上指定端口,间接访问自已本地的内网服务。

二、原理图如下:

三、实现代码如下:

server.go代码:

package main;import (	"net"	"fmt"	"flag"	"os")type MIDServer struct {	//客户端监听	clIEntlis *net.TCPListener;	//后端服务连接	transferlis *net.TCPListener;	//所有通道	channels map[int]*Channel;	//当前通道ID	curChannelID int;}type Channel struct {	//通道ID	ID int;	//客户端连接	clIEnt net.Conn;	//后端服务连接	transfer net.Conn;	//客户端接收消息	clIEntRecvMsg chan []byte;	//后端服务发送消息	transferSendMsg chan []byte;}//创建一个服务器func New() *MIDServer {	return &MIDServer{		channels:     make(map[int]*Channel),curChannelID: 0,};}//启动服务func (m *MIDServer) Start(clIEntPort int,transferPort int) error {	addr,err := net.ResolveTCPAddr("tcp",fmt.Sprintf(":%d",clIEntPort));	if err != nil {		return err;	}	m.clIEntlis,err = net.ListenTCP("tcp",addr);	if err != nil {		return err;	}	addr,err = net.ResolveTCPAddr("tcp",transferPort));	if err != nil {		return err;	}	m.transferlis,addr);	if err != nil {		return err;	}	go m.AcceptLoop();	return nil;}//关闭服务func (m *MIDServer) Stop() {	m.clIEntlis.Close();	m.transferlis.Close();	//循环关闭通道连接	for _,v := range m.channels {		v.clIEnt.Close();		v.transfer.Close();	}}//删除通道func (m *MIDServer) DelChannel(ID int) {	chs := m.channels;	delete(chs,ID);	m.channels = chs;}//处理连接func (m *MIDServer) AcceptLoop() {	transfer,err := m.transferlis.Accept();	if err != nil {		return;	}	for {		//获取连接		clIEnt,err := m.clIEntlis.Accept();		if err != nil {			continue;		}		//创建一个通道		ch := &Channel{			ID:              m.curChannelID,clIEnt:          clIEnt,transfer:        transfer,clIEntRecvMsg:   make(chan []byte),transferSendMsg: make(chan []byte),};		m.curChannelID++;		//把通道加入channels中		chs := m.channels;		chs[ch.ID] = ch;		m.channels = chs;		//启一个goroutine处理客户端消息		go m.ClIEntMsgloop(ch);		//启一个goroutine处理后端服务消息		go m.TransferMsgloop(ch);		go m.Msgloop(ch);	}}//处理客户端消息func (m *MIDServer) ClIEntMsgloop(ch *Channel) {	defer func() {		fmt.Println("ClIEntMsgloop exit");	}();	for {		select {		case data,isClose := <-ch.transferSendMsg:			{				//判断channel是否关闭,如果是则返回				if !isClose {					return;				}				_,err := ch.clIEnt.Write(data);				if err != nil {					return;				}			}		}	}}//处理后端服务消息func (m *MIDServer) TransferMsgloop(ch *Channel) {	defer func() {		fmt.Println("TransferMsgloop exit");	}();	for {		select {		case data,isClose := <-ch.clIEntRecvMsg:			{				//判断channel是否关闭,如果是则返回				if !isClose {					return;				}				_,err := ch.transfer.Write(data);				if err != nil {					return;				}			}		}	}}//客户端与后端服务消息处理func (m *MIDServer) Msgloop(ch *Channel) {	defer func() {		//关闭channel,好让ClIEntMsgloop与TransferMsgloop退出		close(ch.clIEntRecvMsg);		close(ch.transferSendMsg);		m.DelChannel(ch.ID);		fmt.Println("Msgloop exit");	}();	buf := make([]byte,1024);	for {		n,err := ch.clIEnt.Read(buf);		if err != nil {			return;		}		ch.clIEntRecvMsg <- buf[:n];		n,err = ch.transfer.Read(buf);		if err != nil {			return;		}		ch.transferSendMsg <- buf[:n];	}}func main() {	//参数解析	localPort := flag.Int("localPort",8080,"客户端访问端口");	remotePort := flag.Int("remotePort",8888,"服务访问端口");	flag.Parse();	if flag.NFlag() != 2 {		flag.PrintDefaults();		os.Exit(1);	}	ms := New();	//启动服务	ms.Start(*localPort,*remotePort);	//循环	select {};}

clIEnt.go代码:

package main;import (	"net"	"fmt"	"flag"	"os")func handler(r net.Conn,localPort int) {	buf := make([]byte,1024);	for {		//先从远程读数据		n,err := r.Read(buf);		if err != nil {			continue;		}		data := buf[:n];		//建立与本地80服务的连接		local,err := net.Dial("tcp",localPort));		if err != nil {			continue;		}		//向80服务写数据		n,err = local.Write(data);		if err != nil {			continue;		}		//读取80服务返回的数据		n,err = local.Read(buf);		//关闭80服务,因为本地80服务是http服务,不是持久连接		//一个请求结束,就会自动断开。所以在for循环里我们要不断Dial,然后关闭。		local.Close();		if err != nil {			continue;		}		data = buf[:n];		//向远程写数据		n,err = r.Write(data);		if err != nil {			continue;		}	}}func main() {	//参数解析	host := flag.String("host","127.0.0.1","服务器地址");	remotePort := flag.Int("remotePort","服务器端口");	localPort := flag.Int("localPort",80,"本地端口");	flag.Parse();	if flag.NFlag() != 3 {		flag.PrintDefaults();		os.Exit(1);	}	//建立与服务器的连接	remote,fmt.Sprintf("%s:%d",*host,*remotePort));	if err != nil {		fmt.Println(err);	}	go handler(remote,*localPort);	select {};}

四、测试

1、先把server.go上传到外网服务器上,安装GO环境,并编译,然后运行server

> ./server -localPort 8080 -remotePort 8888

2、在本地编译clIEnt.go,运行clIEnt

> clIEnt.exe -host 外网服务器IP -localPort 80 -remotePort 8888

3、浏览器访问外网服务器8080端口

当我浏览器访问时,外网服务器的server会打印两次Msgloop exit,这是因为谷歌浏览器会多一个favicon.ico请求,不知道其他浏览器会不会。

注意,上面的server.go和clIEnt.go代码不排除会有BUG,代码仅供参考,切勿用于生产环境。

总结

以上是内存溢出为你收集整理的golang 简单的实现内 网 穿 透,用户访问本地服务。全部内容,希望文章能够帮你解决golang 简单的实现内 网 穿 透,用户访问本地服务。所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1271722.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存