有些时候项目会碰到需要端口转发的需求,比如:
- 一个服务发布在公共端口上,客户端进来的请求需要转发到另一个本地端口,或者另一台主机;或者
- 一个公共服务需要转到到多台内部服务上,轮询,按优先级等;或者
- 一个服务请求进来之前需要做请求检查,例如安全功能参数等检查,类似网关功能,只有验证通过的请求才能发送给实际服务器。
- 等等等
下面是一个实现TCP端口转发功能的代码例子。
这里例子将会起一个监听在端口fromport,然后把所有向此fromport发起的请求都转发到端口toport,当然必须先有另一个服务已经监听在toport接受请求服务。
package main
import (
"os"
"fmt"
"net"
)
// Start a proxy server listen on fromport
// this proxy will then forward all request from fromport to toport
//
// Notice: a service must has been started on toport
func proxyStart(fromport, toport int) {
proxyaddr := fmt.Sprintf(":%d", fromport)
proxylistener, err := net.Listen("tcp", proxyaddr)
if err != nil {
fmt.Println("Unable to listen on: %s, error: %s\n", proxyaddr, err.Error())
os.Exit(1)
}
defer proxylistener.Close()
for {
proxyconn, err := proxylistener.Accept()
if err != nil {
fmt.Printf("Unable to accept a request, error: %s\n", err.Error())
continue
}
// Read a header firstly in case you could have opportunity to check request
// whether to decline or proceed the request
buffer := make([]byte, 1024)
n, err := proxyconn.Read(buffer)
if err != nil {
fmt.Printf("Unable to read from input, error: %s\n", err.Error())
continue
}
// TODO
// Your choice to make decision based on request header
targetaddr := fmt.Sprintf("localhost:%d", toport);
targetconn, err := net.Dial("tcp", targetaddr)
if err != nil {
fmt.Println("Unable to connect to: %s, error: %s\n", targetaddr, err.Error())
proxyconn.Close()
continue
}
n, err = targetconn.Write(buffer[:n])
if err != nil {
fmt.Printf("Unable to write to output, error: %s\n", err.Error())
proxyconn.Close()
targetconn.Close()
continue
}
go proxyRequest(proxyconn, targetconn)
go proxyRequest(targetconn, proxyconn)
}
}
// Forward all requests from r to w
func proxyRequest(r net.Conn, w net.Conn) {
defer r.Close()
defer w.Close()
var buffer = make([]byte, 4096000)
for {
n, err := r.Read(buffer)
if err != nil {
fmt.Printf("Unable to read from input, error: %s\n", err.Error())
break
}
n, err = w.Write(buffer[:n])
if err != nil {
fmt.Printf("Unable to write to output, error: %s\n", err.Error())
break
}
}
}