请教一个关于 netty 的问题,关于代理和请求转发

2023-07-03 15:41:45 +08:00
 sdenvi

最近做一个 netty 项目,使用 TCP 协议通信,要求是服务端接收到从客户端发出的请求后,将请求的 client IP 和 remote IP 重新修改后转发给远程接口,因为远程接口进行了 client IP 的强校验,另外每个 client IP 对应的远程接口 remote IP 也不一定一样,请问使用 netty 可以达到这种目的吗,或者说有没有什么别的方式可以实现?

1140 次点击
所在节点    问与答
12 条回复
assiadamo
2023-07-03 15:57:46 +08:00
netty 自带 proxy 的例子看看行不行
https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/proxy
这种小功能感觉用 java 太重,可以用 golang 试试
assiadamo
2023-07-03 16:05:14 +08:00
golang 随便抄了一个,就这么简单、
```
package main

import (
"flag"
"fmt"
"io"
"log"
"net"
)

var localAddr *string = flag.String("l", "localhost:9999", "local address")
var remoteAddr *string = flag.String("r", "localhost:80", "remote address")

func main() {
flag.Parse()
fmt.Printf("Listening: %v\nProxying: %v\n\n", *localAddr, *remoteAddr)

listener, err := net.Listen("tcp", *localAddr)
if err != nil {
panic(err)
}
for {
conn, err := listener.Accept()
log.Println("New connection", conn.RemoteAddr())
if err != nil {
log.Println("error accepting connection", err)
continue
}
go func() {
defer conn.Close()
conn2, err := net.Dial("tcp", *remoteAddr)
if err != nil {
log.Println("error dialing remote addr", err)
return
}
defer conn2.Close()
closer := make(chan struct{}, 2)
go copy(closer, conn2, conn)
go copy(closer, conn, conn2)
<-closer
log.Println("Connection complete", conn.RemoteAddr())
}()
}
}

func copy(closer chan struct{}, dst io.Writer, src io.Reader) {
_, _ = io.Copy(dst, src)
closer <- struct{}{} // connection is closed, send signal to stop proxy
}
```
sdenvi
2023-07-03 16:06:01 +08:00
@assiadamo 感谢回复,研究一下看看行不行,在做调研时候也是考虑使用 golang , 被否了,公司要求不能使用 Java 之外的语言
zzzkkk
2023-07-03 16:34:53 +08:00
@assiadamo
<-closer 收到一个 struct 就会退出
虽然 close 分配了两个空间
是不是有问题?
assiadamo
2023-07-03 18:33:00 +08:00
@zzzkkk
在这种情况下,`<-closer` 将会阻塞,直到从 `closer` 通道接收到一个值。

在 `make(chan struct{}, 2)` 中,我们创建了一个带有缓冲大小为 2 的无缓冲通道。这意味着可以向 `closer` 通道发送两个值,而不会立即阻塞。只有当 `closer` 通道的缓冲区已满时,发送操作才会阻塞。

在 `closer <- struct{}{}` 这一行中,我们向 `closer` 通道发送一个值。如果缓冲区仍有空间,该操作会成功并立即返回。如果缓冲区已满,发送操作将会阻塞,直到有空间可用。

在 `<-closer` 这一行中,我们从 `closer` 通道接收一个值。如果通道中有值可用,该操作会成功并立即返回。如果通道为空,接收操作将会阻塞,直到有值可用。

因此,在你提供的代码中,`<-closer` 将会阻塞,直到从 `closer` 通道接收到一个值。这可以用于等待某些操作完成或信号传递。

希望这解答了你的疑问。如果还有进一步的问题,请随时提问。
oldshensheep
2023-07-03 18:41:14 +08:00
vertx 非常简单,几行代码吧,
public class TcpProxyServer extends AbstractVerticle {

private static final int LOCAL_PORT = 8888;
private static final String REMOTE_HOST = "1.1.1.1";
private static final int REMOTE_PORT = 80;

@Override
public void start() {
NetServer server = vertx.createNetServer();

server.connectHandler(clientSocket -> {

vertx.createNetClient().connect(REMOTE_PORT, REMOTE_HOST, targetSocket -> {
if (!targetSocket.succeeded()) {
// ?
}
var remoteSocket = targetSocket.result();
remoteSocket.pipeTo(clientSocket);
clientSocket.pipeTo(clientSocket);
});
});
server.listen(LOCAL_PORT);
}
}
oldshensheep
2023-07-03 18:45:41 +08:00
修正一下应该是这样的:
remoteSocket.pipeTo(clientSocket);
clientSocket.pipeTo(remoteSocket);
其他的错误处理,连接关闭代码我没写
zzzkkk
2023-07-03 18:55:31 +08:00
@assiadamo
需要两次接收才对
因为只接受一次的话 主程序就退出了 另一个 go 携程可能还没执行完
sdenvi
2023-07-03 21:47:29 +08:00
@oldshensheep 没有具体使用过,多问一句,看代码是直接指定了 local IP 和 remote IP ,能够在客户端发送请求的时候从数据流里面拦截到信息之后,通过 ID 从数据库里面读取到 local IP 和 remote IP 重新建立 TCP 请求发送到后端服务器吗?
sdenvi
2023-07-03 21:55:10 +08:00
@oldshensheep 类似 netty ,道连接成功触发 channelActive ,数据传输时触发 channelRead
oldshensheep
2023-07-03 22:12:33 +08:00
可以的,在客户端建立连接后,客户端发个 ID ,然后服务端读取 ID 查数据库得到 remote IP ,再建立连接也行。
大概这样吧
public void start() {
NetServer server = vertx.createNetServer();

server.connectHandler(clientSocket -> {
String[] remote = { "" };

clientSocket.handler(b -> {
var id = b.getBytes(0, 1);
// 查数据库的到 remote IP
remote[0] = "1.1.1.1";
});

vertx.createNetClient().connect(REMOTE_PORT, remote[0], targetSocket -> {
if (!targetSocket.succeeded()) {
// ?
}
var remoteSocket = targetSocket.result();
remoteSocket.pipeTo(clientSocket);
clientSocket.pipeTo(remoteSocket);
});
});
server.listen(LOCAL_PORT);
}
sdenvi
2023-07-03 22:48:11 +08:00
@oldshensheep 想最终达到的目的就是类似 nat 的功能:客户端 ---> server <---> client ---> remote app 这样的效果,客户端和 TCP 的 server 端建立连接后读取数据流,从数据流里面获取到用户 ID ,拿到 ID 后发送给 TCP 的 client 端,client 端根据 ID 查询到保存到数据库的 client IP 和 remote IP 后重新进行 socket bind ,达到让 remote app 通过 IP 校验。如果只是 proxy 的话直接做转发就行,不用额外的处理,涉及到重新绑定四元组,一时想不到好的解决方案了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/953699

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX