普通Socket客户端、Netty服务器进行通信

Netty作为服务器,利用普通的socket进行调用时,服务器能收到客户端的请求信息,但是客户端收不到响应。最终定位到问题是:inputstram的read()方法被阻塞了,所以收不到响应信息。

解决方法:
在利用socket通信时,一般都会在报文内容前面添加10位数字(报文长度),先read()响应报文的前10位长度,然后根据长度去读取。这样就不会一直阻塞了。详细内容看代码:

服务端:

public class SocketServer {

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

    @Value("${server.port}")
    private String port;

    @PostConstruct
    public void init() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                bind();
            }
        }).start();
    }

    private void bind() {
        try {
            EventLoopGroup bossGroup = new NioEventLoopGroup(2); //bossGroup就是parentGroup,是负责处理TCP/IP连接的
            EventLoopGroup workerGroup = new NioEventLoopGroup(100); //workerGroup就是childGroup,是负责处理Channel(通道)的I/O事件

            ServerBootstrap sb = new ServerBootstrap();
            sb.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128) //初始化服务端可连接队列,指定了队列的大小128
                    .childOption(ChannelOption.SO_KEEPALIVE, false) //短连接
                    .childHandler(new ChannelInitializer<SocketChannel>() {  // 绑定客户端连接时候触发操作
                        @Override
                        protected void initChannel(SocketChannel sh) throws Exception {
                            sh.pipeline()
                                .addLast(new MyDecoder(Charset.forName("UTF8"))) //解码request
                                .addLast(new MyEncoder(Charset.forName("UTF8")))
                                .addLast(new MyServerHandler()); //使用ServerHandler类来处理接收到的消息
                        }
                    });
            //绑定监听端口,调用sync同步阻塞方法等待绑定操作完
            ChannelFuture future = sb.bind(Integer.parseInt(port)).sync();
            future.channel().closeFuture().sync();
        }catch (Exception e){
            logger.error(e.getMessage(),e);
        }
    }
}

class MyDecoder extends StringDecoder {

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

    private Charset charset;

    public MyDecoder(Charset charset) {
        this.charset = charset;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
		private StringBuffer sb = new StringBuffer();
        String data = in.toString(charset);
        try {
			if (data != null && data.length() >= 10) {
                data = data.substring(10);//去掉10位长度
            }
            sb.append(data);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        out.add(sb.toString());
    }
}




class MyEncoder extends MessageToByteEncoder {

    private Charset charset;

    public MyEncoder(Charset charset) {
        this.charset = charset;
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) {
        out.writeBytes(setLength((String) msg).getBytes(charset));
    }

    private String setLength(String str) {
        // 完整报文:10位长度 + 报文内容
        return String.format("%010d", str.getBytes(charset).length) + str;
    }

}


class MyServerHandler extends SimpleChannelInboundHandler<String> {
 
    private static final Logger logger = LoggerFactory.getLogger(AcceptMsgEntryBySocket.class);

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String requestContent) throws Exception {
		String response = "hello,world!";
		System.out.println("请求数据:"+ requestContent);
		channelHandlerContext.writeAndFlush(response); 
		System.out.println("响应数据:"+ response);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.writeAndFlush(cause);
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
		
    }
}

客户端:

import org.apache.commons.io.FileUtils;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;

public class SocketClient {

    public static void main(String[] args) throws IOException {

        String ip = "127.0.0.1";
        int port = 8088;
        String charSetType = "UTF-8";
        String reqData = FileUtils.readFileToString(new File("src/test/java/socket请求报文.json"), charSetType);

        OutputStream out = null; // 写
        InputStream in = null; // 读
        String respData = ""; // 响应报文
        Socket socket = new Socket(); // 客户机
        ByteArrayOutputStream bytesOut = null;
        try {
            socket.setTcpNoDelay(true);//关闭socket的缓冲,将数据立即发送出去
            socket.setReuseAddress(true);
            socket.setSoLinger(true, 0);
            socket.setSendBufferSize(32*1024);//发送缓存区
            socket.setReceiveBufferSize(32*1024);//接收缓存区
            socket.setKeepAlive(false);//短连接
            socket.connect(new InetSocketAddress(ip, port), 5000);
            /**
             * 发送TCP请求
             */
            out = socket.getOutputStream();
            String requestData = setLength(reqData, charSetType);
            System.out.println("请求数据:" + requestData);
            out.write(requestData.getBytes(charSetType));
            out.flush();

            /**
             * 接收TCP响应
             */
            socket.setSoTimeout(30000);//socket调用InputStream读数据的超时时间,以毫秒为单位
            in = socket.getInputStream();
            //1、读取数据长度
            byte[] lenBytes = new byte[10];
            in.read(lenBytes);
            Integer length = Integer.valueOf(new String(lenBytes));
            System.out.println("返回数据长度:" + length);

            //2、读取报文数据
            byte[] data = new byte[1024 * 4];
            int len = 0;
            bytesOut = new ByteArrayOutputStream();
            Integer dataLen = 0;
            while ((len = in.read(data)) != -1) {
                bytesOut.write(data, 0, len);
                dataLen = bytesOut.toByteArray().length;

                if(length.equals(dataLen)){
                    break;
                }
            }
            System.out.println("返回数据:" + bytesOut.toString(charSetType));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static String setLength(String str, String charSetType) throws UnsupportedEncodingException {
        // 完整报文:10位长度 + 报文内容
        return String.format("%010d", str.getBytes(charSetType).length) + str;
    }

}

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值