基于Netty实现的RPC(版本二笔记)

基于Netty实现的RPC(版本二笔记)

网络传输从BIO到NIO,序列化要减少字节流长度,提高序列化反序列化的效率

一、Netty服务端和客户端

1、服务端server

1.1 NettyServer

服务端接收客户端的RpcRquest请求,其中执行链中添加了对应的处理器,分别是编码器、拆包器、解码器、客户端处理器

  • 编码器(对象 -> 字节数组 -> ByteBuf(自定义协议)):发送RpcResponse响应对象,经过CommonEncoder编码按照自定义协议编码成ByteBuf对象
  • 拆包器:接收客户端请求的RpcRequest对象编码成的ByteBuf对象,按照基于固定长度域的拆包器进行拆包
  • 解码器(ByteBuf -> 字节数组 -> 对象(自定义协议)):对ByteBuf对象按照自定义协议进行解码成POJO对象
  • 服务端处理器:NettyServerHandler,接收客户端传送的RpcReuqest,执行客户端调用对应接口的服务方法,返回响应对象RpcResponse
/**
 * Netty中处理RpcRequest的Handler
 * @ClassName: NettyServerHandler
 * @Author: whc
 * @Date: 2021/05/29/21:49
 */
public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> {

	private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
	private static RequestHandler requestHandler;
	private static ServiceRegistry serviceRegistry;

	static {
		requestHandler = new RequestHandler();
		serviceRegistry = new DefaultServiceRegistry();
	}


	@Override
	protected void channelRead0(ChannelHandlerContext ctx, RpcRequest msg) throws Exception {
		try {
			logger.info("服务器接收到消息:{}", msg);
			String interfaceName = msg.getInterfaceName();
			Object server = serviceRegistry.getService(interfaceName);
			Object result = requestHandler.handle(msg, server);
			// 向客户端返回响应数据
			// 注意服务端处理器这里的执行链顺序,因为是ctx.writeAndFlush而不是ch.writeAndFlush,所以在执行出栈(out)时,是从当前ctx处理器从后往前找,而不是从通道最后从后往前找
			ChannelFuture future = ctx.writeAndFlush(RpcResponse.success(result, msg.getRequestId()));
			// 消息发送完毕关闭连接
			future.addListener(ChannelFutureListener.CLOSE);
		} finally {
			// 记得释放对象,防止内存泄漏
			ReferenceCountUtil.release(msg);
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		logger.error("处理过程调用时有错误发生");
		cause.printStackTrace();
		ctx.close();
	}

}

1.2 NettyServerHandler

位于服务端责任链的尾部,用于接收RpcRequest,并且执行调用,执行真正的接口调用方法,返回处理结果。

/**
 * Netty中处理RpcRequest的Handler
 * @ClassName: NettyServerHandler
 * @Author: whc
 * @Date: 2021/05/29/21:49
 */
public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> {

	private static final Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);
	private static RequestHandler requestHandler;
	private static ServiceRegistry serviceRegistry;

	static {
		requestHandler = new RequestHandler();
		serviceRegistry = new DefaultServiceRegistry();
	}


	@Override
	protected void channelRead0(ChannelHandlerContext ctx, RpcRequest msg) throws Exception {
		try {
			logger.info("服务器接收到消息:{}", msg);
			String interfaceName = msg.getInterfaceName();
			Object server = serviceRegistry.getService(interfaceName);
			Object result = requestHandler.handle(msg, server);
			// 向客户端返回响应数据
			ChannelFuture future = ctx.writeAndFlush(RpcResponse.success(result, msg.getRequestId()));
			// 消息发送完毕关闭连接
			future.addListener(ChannelFutureListener.CLOSE);
		} finally {
			// 记得释放对象,防止内存泄漏
			ReferenceCountUtil.release(msg);
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		logger.error("处理过程调用时有错误发生");
		cause.printStackTrace();
		ctx.close();
	}

}

2、客户端client

2.1 NettyClient

负责执行客户端调用远程服务端服务的sendRequest请求,其中执行链中添加了对应的处理器,分别是编码器、拆包器、解码器、客户端处理器

  • 编码器(对象 -> 字节数组 -> ByteBuf(自定义协议)):发送RpcRequest请求对象,经过CommonEncoder编码按照自定义协议编码成ByteBuf对象
  • 拆包器:接收服务端响应回来的RpcResponse对象编码成的ByteBuf对象,然后对网络数据包按照基于固定长度域的拆包器进行拆包
  • 解码器(ByteBuf -> 字节数组 -> 对象(自定义协议)):对ByteBuf对象按照自定义协议进行解码成POJO对象
  • 客户端处理器:NettyClientHandler,接收服务端传送的RpcReponse,设置服务端响应的RpcResponse标识
/**
 * NIO方式消费者客户端类
 * @ClassName: NettyClient
 * @Author: whc
 * @Date: 2021/05/29/23:07
 */
public class NettyClient implements RpcClient {

	private static final Logger logger = LoggerFactory.getLogger(NettyClient.class);
	private static final Bootstrap bootstrap;

	private CommonSerializer serializer;

	static {
		EventLoopGroup group = new NioEventLoopGroup();
		bootstrap = new Bootstrap();
		// 1. 指定线程模型
		bootstrap.group(group)
				// 2. 指定IO类型为NIO
				.channel(NioSocketChannel.class)
				// 开启TCP底层心跳机制
				.option(ChannelOption.SO_KEEPALIVE, true);
	}

	private String host;
	private int port;

	public NettyClient(String host, int port) {
		this.host = host;
		this.port = port;
	}

	@Override
	public Object sendRequest(RpcRequest rpcRequest) {
		if(serializer == null) {
			logger.error("未设置序列化器");
			throw new RpcException(RpcError.SERIALIZER_NOT_FOUND);
		}
		bootstrap.handler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel ch) throws Exception {
				ChannelPipeline pipeline = ch.pipeline();
				// 执行链: head -> CommonEncoder(out) -> Spliter -> CommonDecoder -> NettyClientHandler -> tail
				// out出栈主要是对写回结果进行加工
				// in入栈主要是用来读取服务端数据,写回结果
				// 发送RpcRequest请求对象,经过CommonEncoder编码按照自定义协议编码成ByteBuf对象
				pipeline.addLast(new CommonEncoder(serializer))
						// 接收服务端响应回来的RpcResponse对象,经过Spliter,对网络数据包按照基于固定长度域的拆包器进行拆包
						.addLast(new Spliter())
						// 对数据包按照自定义协议进行解码成POJO对象
						.addLast(new CommonDecoder())
						// 客户端对解码出来的POJO对象进行调用处理
						.addLast(new NettyClientHandler());
			}
		});
		try {
			ChannelFuture future = bootstrap.connect(host, port).sync();
			logger.info("客户端连接到服务器 {}:{}", host, port);
			Channel channel = future.channel();
			if(channel != null) {
				// 发送数据
				channel.writeAndFlush(rpcRequest).addListener(future1 -> {
					if(future1.isSuccess()) {
						logger.info(String.format("客户端发送消息: %s", rpcRequest.toString()));
					} else {
						logger.error("发送消息时有错误发生: ", future1.cause());
					}
				});
				// 为了让netty不会关闭
				channel.closeFuture().sync();
				// 通过给channel设计别名,获取特定名字下的channel中的内容(这个在hanlder中设置)
				// AttributeKey是,线程隔离的,不会有线程安全问题。
				AttributeKey<RpcResponse> key = AttributeKey.valueOf("rpcResponse" + rpcRequest.getRequestId());
				RpcResponse rpcResponse = channel.attr(key).get();
				RpcMessageChecker.check(rpcRequest, rpcResponse);
				return rpcResponse.getData();
			}
		} catch (InterruptedException e) {
			logger.error("发送消息时有错误发生: ", e);
		}
		return null;
	}

	@Override
	public void setSerializer(CommonSerializer serializer) {
		this.serializer = serializer;
	}
}

2.2 NettyClientHandler

位于客户端责任链的尾部,用于接收RpcResponse,并且执行调用,给channel设计别名

(判断客户端接收服务端传过来的RpcResponse是否能接收成功,客户端先通过对接收到的RpcResponse在通道中设置一个标识,接着客户端在后续操作中只要从通道获取这个标识符,取出然后判断客户端传递过去的RpcRequest中的请求号是否和服务端传递过来的RpcRespons中的响应请求号相等(对应RpcRequest的请求号),则说明双方成功通信)

/**
 * Netty客户端处理器
 * @ClassName: NettyClientHandler
 * @Author: whc
 * @Date: 2021/05/30/0:06
 */
public class NettyClientHandler extends SimpleChannelInboundHandler<RpcResponse> {

	private static final Logger logger = LoggerFactory.getLogger(NettyClientHandler.class);

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, RpcResponse msg) throws Exception {
		try {
			logger.info(String.format("客户端接收到消息: %s", msg));
			// 接收到response, 给channel设计别名,让sendRequest里读取response
			AttributeKey<RpcResponse> key = AttributeKey.valueOf("rpcResponse" + msg.getRequestId());
			ctx.channel().attr(key).set(msg);
			ctx.channel().close();
		} finally {
			ReferenceCountUtil.release(msg);
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		logger.error("过程调用时有错误发生:");
		cause.printStackTrace();
		ctx.close();
	}
}

二、自定义协议和编解码器

1、协议

在传输过程中,在发送的数据上加上各种必要的数据,形成自定义的协议,而自动加上这个数据就是编码器的工作,解析数据获得原始数据就是解码器的工作。

自定义协议如下:

+---------------+---------------+-----------------+-------------+
|  Magic Number |  Package Type | Serializer Type | Data Length |
|    4 bytes    |    4 bytes    |     4 bytes     |   4 bytes   |
+---------------+---------------+-----------------+-------------+
|                          Data Bytes                           |
|                   Length: ${Data Length}                      |
+---------------------------------------------------------------+
  • Magic Number : 魔数,标识一个协议包
  • Package Type:标明是一个调用请求还是调用响应
  • Serializer Type:标明实际数据使用的序列化器(客户端和服务端应使用统一标准)
  • Data Length:实际数据的长度

2、编码器

/**
 * 通信协议的设计
 * 通用的编码拦截器
 * 负责将 POJO 对象编码成 ByteBuf
 * @ClassName: CommonEncoder
 * @Author: whc
 * @Date: 2021/05/29/20:48
 */
public class CommonEncoder extends MessageToByteEncoder {

	private static final int MAGIC_NUMBER = 0xCAFEBABE;

	private final CommonSerializer serializer;

	public CommonEncoder(CommonSerializer serializer) {
		this.serializer = serializer;
	}

	// 自定义传输协议,防止粘包
	// 消息格式为: [魔数][数据包类型][序列化器类型][数据长度][数据]
	//			  4字节   4字节      4字节       4字节
	@Override
	protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
		// 魔数
		out.writeInt(MAGIC_NUMBER);
		// 数据包类型
		if (msg instanceof RpcRequest) {
			out.writeInt(PackageType.REQUEST_PACK.getCode());
		} else {
			out.writeInt(PackageType.RESPONSE_PACK.getCode());
		}
		// 序列化器类型
		out.writeInt(serializer.getCode());
		byte[] bytes = serializer.serialize(msg);
		// 数据长度
		out.writeInt(bytes.length);
		// 数据
		out.writeBytes(bytes);
	}
}

3、解码器

/**
 * 通用的解码拦截器
 * 完成 ByteBuf 到 POJO 对象的解码
 * @ClassName: CommonDecoder
 * @Author: whc
 * @Date: 2021/05/29/21:24
 */
public class CommonDecoder extends ReplayingDecoder {

	private static final Logger logger = LoggerFactory.getLogger(CommonDecoder.class);

	private static final int MAGIC_NUMBER = 0xCAFEBABE;


	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
		int magic = in.readInt();
		if(magic != MAGIC_NUMBER) {
			logger.error("不识别的协议包:{}", magic);
			throw new RpcException(RpcError.UNKNOWN_PROTOCOL);
		}
		int packageCode = in.readInt();
		Class<?> packageClass;
		if(packageCode == PackageType.REQUEST_PACK.getCode()) {
			packageClass = RpcRequest.class;
		} else if(packageCode == PackageType.RESPONSE_PACK.getCode()) {
			packageClass = RpcResponse.class;
		} else {
			logger.error("不识别的数据包:{}", packageCode);
			throw new RpcException(RpcError.UNKNOWN_PACKAGE_TYPE);
		}
		int serializerCode = in.readInt();
		// 获取序列化器类型
		CommonSerializer serializer = CommonSerializer.getByCode(serializerCode);
		if(serializer == null) {
			logger.error("不识别的反序列化器:{}", serializerCode);
			throw new RpcException(RpcError.UNKNOWN_SERIALIZER);
		}
		// 数据长度
		int length = in.readInt();
		byte[] bytes = new byte[length];
		// 填充数据
		in.readBytes(bytes);
		// 反序列化
		Object obj = serializer.deserialize(bytes, packageClass);
		out.add(obj);
	}
}

4、拆包器

/**
 * Netty 提供了 LengthFieldBasedFrameDecoder,自动屏蔽 TCP 底层的拆包和粘包问题,只需要传入正确的参数,即可轻松解决“读半包“问题。
 *
 * Spliter作用:
 * 1. 基于固定长度域的拆包器,根据我们的自定义协议,把数据拼装成一个个符合我们自定义数据包大小的ByteBuf,接着根据我们的自定义协议解码器去解码
 * 2. 拒绝非本协议连接
 *
 * @ClassName: Spliter
 * @Author: whc
 * @Date: 2021/06/04/22:16
 */
public class Spliter extends LengthFieldBasedFrameDecoder {

	private static final int MAGIC_NUMBER = 0xCAFEBABE;

	// 消息格式为: [魔数][数据包类型][序列化器类型][数据长度][数据]
	//			  4字节   4字节      4字节       4字节
	private static final int LENGTH_FIELD_OFFSET = 12;
	// 数据长度
	private static final int LENGTH_FIELD_LENGTH = 4;

	public Spliter() {
		super(Integer.MAX_VALUE, LENGTH_FIELD_OFFSET, LENGTH_FIELD_LENGTH);
	}

	@Override
	protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
		if(in.getInt(in.readerIndex()) != MAGIC_NUMBER) {
			ctx.channel().closeFuture();
			return null;
		}
		return super.decode(ctx, in);
	}
}

5、补充知识

5.1 TCP粘包拆包问题

  • 什么是粘包和半包?

    Netty发送和读取数据的单位,可以形象的使用ByteBuf来充当。
    每一次发送,就是向Channel写入一个ByteBuf;每一次读取,就是从Channel读到一个ByteBuf
    例如发送一次数据:channel.writeAndFlush(buffer);
    读取一次数据: ByteBuf byteBuf = (ByteBuf)msg;
    理想情况下,发送端每发送一个buffer,接收端就能收到一个一模一样的buffer,但是在实际的通讯过程中,实际情况并不是如此。
    
    • 粘包

      就是接收端读取的时候,多个发送过来的 ByteBuf “粘”在了一起。

      换句话说,接收端读取一次ByteBuf,读取了多个发送的ByteBuf

    • 半包

      就是接收端将一个发送端的ByteBuf “拆”开了,形成一个破碎的包,我们定义这种 ByteBuf 为半包。

      换句话说,接收端读取一次的 ByteBuf ,读到了发送端的一个 ByteBuf的一部分,是为半包。

  • 为什么会粘包、半包?

    因为Netty底层是走的TCP协议,说白了传输的是就是字节流,消息与消息之间是没有边界的。发生TCP粘包拆包的原因主要有:

    1. 当连续发送数据时,由于TCP协议的nagle算法,会将较小的内容拼接成较大的包一次性发送到服务器端,因而导致粘包;
    2. 当发送的内容较大时,由于服务器端的recv(buffer_size)方法中buffer_size较小,不能一次性读完所有数据,从而导致一个消息分拆成多次读取,就是半包的情况。
    首先,上层应用层每次读取底层缓冲的数据容量是有限制的,当TCP底层缓冲数据包比较大时,将被分成多次读取,造成断包,在应用层来说,就是半包。
    
    其次,如果上层应用层一次读到多个底层缓冲数据包,就是粘包。
    
  • 如何解决粘包、 半包问题?

    基本思路是:在接收端,需要根据自定义协议来读取底层的数据包,重新组装我们应用层的数据包,这个过程通常在接收端称为拆包。

  • 拆包的原理

    • 接收端应用层不断从底层的TCP 缓冲区中读取数据。

    • 每次读取完,判断一下是否为一个完整的应用层数据包。如果是,上层应用层数据包读取完成。

    • 如果不是,那就保留该数据在应用层缓冲区,然后继续从 TCP 缓冲区中读取,直到得到一个完整的应用层数据包为止。

    • 至此,半包问题得以解决。

    • 如果从TCP底层读到了多个应用层数据包,则将整个应用层缓冲区,拆成一个一个的独立的应用层数据包,返回给调用程序。

    • 至此,粘包问题得以解决。

  • Netty常用的拆包器解决粘包拆包问题

    • LengthFieldBasedFrameDecoder:基于长度域的拆包器
    • LineBasedFrameDecoder:行拆包器
    • DelimiterBasedFrameDecoder:基于分隔符拆包器
    • FixedLengthFrameDecoder:定长拆包器

三、序列化接口

public interface CommonSerializer {

	static CommonSerializer getByCode(int code) {
		switch (code) {
			case 0:
				return new KryoSerializer();
			case 1:
				return new JsonSerializer();
			case 2:
				return new HessianSerializer();
			default:
				return null;
		}
	}

	byte[] serialize(Object obj);

	Object deserialize(byte[] bytes, Class<?> clazz);

	int getCode();
}
  • 序列化
  • 反序列化
  • 获取序列化器的编号
  • 根据编号获取序列化器

1、JsonSerializer

/**
 * 使用JSON格式的序列化器
 * @ClassName: JsonSerializer
 * @Author: whc
 * @Date: 2021/05/29/20:51
 */
public class JsonSerializer implements CommonSerializer{

	private static final Logger logger = LoggerFactory.getLogger(JsonSerializer.class);

	private ObjectMapper objectMapper = new ObjectMapper();

	@Override
	public byte[] serialize(Object obj) {
		try {
			// 对象转byte数组
			return objectMapper.writeValueAsBytes(obj);
		} catch (JsonProcessingException e) {
			logger.error("序列化时有错误发生:", e);
			throw new SerializeException("序列化时有错误发生");
		}
	}

	@Override
	public Object deserialize(byte[] bytes, Class<?> clazz) {
		try {
			// byte数组转对象
			Object obj = objectMapper.readValue(bytes, clazz);
			if(obj instanceof RpcRequest) {
				obj = handleRequest(obj);
			}
			return obj;
		} catch (IOException e) {
			logger.error("序列化时有错误发生:", e);
			throw new SerializeException("序列化时有错误发生");
		}
	}

	/**
	 *  由于这里使用JSON序列化和反序列化时Object数组,无法保证反序列化时后仍然为原实例类型
	 *  需要重新判断处理
	 */
	private Object handleRequest(Object obj) throws IOException {
		RpcRequest rpcRequest = (RpcRequest)obj;
		for (int i = 0; i < rpcRequest.getParamTypes().length; i++) {
			Class<?> clazz = rpcRequest.getParamTypes()[i];
			if(!clazz.isAssignableFrom(rpcRequest.getParameters()[i].getClass())) {
				byte[] bytes = objectMapper.writeValueAsBytes(rpcRequest.getParameters()[i]);
				rpcRequest.getParameters()[i] = objectMapper.readValue(bytes, clazz);
			}
		}
		return rpcRequest;
	}

	@Override
	public int getCode() {
		return SerializerCode.valueOf("JSON").getCode();
	}
}

2、HessianSerializer

public class HessianSerializer implements CommonSerializer {

	private static final Logger logger = LoggerFactory.getLogger(HessianSerializer.class);

	@Override
	public byte[] serialize(Object obj) {
		HessianOutput hessianOutput = null;
		try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
			hessianOutput = new HessianOutput(byteArrayOutputStream);
			hessianOutput.writeObject(obj);
			return byteArrayOutputStream.toByteArray();
		} catch (IOException e) {
			logger.error("序列化时有错误发生", e);
			throw new SerializeException("序列化时有错误发生");
		} finally {
			if(hessianOutput != null) {
				try {
					hessianOutput.close();
				} catch (IOException e) {
					logger.error("关闭流时有错误发生:", e);
				}
			}
		}
	}

	@Override
	public Object deserialize(byte[] bytes, Class<?> clazz) {
		HessianInput hessianInput = null;
		try(ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) {
			hessianInput = new HessianInput(byteArrayInputStream);
			return hessianInput.readObject();
		} catch (IOException e) {
			logger.error("序列化时有错误发生:", e);
			throw new SerializeException("序列化时有错误发生");
		} finally {
			if(hessianInput != null) {
				hessianInput.close();
			}
		}
	}

	@Override
	public int getCode() {
		return SerializerCode.valueOf("HESSIAN").getCode();
	}
}

3、KryoSerializer

public class KryoSerializer implements CommonSerializer {

	private static final Logger logger = LoggerFactory.getLogger(KryoSerializer.class);

	private static final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
		Kryo kryo = new Kryo();
		kryo.register(RpcResponse.class);
		kryo.register(RpcRequest.class);
		kryo.setReferences(true);
		kryo.setRegistrationRequired(false);
		return kryo;
	});

	@Override
	public byte[] serialize(Object obj) {
		try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
			Output output = new Output(byteArrayOutputStream);
			Kryo kryo = kryoThreadLocal.get();
			kryo.writeObject(output, obj);
			kryoThreadLocal.remove();
			return output.toBytes();
		} catch (Exception e) {
			logger.error("序列化时有错误发生:", e);
			throw new SerializeException("序列化时有错误发生");
		}
	}

	@Override
	public Object deserialize(byte[] bytes, Class<?> clazz) {
		try(ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) {
			Input input = new Input(byteArrayInputStream);
			Kryo kryo = kryoThreadLocal.get();
			Object o = kryo.readObject(input, clazz);
			kryoThreadLocal.remove();
			return o;
		} catch (Exception e) {
			logger.error("序列化时有错误发生:", e);
			throw new SerializeException("序列化时有错误发生");
		}
	}

	@Override
	public int getCode() {
		return SerializerCode.valueOf("KRYO").getCode();
	}
}

四、测试结果

服务端

image-20210608021646918

客户端

image-20210608021709363

五、版本二特点

优点:

  • 使用Netty实现了客户端与服务端的通信
  • 自定义了消息格式,使之支持多种消息类型、序列化方式(Json、Hessian、Kryo)
  • 使用Netty的拆包器解决了粘包问题

缺点:

  • 服务端和客户端通信的host和port预先就必须知道,每一个客户端都必须知道对应的ip和端口号,并且如果服务挂了或者地址换了,就很麻烦。

待解决:

  • 服务器注册与发现的实现用zookeeper作为注册中心
  • 负载均衡的策略的实现
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要基于 Netty 实现一个 SOCKS5 服务器,可以按照以下步骤进行: 1. 创建一个 Netty 的 ServerBootstrap 对象,并设置其相关属性,例如监听端口号、处理器等。 2. 在处理器中实现 SOCKS5 协议的解析和处理。对于 SOCKS5 协议,客户端会发送一个 Greeting 消息,服务器需要回复一个 Greeting 消息确认连接。然后客户端会发送一个请求,包括请求类型、目标地址和端口等信息,服务器需要根据请求类型进行相应的处理,例如连接目标地址和端口、绑定到指定的地址和端口等。 3. 在处理器中实现数据的转发,当客户端和目标服务器建立连接后,服务器需要将数据从客户端转发给目标服务器,然后将目标服务器返回的数据转发给客户端。 下面是一个简单的示例代码: ```java public class Socks5Server { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new Socks5ServerEncoder()); pipeline.addLast(new Socks5InitialRequestDecoder()); pipeline.addLast(new Socks5ServerHandler()); } }); ChannelFuture future = bootstrap.bind(1080).sync(); future.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } ``` 在上面的代码中,创建了一个 ServerBootstrap 对象,并设置了监听端口号为 1080,处理器为 Socks5ServerHandler。Socks5ServerHandler 实现了 SOCKS5 协议的解析和处理,以及数据的转发。 需要注意的是,这只是一个简单的示例代码,实际使用中可能需要根据具体需求进行扩展和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值