netty客户端与服务端连接后的通信 300C

最近做的项目学习netty,时间紧来不及学,希望请教大家一些问题,
1.netty的客户端与服务端建立长连接后,客户端如何与服务端通信?要随心所欲发送自定义的消息?
2.netty的自定义编码器,解码器,希望也能得到帮助,
3.第一个问题为主要问题。

数据包结构:
包头:2字节 0ddd 0fff。
消息体:全部为文本(ASCII码),汉字是GB2312编码。
包尾:2字节 0xxx 0aaa。

最后,先感谢每一位浏览解答的朋友,我会多多赠送C币给那些乐于帮助的人。

补充一些:
我是客户端,需要与服务端长连接;接通后需要发送登陆消息数据包;
每30秒发送一条链路数据

这些我熬夜两天,时间太紧张,希望大家不吝指教。

9个回答

客户端代码:
public class TCPClient {

    public void connect(int port,String host)throws Exception{
        //网络事件处理线程组
        EventLoopGroup group=new NioEventLoopGroup();
        try{
            //配置客户端启动类
            Bootstrap b=new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)//设置封包 使用一次大数据的写操作,而不是多次小数据的写操作
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new DealMsg());//设置自定义解码器
                            ch.pipeline().addLast(new TCPClientHandler());//设置客户端网络IO处理器
                        }
                    });
            //连接服务器 同步等待成功
            ChannelFuture f=b.connect(host,port).sync();

            //同步等待客户端通道关闭
            f.channel().closeFuture().sync();
        }finally{
            //释放线程组资源
            group.shutdownGracefully();
        }
    }

}

客户端handler

public class TCPClientHandler extends ChannelHandlerAdapter {

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    System.out.println("通信异常!!");
    cause.printStackTrace();
}

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println("链接服务端成功!");
}

@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    System.out.println("退出链接!!");
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

    System.out.println("接受服务器数据:=="+msg);

}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    ctx.channel().writeAndFlush("数据读取完成!");
}

}

  首先,我们定义消息类型:



[java] view plain copy
/** 
 * 消息类型 
 * @author 李熠 
 * 
 */  
public enum MsgType {  

    PING,SEND,LOGIN,NO_TARGET  
}  
分别是心跳、发送、登录、找不到目标
当客户端和服务端连接后,需要向服务端发送登录请求,也就是消息类型:LOGIN,服务端接收到LOGIN请求后,会将客户端加入到队列中,

[java] view plain copy
import java.io.Serializable;  

public class Message implements Serializable {  

    private static final long serialVersionUID = -5756901646411393269L;  

    private String clientId;//发送者客户端ID  

    private MsgType type;//消息类型  

    private String data;//数据  

    private String targetId;//目标客户端ID  

    public String getTargetId() {  
        return targetId;  
    }  

    public void setTargetId(String targetId) {  
        this.targetId = targetId;  
    }  

    public String getClientId() {  
        return clientId;  
    }  

    public void setClientId(String clientId) {  
        this.clientId = clientId;  
    }  

    public MsgType getType() {  
        return type;  
    }  

    public void setType(MsgType type) {  
        this.type = type;  
    }  

    public String getData() {  
        return data;  
    }  

    public void setData(String data) {  
        this.data = data;  
    }  

    public Message(){  

    }  

    public Message(MsgType type){  
        this.type = type;  
    }  
}  
这类是定义的消息Bean,想服务端发送消息就是发送的这个对象的数据。
接下来,实现客户端队列代码:

[java] view plain copy
import io.netty.channel.Channel;  
import io.netty.channel.socket.SocketChannel;  

import java.util.Map;  
import java.util.concurrent.ConcurrentHashMap;  

public class NettyChannelMap {  

    private static Map<String , SocketChannel> map = new ConcurrentHashMap<>();  

    public static void add(String clientId,SocketChannel channel){  
        map.put(clientId, channel);  
    }  

    public static Channel get(String clientId){  
        return map.get(clientId);  
    }  

    public static void remove(SocketChannel channel){  
        for (Map.Entry<String,SocketChannel> entry:map.entrySet()){  
            if (entry.getValue()==channel){  
                map.remove(entry.getKey());  
            }  
        }  
    }  

}  
服务端:
[java] view plain copy
import io.netty.bootstrap.ServerBootstrap;  
import io.netty.channel.ChannelFuture;  
import io.netty.channel.ChannelInitializer;  
import io.netty.channel.ChannelOption;  
import io.netty.channel.ChannelPipeline;  
import io.netty.channel.EventLoopGroup;  
import io.netty.channel.nio.NioEventLoopGroup;  
import io.netty.channel.socket.SocketChannel;  
import io.netty.channel.socket.nio.NioServerSocketChannel;  
import io.netty.handler.codec.string.StringDecoder;  
import io.netty.handler.codec.string.StringEncoder;  

import java.nio.charset.Charset;  

public class NettyServer {  

    private int port;  
    public SocketChannel socketChannel;  
    public NettyServer(int port) throws InterruptedException {  
        this.port = port;  
        bind();  
    }  

    private void bind() throws InterruptedException {  
        EventLoopGroup boss=new NioEventLoopGroup();  
        EventLoopGroup worker=new NioEventLoopGroup();  
        ServerBootstrap bootstrap=new ServerBootstrap();  
        bootstrap.group(boss,worker);  
        bootstrap.channel(NioServerSocketChannel.class);  
        bootstrap.option(ChannelOption.SO_BACKLOG, 128);  
        //通过NoDelay禁用Nagle,使消息立即发出去,不用等待到一定的数据量才发出去  
        bootstrap.option(ChannelOption.TCP_NODELAY, true);  
        //保持长连接状态  
        bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);  
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {  
            @Override  
            protected void initChannel(SocketChannel socketChannel) throws Exception {  
                ChannelPipeline p = socketChannel.pipeline();  
                //字符串类解析  
[java] view plain copy
<span style="white-space:pre;">     </span>//这里只能添加字符串的编码和解码器,  
[java] view plain copy
<span style="white-space:pre;">     </span>//网上有很多例子是这样写的:  
[java] view plain copy
<span style="white-space:pre;">     </span>//这种写法只能所有客户端都用netty写,否则其他框架实现的客户端无法发送消息到服务端,因为他是转换的netty自己的Object  
[java] view plain copy
<span style="white-space:pre;">     </span>//p.addLast(new ObjectEncoder());  
          //p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));    
[java] view plain copy
            p.addLast(new StringEncoder(Charset.forName("UTF-8")));  
            p.addLast(new StringDecoder(Charset.forName("UTF-8")));  
            p.addLast(new NettyServerHandler());  
        }  
    });  
    ChannelFuture f= bootstrap.bind(port).sync();  
    if(f.isSuccess()){  
        System.out.println("server start---------------");  
    }  
}  
public static void main(String []args) throws InterruptedException {  
    if(args.length == 0){  
        new NettyServer(9999);  
    }else{  
        new NettyServer(Integer.parseInt(args[0]));  
    }  
}  

[java] view plain copy
import io.netty.channel.ChannelHandlerContext;  
import io.netty.channel.SimpleChannelInboundHandler;  
import io.netty.channel.socket.SocketChannel;  
import cn.sunsharp.netty.bean.Message;  
import cn.sunsharp.netty.bean.MsgType;  
import cn.sunsharp.netty.bean.NettyChannelMap;  

import com.alibaba.fastjson.JSON;  
[java] view plain copy
//最好继承  
[java] view plain copy
SimpleChannelInboundHandler<String>表示传递字符串消息,handler会把json格式的字符串转换为Message对象  
public class NettyServerHandler extends SimpleChannelInboundHandler<String>{@Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { //channel失效,从Map中移除 NettyChannelMap.remove((SocketChannel)ctx.channel()); }@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)throws Exception {//cause.printStackTrace();System.out.println("出现异常!");}@Overrideprotected void messageReceived(ChannelHandlerContext ctx, String msg)throws Exception {System.out.println(msg);Message message = JSON.parseObject(msg+"", Message.class);System.out.println("接收到消息:"+message);String clientId = message.getClientId();if(MsgType.LOGIN.equals(message.getType())){System.out.printf("将%s添加到队列\n",clientId); NettyChannelMap.add(clientId,(SocketChannel)ctx.channel()); }else{ if(NettyChannelMap.get(clientId)==null){ System.out.printf("登录失败,请重新登录!",clientId); //说明未登录,或者连接断了,服务器向客户端发起登录请求,让客户端重新登录 message = new Message(MsgType.LOGIN); ctx.channel().writeAndFlush(JSON.toJSONString(message)); } } switch (message.getType()){ case PING:{ message = new Message(MsgType.PING); NettyChannelMap.get(clientId).writeAndFlush(JSON.toJSONString(message)); }break; case SEND:{ //收到客户端的请求,发送给targetId System.out.println("发送消息:"+message); if(NettyChannelMap.get(message.getTargetId()) != null){ NettyChannelMap.get(message.getTargetId()).writeAndFlush(JSON.toJSONString(message)); }else{ message.setType(MsgType.NO_TARGET); NettyChannelMap.get(clientId).writeAndFlush(JSON.toJSONString(message)); } }break; default:break; }}}客户端可以使用任何框架任何语言的Socket来连接并发送消息,为了方便,这里依然用Netty来实现客户端:
[java] view plain copy
import java.nio.charset.Charset;  

import io.netty.bootstrap.Bootstrap;  
import io.netty.channel.ChannelFuture;  
import io.netty.channel.ChannelInitializer;  
import io.netty.channel.ChannelOption;  
import io.netty.channel.EventLoopGroup;  
import io.netty.channel.nio.NioEventLoopGroup;  
import io.netty.channel.socket.SocketChannel;  
import io.netty.channel.socket.nio.NioSocketChannel;  
import io.netty.handler.codec.serialization.ClassResolvers;  
import io.netty.handler.codec.serialization.ObjectDecoder;  
import io.netty.handler.codec.serialization.ObjectEncoder;  
import io.netty.handler.codec.string.StringDecoder;  
import io.netty.handler.codec.string.StringEncoder;  
import io.netty.handler.timeout.IdleStateHandler;  
import io.netty.util.concurrent.DefaultEventExecutorGroup;  
import io.netty.util.concurrent.EventExecutorGroup;  
import cn.sunsharp.regulation.bean.Message;  
import cn.sunsharp.regulation.bean.MsgType;  

import com.alibaba.fastjson.JSON;  

public class NettyClient {  

    private int port;  
    private String host;  
    public SocketChannel socketChannel;  
    private static final EventExecutorGroup group = new DefaultEventExecutorGroup(20);  
    public NettyClient(int port, String host) {  
        this.port = port;  
        this.host = host;  
        start();  
    }  
    private void start(){  
        ChannelFuture future = null;  
        try {  
            EventLoopGroup eventLoopGroup=new NioEventLoopGroup();  
            Bootstrap bootstrap=new Bootstrap();  
            bootstrap.channel(NioSocketChannel.class);  
            bootstrap.option(ChannelOption.SO_KEEPALIVE,true);  
            bootstrap.group(eventLoopGroup);  
            bootstrap.remoteAddress(host,port);  
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {  
                @Override  
                protected void initChannel(SocketChannel socketChannel) throws Exception {  
                    socketChannel.pipeline().addLast(new IdleStateHandler(20,10,0));  
                    socketChannel.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));  
                    socketChannel.pipeline().addLast(new StringDecoder(Charset.forName("UTF-8")));  
                    socketChannel.pipeline().addLast(new NettyClientHandler());  
                }  
            });  
            future =bootstrap.connect(host,port).sync();  
            if (future.isSuccess()) {  
                socketChannel = (SocketChannel)future.channel();  
                System.out.println("connect server  成功---------");  
            }else{  
                System.out.println("连接失败!");  
                System.out.println("准备重连!");  
                start();  
            }  
        } catch (Exception e) {  

        }finally{  
//          if(null != future){  
//              if(null != future.channel() && future.channel().isOpen()){  
//                  future.channel().close();  
//              }  
//          }  
//          System.out.println("准备重连!");  
//          start();  
        }  
    }  
    public static void main(String[]args) throws InterruptedException {  
        NettyClient bootstrap=new NettyClient(9999,"192.168.1.38");  
        System.out.println(11111);  
        Message loginMsg=new Message(MsgType.LOGIN);  
        loginMsg.setClientId("001");  
        loginMsg.setTargetId("192.168.1.38");  
        loginMsg.setType(MsgType.LOGIN);  
        bootstrap.socketChannel.writeAndFlush(JSON.toJSON(loginMsg));  
    }   
}  

[java] view plain copy
import io.netty.channel.ChannelHandlerAdapter;  
import io.netty.channel.ChannelHandlerContext;  
import io.netty.channel.SimpleChannelInboundHandler;  
import io.netty.handler.timeout.IdleStateEvent;  
import cn.sunsharp.regulation.bean.Message;  
import cn.sunsharp.regulation.bean.MsgType;  

import com.alibaba.fastjson.JSON;  

public class NettyClientHandler extends SimpleChannelInboundHandler<String> {  

    public static ChannelHandlerContext context = null;  

    //利用写空闲发送心跳检测消息  
    @Override  
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {  
        if (evt instanceof IdleStateEvent) {  
            IdleStateEvent e = (IdleStateEvent) evt;  
            switch (e.state()) {  
                case WRITER_IDLE:  
                    Message pingMsg=new Message(MsgType.PING);  
                    ctx.writeAndFlush(JSON.toJSON(pingMsg));  
                    System.out.println("send ping to server----------");  
                    break;  
                default:  
                    break;  
            }  
        }  
    }  

    @Override  
    protected void messageReceived(ChannelHandlerContext ctx, String msg)  
            throws Exception {  
        Message message = JSON.parseObject(msg+"", Message.class);  
        MsgType msgType=message.getType();  
        switch (msgType){  
            case LOGIN:{  
                //向服务器发起登录  
                message = new Message(MsgType.LOGIN);  
                ctx.writeAndFlush(JSON.toJSONString(message));  
            }break;  
            case PING:{  
                System.out.println("receive ping from server----------");  
            }break;  
            case SEND:{  
                //收到服务端消息  
                System.out.println("收到服务端消息:"+message.getData());  
            }break;  
            case NO_TARGET:{  
                //收到服务端消息  
                System.out.println("找不到targetId:"+message.getTargetId());  
            }break;  
            default:break;  
        }  
    }  
} 

Netty是建立在NIO基础之上,Netty在NIO之上又提供了更高层次的抽象。

在Netty里面,Accept连接可以使用单独的线程池去处理,读写操作又是另外的线程池来处理。

Accept连接和读写操作也可以使用同一个线程池来进行处理。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。线程池中的每一个线程都是NIO线程。用户可以根据实际情况进行组装,构造出满足系统需求的并发模型。

Netty提供了内置的常用编解码器,包括行编解码器[一行一个请求],前缀长度编解码器[前N个字节定义请求的字节长度],可重放解码器[记录半包消息的状态],HTTP编解码器,WebSocket消息编解码器等等

Netty提供了一些列生命周期回调接口,当一个完整的请求到达时,当一个连接关闭时,当一个连接建立时,用户都会收到回调事件,然后进行逻辑处理。

Netty可以同时管理多个端口,可以使用NIO客户端模型,这些对于RPC服务是很有必要的。

Netty除了可以处理TCP Socket之外,还可以处理UDP Socket。

在消息读写过程中,需要大量使用ByteBuffer,Netty对ByteBuffer在性能和使用的便捷性上都进行了优化和抽象。

代码:

服务端:

复制代码
package com.kinson.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**

  • descripiton:服务端 *
  • @author: www.iknowba.cn
  • @date: 2018/3/23
  • @time: 15:37
  • @modifier:
  • @since:
    /
    public class NettyServer {
    /
    *

    • 端口 */ private int port;

    public NettyServer(int port) {
    this.port = port;
    }

    public void run() {
    //EventLoopGroup是用来处理IO操作的多线程事件循环器
    //负责接收客户端连接线程
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    //负责处理客户端i/o事件、task任务、监听任务组
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    //启动 NIO 服务的辅助启动类
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(bossGroup, workerGroup);
    //配置 Channel
    bootstrap.channel(NioServerSocketChannel.class);
    bootstrap.childHandler(new ServerIniterHandler());
    //BACKLOG用于构造服务端套接字ServerSocket对象,
    // 标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度
    bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
    //是否启用心跳保活机制
    bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
    try {
    //绑定服务端口监听
    Channel channel = bootstrap.bind(port).sync().channel();
    System.out.println("server run in port " + port);
    //服务器关闭监听
    /*channel.closeFuture().sync()实际是如何工作:
    channel.closeFuture()不做任何操作,只是简单的返回channel对象中的closeFuture对象,对于每个Channel对象,都会有唯一的一个CloseFuture,用来表示关闭的Future,
    所有执行channel.closeFuture().sync()就是执行的CloseFuturn的sync方法,从上面的解释可以知道,这步是会将当前线程阻塞在CloseFuture上*/
    channel.closeFuture().sync();
    } catch (InterruptedException e) {
    e.printStackTrace();
    } finally {
    //关闭事件流组
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
    }
    }

    public static void main(String[] args) {
    new NettyServer(8899).run();
    }
    }
    复制代码
    服务端业务逻辑处理:

复制代码
package com.kinson.netty.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**

  • descripiton: 服务器的处理逻辑 *
  • @author: www.iknowba.cn
  • @date: 2018/3/23
  • @time: 15:50
  • @modifier:
  • @since:
    */
    public class ServerHandler extends SimpleChannelInboundHandler {

    /**

    • 所有的活动用户 */ public static final ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**

    • 读取消息通道 *
    • @param context
    • @param s
    • @throws Exception */ @Override protected void channelRead0(ChannelHandlerContext context, String s) throws Exception { Channel channel = context.channel(); //当有用户发送消息的时候,对其他的用户发送消息 for (Channel ch : group) { if (ch == channel) { ch.writeAndFlush("[you]: " + s + "\n"); } else { ch.writeAndFlush("[" + channel.remoteAddress() + "]: " + s + "\n"); } } System.out.println("[" + channel.remoteAddress() + "]: " + s + "\n"); }

    /**

    • 处理新加的消息通道 *
    • @param ctx
    • @throws Exception */ @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); for (Channel ch : group) { if (ch == channel) { ch.writeAndFlush("[" + channel.remoteAddress() + "] coming"); } } group.add(channel); }

    /**

    • 处理退出消息通道 *
    • @param ctx
    • @throws Exception */ @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); for (Channel ch : group) { if (ch == channel) { ch.writeAndFlush("[" + channel.remoteAddress() + "] leaving"); } } group.remove(channel); }

    /**

    • 在建立连接时发送消息 *
    • @param ctx
    • @throws Exception */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); boolean active = channel.isActive(); if (active) { System.out.println("[" + channel.remoteAddress() + "] is online"); } else { System.out.println("[" + channel.remoteAddress() + "] is offline"); } ctx.writeAndFlush("[server]: welcome"); }

    /**

    • 退出时发送消息 *
    • @param ctx
    • @throws Exception */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); if (!channel.isActive()) { System.out.println("[" + channel.remoteAddress() + "] is offline"); } else { System.out.println("[" + channel.remoteAddress() + "] is online"); } }

    /**

    • 异常捕获 *
    • @param ctx
    • @param e
    • @throws Exception */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception { Channel channel = ctx.channel(); System.out.println("[" + channel.remoteAddress() + "] leave the room"); ctx.close().sync(); }

}
复制代码
服务端处理器注册:

复制代码
package com.kinson.netty.server;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**

  • descripiton: 服务器初始化 *
  • @author: www.iknowba.cn
  • @date: 2018/3/23
  • @time: 15:46
  • @modifier:
  • @since: */ public class ServerIniterHandler extends ChannelInitializer { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //管道注册handler ChannelPipeline pipeline = socketChannel.pipeline(); //编码通道处理 pipeline.addLast("decode", new StringDecoder()); //转码通道处理 pipeline.addLast("encode", new StringEncoder()); //聊天服务通道处理 pipeline.addLast("chat", new ServerHandler()); } } 复制代码 客户端:

复制代码
package com.kinson.netty.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**

  • descripiton: 客户端 *
  • @author: www.iknowba.cn
  • @date: 2018/3/23
  • @time: 16:40
  • @modifier:
  • @since:
    */
    public class NettyClient {

    private String ip;

    private int port;

    private boolean stop = false;

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

    public void run() throws IOException {
    //设置一个多线程循环器
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    //启动附注类
    Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(workerGroup);
    //指定所使用的NIO传输channel
    bootstrap.channel(NioSocketChannel.class);
    //指定客户端初始化处理
    bootstrap.handler(new ClientIniterHandler());
    try {
    //连接服务
    Channel channel = bootstrap.connect(ip, port).sync().channel();
    while (true) {
    //向服务端发送内容
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String content = reader.readLine();
    if (StringUtils.isNotEmpty(content)) {
    if (StringUtils.equalsIgnoreCase(content, "q")) {
    System.exit(1);
    }
    channel.writeAndFlush(content);
    }
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    System.exit(1);
    } finally {
    workerGroup.shutdownGracefully();
    }
    }

    public static void main(String[] args) throws Exception {
    new NettyClient("127.0.0.1", 8899).run();
    }
    }
    复制代码
    客户端逻辑处理:

复制代码
package com.kinson.netty.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**

  • descripiton: 客户端逻辑处理 *
  • @author: www.iknowba.cn
  • @date: 2018/3/23
  • @time: 16:50
  • @modifier:
  • @since: */ public class ClientHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception { //打印服务端的发送数据 System.out.println(s); } } 复制代码 客户端处理器注册:

复制代码
package com.kinson.netty.client;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**

  • descripiton: 客户端处理初始化 *
  • @author: www.iknowba.cn
  • @date: 2018/3/23
  • @time: 16:55
  • @modifier:
  • @since: */ public class ClientIniterHandler extends ChannelInitializer { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //注册管道 ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); pipeline.addLast("http", new HttpClientCodec()); pipeline.addLast("chat", new ClientHandler()); } }

非常感谢上面的回复,我马上验证。最迟明天。也希望其他大佬多指点后生,学习的路上永无止境,我也会把这种精神传递下去

再详细补充一些:

我在做的是TCP通信。

我需要链接服务端后,发送登录请求,请求的数据必须上面数据包格式,希望大家帮忙。。。。。目前并不能实现

Netty提供了一些列生命周期回调接口,当一个完整的请求到达时,当一个连接关闭时,当一个连接建立时,用户都会收到回调事件,然后进行逻辑处理。

helloworld_xwb
helloworld_xwb 时间紧张,一天需要从陌生到熟悉做出来一套通信方案,你说的这些我都不会啊,正在学;
一年多之前 回复

试试这个https://blog.csdn.net/qq_27903845/article/details/79453412 应该符合你的要求

helloworld_xwb
helloworld_xwb 感谢您的关注,我看了看代码,还是有所不同,最核心的问题不能解决
一年多之前 回复

1.连接后,你要发送登录请求, 2 自定义协议,自己开发个编解码就行,

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
其他相关推荐
Netty服务端与客户端通信
最近开始学习Netty, 服务端: public class TimeServer { public void bind(int port) throws Exception { /** * NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器, * Netty提供了许多不同的EventLoopGroup的...
netty实现一个简单的服务端和客户端通信
netty java Nio网络通信框架,能接收TCP,http,webSocket,UDPelk包括:logStash,Elasticsearch,KibanaLogStash:tcp监听netty服务器发送的信息,并把消息处理好后转发给ESElasticsearch:接受LogStash发送来的数据,并进行分词构建索引。kibana:主要是从ES中获取相关索引的数据进行检索,分析。默认端口56...
Netty实现简单的客户端服务端通信示例
Netty实现简单的客户端服务端通信示例,户端发送请求给服务端,并由服务端响应客户端请求,希望对初学Netty的同学有所帮助。
Netty框架服务端主动向客户端通信
项目场景:一个车联网项目,很多很多台车辆会定时像服务端发包,所以都是使用TCP长连接的方式。现在有一个需求是,服务端需要向客户端主动的发送升级命令,由于使用的事件驱动的Netty框架,我们怎么才能由服务端主动的向客服端通信呢?解决方案:package cn.ac.yangge.domain;import io.netty.channel.Channel;import java.util.HashMa
Netty实现客户端和服务端通信简单例子
Netty是建立在NIO基础之上,Netty在NIO之上又提供了更高层次的抽象。在Netty里面,Accept连接可以使用单独的线程池去处理,读写操作又是另外的线程池来处理。Accept连接和读写操作也可以使用同一个线程池来进行处理。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。线程池中的每一个线程都是NIO线程。用户可以根据实际情况进行组装,构造出满足系统需求的并发...
Netty(三) Netty客户端+服务端
Netty(三) Netty客户端+服务端 Netty服务端和客户端处理字符串的消息的简单例子 服务端 服务端启动类: package com.zqw.nio.netty.n2; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; i...
netty客户端与服务端例子
  package com.snailteam.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEv...
【Netty】服务端和客户端
欢迎关注公众号:【爱编程】 如果有需要后台回复2019赠送1T的学习资料哦!! 本文是基于Netty4.1.36进行分析 服务端 Netty服务端的启动代码基本都是如下: private void start() throws Exception { final EchoServerHandler serverHandler = new EchoServerHandler(...
Netty 客户端与服务端双向通信
2016年,拍摄于台湾省垦丁,这就是太平洋了,风太大了我这个吨位几乎站不住。 微信公众号 王皓的GitHub:https://github.com/TenaciousDWang 现在我们已经学会了如何启动一个Netty的客户端与服务端,接下来,我们来让他俩双向通讯。 客户端发送数据 首先,我们看一下引导类.handler这个位置,之前我们说过这...
netty客户端和服务端通讯
此项目做了netty通讯的客户端和服务端,可以直接运行 注意pom文件需要添加netty依赖
netty客户端服务端
 Netty 是一个基于 JAVA NIO 类库的异步通信框架,它的架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性。 netty服务端 public class NettyServer { public static void main(String[] args) { // 1.创建服务对象 ServerBootstrap serverBootstrap ...
Netty服务端客户端
Netty服务端客户端简单配置
netty 服务端连接分析
一、 NioEventLoopGroup 初始化 NioEventLoopGroup boss 线程和work线程 共享线程池 public ServerBootstrap group(EventLoopGroup group) { return group(group, group); }boss 线程和work线程 拥有线程池 public ServerBo
客户端与服务端通信-客户端请求服务端
本课程从单体应用模型的问题为切入点,以理论配合实战的方式带领大家了解微服务架构的魅力。通过本课程您可以掌握从基本的Linux服务器架设到容器化引擎的自动化部署以及以Spring Boot为核心开发框架的全栈式分布式系统开发解决方案。
UDP 通信服务端如何监听客户端连接
客户端不正常退出,服务端这边怎么知道客户端连接已经断开rn请详细说明下rnprivate void OnReceive(IAsyncResult ar)rn rn tryrn rn IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);rn EndPoint epSender = (EndPoint)ipeSender;rn serverSocket.EndReceiveFrom (ar, ref epSender);rn rn //Transform the array of bytes received from the user into anrn //intelligent form of object Datarn Data msgReceived = new Data(byteData);rnrn //We will send this object in response the users requestrn Data msgToSend = new Data();rnrn byte [] message;rn rn //If the message is to login, logout, or simple text messagern //then when send to others the type of the message remains the samern msgToSend.cmdCommand = msgReceived.cmdCommand;rn msgToSend.strName = msgReceived.strName;rnrn switch (msgReceived.cmdCommand)rn rn case Command.Login:rn rn //When a user logs in to the server then we add her to ourrn //list of clientsrnrn ClientInfo clientInfo = new ClientInfo();rn clientInfo.endpoint = epSender; rn clientInfo.strName = msgReceived.strName; rn clientList.Add(clientInfo);rn rnrn //Set the text of the message that we will broadcast to all usersrn msgToSend.strMessage = "<<<" + msgReceived.strName + " has joined the room>>>"; rn break;rnrn case Command.Logout: rn rn //When a user wants to log out of the server then we search for her rn //in the list of clients and close the corresponding connectionrnrn int nIndex = 0;rn foreach (ClientInfo client in clientList)rn rn if (client.endpoint.ToString() == epSender.ToString())rn rn clientList.RemoveAt(nIndex);rn break;rn rn ++nIndex;rn rn rn msgToSend.strMessage = "<<<" + msgReceived.strName + " has left the room>>>";rn break;rnrn case Command.Message:rnrn //Set the text of the message that we will broadcast to all usersrn tryrn rn string[] msg = msgReceived.strMessage.Split('|');rn msgToSend.strMessage = msgReceived.strName + " said to " + msg[0] + ":" + msg[1];rn rn rn catch (Exception E)rn rn MessageBox.Show(E.Message + "A");rn rn break;rnrn case Command.List:rnrn //Send the names of all users in the chat room to the new userrn msgToSend.cmdCommand = Command.List;rn msgToSend.strName = null;rn msgToSend.strMessage = null;rnrn //Collect the names of the user in the chat roomrn foreach (ClientInfo client in clientList)rn rn //To keep things simple we use asterisk as the marker to separate the user namesrn msgToSend.strMessage += client.strName + "*"; rn rnrn message = msgToSend.ToByte();rnrn //Send the name of the users in the chat roomrn serverSocket.BeginSendTo (message, 0, message.Length, SocketFlags.None, epSender, rn new AsyncCallback(OnSend), epSender); rn break;rn rnrn if (msgToSend.cmdCommand == Command.Message)rn rn message = msgToSend.ToByte();rnrn ////////////////////反馈到客户端rn string[] msg = msgReceived.strMessage.Split('|');rn int count = clientList.Count;rn for (int i = 0; i < count; i++)rn rnrn if (msg[0].CompareTo("public") == 0)rn rn if (((ClientInfo)clientList[i]).endpoint != epSender ||rn msgToSend.cmdCommand != Command.Login)rn rn //Send the message to all usersrn serverSocket.BeginSendTo(message, 0, message.Length, SocketFlags.None, ((ClientInfo)clientList[i]).endpoint,rn new AsyncCallback(OnSend), ((ClientInfo)clientList[i]).endpoint);rn rn rn elsern rn if (msg[0].CompareTo(((ClientInfo)clientList[i]).strName) == 0 || msgReceived.strName.CompareTo(((ClientInfo)clientList[i]).strName) == 0)rn rn //Send the message to all usersrn serverSocket.BeginSendTo(message, 0, message.Length, SocketFlags.None, ((ClientInfo)clientList[i]).endpoint,rn new AsyncCallback(OnSend), ((ClientInfo)clientList[i]).endpoint);rn rn rn rn rn else if (msgToSend.cmdCommand != Command.List) //List messages are not broadcastedrn rn message = msgToSend.ToByte();rnrn ////////////////////反馈到客户端rnrn int count = clientList.Count;rn for (int i = 0; i < count; i++)rn rn if (((ClientInfo)clientList[i]).endpoint != epSender ||rn msgToSend.cmdCommand != Command.Login)rn rn //Send the message to all usersrn serverSocket.BeginSendTo(message, 0, message.Length, SocketFlags.None, ((ClientInfo)clientList[i]).endpoint,rn new AsyncCallback(OnSend), ((ClientInfo)clientList[i]).endpoint);rn rn rnrn txtLog.Text += msgToSend.strMessage + "\r\n";rn rnrn //If the user is logging out then we need not listen from herrn if (msgReceived.cmdCommand != Command.Logout)rn rn //Start listening to the message send by the userrn serverSocket.BeginReceiveFrom (byteData, 0, byteData.Length, SocketFlags.None, ref epSender, rn new AsyncCallback(OnReceive), epSender);rn rn rn catch (Exception ex)rn rn MessageBox.Show(ex.Message, "SGSServerUDP", MessageBoxButtons.OK, MessageBoxIcon.Error); rn rn
TCP连接实现 客户端——服务端 通信
客户端: //客户端 client.c #include &lt;stdio.h&gt; #include &lt;unistd.h&gt; #include &lt;string.h&gt; #include &lt;stdlib.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;sys/socket.h&gt; #inc...
netty 服务端作为客户端跳转请求服务端
一般的请求都是从客户端发起请求到服务端,服务端接收到请求后做相应处理然后在返回给客户端相应信息,但是很多时候我们要用服务端作为客户端在发送请求到另一台服务端,这样中的这台服务器不仅是服务端也是客户端。 OK 整体请求就是这样的,下面来看代码: 一、客户端 1、客户端引导 package com.test3; import io.netty.bootstrap.Bootstrap;
netty 学习笔记(一)客户端与服务端
netty 学习笔记(一)客户端与服务端 1、添加pom依赖 &amp;lt;dependency&amp;gt;    &amp;lt;groupId&amp;gt;io.netty&amp;lt;/groupId&amp;gt;    &amp;lt;artifactId&amp;gt;netty&amp;lt;/artifactId&amp;gt;    &amp;lt;version&amp;gt;3.3.0.Final&amp;lt;/version&amp;gt; &amp;lt;/
netty框架,服务端、客户端代码示例
简单的netty框架示例,代码中有完整的注释,代码包含服务端跟客户端代码,RunServer服务端测试类,RunClient客户端测试类
TCP通信,多客户端通信(客户端、服务端)
1、定义 客户端和服务器间的交流, 客户端发送信息,服务器接收到,并返回信息 长连接 2、使用步骤 客户端 创建Socket连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器 调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流 输入流可以读取服务端输出流写出的数据 输出流可以写出数据到服务端的输入流 服务端 创建...
Netty服务端和客户端调用demo
demo采用spring boot和maven编写,直接使用maven方式打开项目先运行TimeServer中的main函数再运行TimeClient中的main函数即可在控制台中看到效果
Netty(二):服务端客户端实例分析
package com.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty
Netty学习5-Netty3.X服务端与客户端
Server1 import java.net.InetSocketAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.jboss.netty.bootstrap.ServerBootstrap;...
java应用netty服务端和客户端
java应用netty服务端和客户端示例,客户端和服务端的model对象目录必须一致
Netty之客户端/服务端初始化(二)
1初始化机制分析2 实现一个简单列子
netty 服务端和客户端创建流程
netty 服务端创建的流程 1. ServerBootstrap NIO 服务端启动辅助类,设置各种必要的参数 使用builder模式,解决构造函数参数过多并且不确定问题 2. EventLoopGroup selector 线程池 系统中有两个Reactor线程组 服务端用于监听和接收客户端连接的Reactor线程组 处理I/O读写的Reactor线程组 绑定NioS
Netty源码之客户端连接监听和客户端channel创建 服务端视角
在前两篇博客中,我们讲解了服务端的启动和EventLoop相关的源码,接下来,我们看一下服务端是如何监听客户端接入并建立连接的: private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) 当客户端想和服务端建立连接的时候,会被服务端channel绑定的selector轮询到这个io事件,之后就会调用上面这个方法...
客户端与服务端通信
比较安全的通信模式,对用户同时登录,可以自己选择加密方法
Socket服务端和客户端通信
1、客户端Client创建 public class Client {     public static void main(String[] args) {         try {             //1、创建客户端Socket,指定服务器端口号和地址             Socket socket = new Socket("localhost",8080);
客户端与服务端通信的问题
现在我想做一个客户端软件Client和一个服务端软件Server,客户端必须登陆服务器Server才能继续操作。rn所以我在服务端建立2张表,一张用户表和一张用户权限表。rn假设客户端以用户“waley”成功登陆了服务端,并且用户“waley”具有以下操作权限rn 1:添加用户rn 2:删除用户rn 3:修改用户权限rnrn现在希望能通过客户端添加一个用户,所以服务端必须判断客户端发过来的消息,即判断该用户是否具有添加用户的权限。rn客户端发送的消息至少应该包含3部分rn1.用户名rn2.操作命令类型 (比如添加用户操作)rn3.与操作相关的参数信息(比如添加的用户名,密码,权限)rnrn问题:rn1.客户端发送的消息应该以何种形式发送到服务端,是直接发字符串,还是以XML形式发送?如果是XML形式,能否给个通信的例子?rn2.服务端不需要界面,是否可以考虑用java来实现?rn
TCP通信客户端与服务端
实现绑定本机IP的客户端与服务器端,适合面对多网卡学习修改使用。
C#客户端和服务端通信
C#客户端和服务端通信实现功能有传送文件的功能和震动,包括简单与客户端和服务端交流
C++ 客户端与服务端通信
C++ 客户端与服务端之间点对点的通讯 希望能够帮到你们
.Net客户端与服务端通信
[b]背景[/b]:现要用写一个多客户端软件,软件需要录入很多数据到数据库,也需要从数据库获取很多数据展示个用户,需要展示的数据可达到十万级,当然可以分页动态加载。目前是在局域网内使用。rn[b]思路[/b]:如果直接在客户端软件里直接CRUD,按客户量连接池是没问题的,但是一旦软件被反编译了,数据库就暴露了。所以想用(客户端--服务端--数据库)的模式,但是小白(纯自学)以前没做过这类软件。rn[b]请问[/b]:1.客户端与服务端应该用什么通讯方式最好?(请考虑大量表数据的背景,以前都直接用DataTable直接绑到DataGridView[face]qq:41.gif[/face])rn 2.如果要适用外网,又应该用什么通讯方式?rn 3.服务端应该怎样验证身份和权限?rn 4.数据序列化是用JSON或者XML还是有其他方式?rn 5.能否给个Demo?网上的文章地址也可以。奶奶的,我自己一搜,全是Socket聊天程序。rn 6.服务端最好是用.NET的,当然如果有其他明显适合的也可以。rn400分奉上,请大佬写得尽量详细点。[face]monkey:2.gif[/face]rn
python客户端和服务端通信
import socket client=socket.socket(type=socket.SOCK_DGRAM) ip_port=(‘10.10.42.68’,9000) while True: content = input(‘我说:’) content = content.encode(‘utf-8’) # 将内容编码转化为utf-8 client.sendto(content, ip...
udp通信的服务端和客户端
完整的c#版UDP通信例程.分为客户端和服务端个项目实现字符串发送接受任务.程序精简易懂.绝对适合你.
服务端与客户端通信
我写了两个小程序,一个服务端和一个客户端,在同一台电脑上两个程序是能交互的,但是我把两个程序分开,不再同一条网络上就连接不上了,求帮助,如何实现客户端和服务端不再同一台电脑上,连上网的话就可以交互,刚学习服务器开发的小白求请教。谢谢。
SSLSocket 客户端、服务端通信
1.SSL安全套接层 SSL:(Secure Socket Layer) 安全套接层,于 1994 年由网景公司设计,并于 1995 年发布了 3.0 版本 TLS:(Transport Layer Security)传输层安全性协议,是 IETF 在 SSL3.0 的基础上设计的协议 这两种协议总体差别不大,实现的功能类似,以下都以SSL统称。 ssl处于网络层次中的会话层,位于传输层TC...
socket服务端和客户端通信
先讲解要注意的地方:双方在主函数那里都写读,在线程里用write,因为不这样会在gets那里卡死,要获取数据后才打印,下面是客户端 socket的返回值是accept的返回值 要注意read读的是accept的返回值,是连接后的返回值;
CSocket通信(客户端和服务端)
客户端和服务端各有一个解决方案,该聊天程序是基于CSocket开发的,服务端可传输文件到客户端。服务端管理多个客户端连接,并进行通信。是基于可靠连接的TCP实现的。是在MFC的框架下的页面上显示出来的
相关热词 c#部署端口监听项目、 c#接口中的属性使用方法 c# 昨天 c#func链接匿名方法 c#怎么创建文件夹 c#从键盘接收空格 c#da/ad c#部门请假管理系统 c#服务器socket c# 默认的访问修饰符