Nginx有两种方式实现TCP代理功能:
一种是使用nginx_tcp_proxy_module模块,一般用于Nginx早期版本。
一种是使用ngx_stream_core_module模块,用于1.9及其以后版本。
本文介绍使用stream的方式来实现TCP代理。
(1)重新编译Nginx
只有在configure时使用了--with-stream参数,编译出来的nginx程序才支持stream方式实现TCP代理。
./configure --prefix=/opt/nginx --with-stream
make
make install
(2)配置tcp代理功能
在nginx.conf配置文件中添加stream配置块以及其中的server配置块。
stream {
server {
listen 2001;
proxy_connect_timeout 3s;
proxy_timeout 10s;
proxy_pass 192.168.197.101:2101;
}
}
上述配置中各参数的含义如下:
(a)listen指定Nginx将在tcp 2001端口上监听,proxy_connect_timeout
(b)proxy_connect_timeout指定Nginx与被代理的主机之间建立连接的超时时间。
Syntax: proxy_connect_timeout time;
Default:
proxy_connect_timeout 60s;
Context: stream, server
(c)proxy_timoute指定Nginx与客户端,以及Nginx与被代理的主机之间在两个相邻的读或写数据的操作之间的最大时间间隔。超过此时间间隔而没有数据读或写操作发生,则断开连接。
Syntax: proxy_timeout timeout;
Default:
proxy_timeout 10m;
Context: stream, server
(d)proxy_pass指定了被代理的主机的地址和端口号。
(3)被代理的TCP服务程序
简单起见,本文使用Java编写了一个功能简单的TCP服务程序作为被代理主机上运行的TCP服务,在端口2101监听。
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.BufferedReader; import java.net.ServerSocket; import java.net.Socket; public class mytcpserver { public static void main( String[] args) { int port = 2101; ServerSocket s= null; try { if( args.length > 0) { System.out.println("port argument:"+args[0]); try{ port = Integer.parseInt(args[0]); if(port < 2000 || port > 65535){ port = 2101; } }catch(NumberFormatException ex){ System.out.println("invalid argument port:"); ex.printStackTrace(); } } System.out.println("using port:" + port ); s = new ServerSocket( port); while(true) { Socket c = s.accept(); System.out.println(""+ c.getInetAddress().getHostAddress() +":"+ c.getPort() +" connected."); BufferedReader br = null; PrintWriter pw = null; try { br = new BufferedReader( new InputStreamReader(c.getInputStream())); pw = new PrintWriter( c.getOutputStream()); String msg; while((msg = br.readLine())!= null) { System.out.println(msg); pw.println(msg); pw.flush(); } }catch(Exception e) { e.printStackTrace(); } finally{ if(c != null) { System.out.println(""+ c.getInetAddress().getHostAddress() +":"+ c.getPort() +" disconnected."); } if(br!=null){br.close();br = null;} if(pw!=null){pw.close();pw = null;} if(c!=null){ c.close();c = null;} } } }catch(Exception ex){ ex.printStackTrace(); } finally{ try{ if(s!=null){ s.close();s = null;} }catch(Exception e){ e.printStackTrace(); } } System.out.println("QUIT."); } }
(4)运行测试。
启动mytcpserver程序(端口2101),启动Nginx程序(端口2001)。在另一台机器上运行telnet 192.168.197.101 2001。
(a)mytcpserver的输出。
java mytcpserver
using port:2101
192.168.197.101:44786 connected.
hello
hello
hello
192.168.197.101:44786 disconnected.
可以看到mytcpserver检测到有客户端(nginx)从44786端口连接上来了。
(b)Nginx的日志输出。
同样可以看到,客户端从20.1.1.11的端口4217连接到了Nginx的2001端口,然后Nginx从44786端口连接到了mytcpserver的2101端口,至此tcp代理连接建立成功。在传递了3次数据后,由于再没有通过telnet输入任何文字,因此Nginx在10秒钟(proxy_timeout)内没有从telnet程序接收到任何数据,断开了连接。在本次通信过程中,telnet程序总共发送了21个字节(hello\r\nhello\r\nhello\r\n),mytcpserver程序总共发送了18个字节(hello\nhello\nhello\n)。
2017/07/11 06:46:38 [info] 3308#0: *39 client 20.1.1.11:4217 connected to 0.0.0.0:2001
2017/07/11 06:46:38 [info] 3308#0: *39 proxy 192.168.197.101:44786 connected to 192.168.197.101:2101
2017/07/11 06:46:56 [info] 3308#0: *39 connection timed out (110: Connection timed out) while proxying connection, client: 20.1.1.11, server: 0.0.0.0:2001, upstream: "192.168.197.101:2101", bytes from/to client:21/18, bytes from/to upstream:18/21
Nginx stream功能完整介绍请参考:
http://nginx.org/en/docs/stream/ngx_stream_core_module.html