欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于netty实现的socks5代理协议

程序员文章站 2022-05-23 16:26:22
...

基于netty实现的socks5代理协议

socks5协议

简介

socks5协议是一个标准的代理协议,工作在网络的四层,理论上可以代理任意应用层协议。协议标准RFC1928,用户/密码鉴权标准RFC1929。协议的中文版本可以参考这里

现有开源实现

用的比较多的是linux下的ss5开源实现,ss5的安装和配置如下:

./configure --with-debug --with-epollio --with-gssapi
make
make install
chomd +x /etc/init.d/ss5

修改/etc/init.d/ss5,增加端口,日志等配置:

export SS5_SOCKS_PORT=1081
export SS5_CONFIG_FILE=/tmp/ss5.conf
export SS5_PASSWORD_FILE=/tmp/ss5.passwd
export SS5_LOG_FILE=/tmp/ss5.log
export SS5_PROFILE_PATH=/tmp

修改/etc/sysconfig/ss5,配置服务器启动用户:

SS5_OPTS=" -u root"

修改/etc/opt/ss5/ss5.conf,配置鉴权方式:

permit -        0.0.0.0/0       -       0.0.0.0/0       -       -       -       -       -    
#不使用用户认证,
auth 0.0.0.0/0 – –
#使用用户名/密码认证,
auth 0.0.0.0/0 – u

修改/etc/opt/ss5/ss5.passwd,配置用户密码:

user passord

启动ss5服务:

service ss5 start

为什么要自己实现

需要更精确的记录代理访问日志,需要自己特殊的鉴权方式。ss5是基于c开发的,本人c语言一般,改起来还是稍显费劲,而基于java开发的socks5协议并没有特别成熟的,最终决定自己用netty4.1开发。

netty基本概念

NIO和BIO

BIO阻塞IO,NIO非阻塞IO。NIO基于事件通知机制,可以达到在单线程情况下同时处理多个IO。传统的BIO对每个IO请求都需要分配单独线程处理,而线程的分配是需要占用内存,分配的代价很高。因此传统的BIO每秒并发最多到千级别(和服务器配置相关),而NIO能支持每秒万级的并发。

NIO并不是没有缺点,NIO的异步特性对编程要求较高,和BIO的传统开发相比较,要更难控制,开发和调试难度也较大。

netty简介

在java开发中,提到NIO必会想到netty,netty是一个NIO开发框架,能降低NIO应用开发难度,开发者不需要关注通讯连接,可以将主要的精力放在对收到的信息进行编码,转换为对象,进行业务逻辑处理。netty框架是一个典型的管道过滤器模式的应用。netty3.x和4.x版本有较大的改动,本文主要使用netty4.1。

EventLoopGroup和EventLoop

前面提到NIO能在单线程模型相支持多IO,实际上线程并不是一个,而是有限的几个,这里的EventLoopGroup就是线程池,EventLoop就是线程。EventLoopGroup的线程数量和CPU核心数量相关,也可以初始化的时候自定义。

NIO应用的服务器端通常不止一个线程池,一般会建立一个boss线程池,一个worker线程池。boss线程池用来接收客户端连接,接收的连接交给worker线程池监听,一个worker线程可以监听多个io。

//boss线程池,这里定义线程数量为2
EventLoopGroup boss = new NioEventLoopGroup(2);
//worker线程池,线程数量结合cpu核心计算出来Runtime.getRuntime().availableProcessors() * 2
EventLoopGroup worker = new NioEventLoopGroup();

ServerBootstrap和Bootstrap

Bootstrap是一个builder模式的类型构造器,ServerBootstrap用于服务器端的构造,Bootstrap用于客户端的构造。主要配置线程池、ChannelFactory、基本参数、ChannelHandler链表初始化等。ServerBootstrap调用bind监听端口,Bootstrap调用connect连接服务器端。

在netty中ChannelFuture会经常碰到,由于netty所有的操作都是异步的,bind,connet,close等都不会立即返回接口,都是通过监听方法回调。通过调用ChannelFuture.addListener()添加监听方法。ChannelFutureListener里有几个内置的监听方法CLOSE,CLOSE_ON_FAILURE,FIRE_EXCEPTION_ON_FAILURE。

Channel,ChannelPipeline,ChannelHandler,ChannelHandlerContext

一个Channel分配一个ChannelPipeline,每个ChannelPipeline里是多个ChannelHandler组成的链表,每个ChannelHandler会对应一个ChannelHandlerContext。看看下面的图就能明白: 基于netty实现的socks5代理协议
            
    
    博客分类: netty nettysocks代理tcp 

  • Channel负责底层通讯,Channel会绑定到一个worker线程中,Channel在收到Event后会传递给ChannelPipeline,Event会在ChannelHandler链表中流转。 基于netty实现的socks5代理协议
            
    
    博客分类: netty nettysocks代理tcp 

  • ChannelPipeline负责消息的传递,里面是一个ChannelHandler的组成的双向链表,按消息类型分为上行消息ChannelHandler和下行消息ChannelHandler。因此就有了两个子接口,ChannelInboundHandler只关心上行消息,ChannelOutboundHandler只关心下行消息。如果上下行消息都需要关系可以继承ChannelDuplexHandler。ChannelHandler通过调用fireChannelRead()将上行消息传递给下一个Handler,通过调用write()形成下行消息,调用write()后,上行消息将不再传递给下一个Handler。

  • ChannelHandler是处理消息的单元,是netty开发的核心。开发人员大部分都是在写各种Handler。netty内置了很多协议的handler,但是比较遗憾文档不多。上面已经说过,ChannelHandler按消息类型分可以分为ChannelInboundHandler和ChannelOutboundHandler。按功能分为编解码Handler(编码Encoder/解码Decoder)和消息处理Handler。通常需要将上行消息需要进行解码转换为对象,再将对象传递给之后的业务处理Handler。业务处理完毕,将下行对象编码后转换为ByteBuf交给Channel传递给网络层。

ByteBuf

理解ByteBuf对消息的编解码有很多好处,ByteBuf是Channel进行网络通信的核心对象,ByteBuf就像一个缓冲池一样,ByteBuf逻辑上就是一个byte容器。ByteBuf里的数据被两个指针划分为三个部分,如下图所示: 基于netty实现的socks5代理协议
            
    
    博客分类: netty nettysocks代理tcp 

  • reader index前面的数据是已经读过的数据,这些数据可以扔掉
  • 从reader index开始,到writer index之前的数据是可读数据
  • 从writer index开始,为可写区域

使用netty实现socks5协议

netty中已经内置了socks5协议的编解码,我们只需要实现逻辑处理Handler就行,下面是几个主要的编解码codec:

  • Socks5InitialRequestDecoder负责服务器和客户端协商鉴权方式,返回服务器端支持的鉴权方式,会将消息解码为DefaultSocks5InitialRequest对象
  • Socks5PasswordAuthRequestDecoder是使用用户名和密码的鉴权方式的协议,返回鉴权是否通过,会将消息解码为DefaultSocks5PasswordAuthRequest对象
  • Socks5CommandRequestDecoder负责目标服务器的连接建立,返回建立是否成功,会将消息解码为DefaultSocks5CommandRequest对象
  • Socks5ServerEncoder负责所有下行对象的编码,转换为ByteBuf

编解码工作不需要我们自己开发,剩下的事情就比较简单,只需要处理各个层级的Socks5Request就行了。ChannelHandler链的结构如下:

Socks5ServerEncoder
Socks5InitialRequestDecoder
Socks5InitialRequestHandler(实现自己的处理逻辑)
Socks5PasswordAuthRequestDecoder
Socks5PasswordAuthRequestHandler(实现自己的处理逻辑)
Socks5CommandRequestDecoder
Socks5CommandRequestHandler(实现自己的处理逻辑)

通过实现3个ChannelHandler,我们就能轻松搞定一个socks5协议。全部代码可以在github上查到,项目名为socks5-netty