Netty实现客户端和服务端的通信

原创 2017年05月23日 09:31:28

        Netty是Apache团队的又一个优秀的Socket框架,它和mina是一个团队开发的,所以很多思想是相同的,接下来,我们就来实现客户端和服务端的双向通信。

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

        

/**
 * 消息类型
 * @author 李熠
 *
 */
public enum MsgType {

	PING,SEND,LOGIN,NO_TARGET
}
分别是心跳、发送、登录、找不到目标

当客户端和服务端连接后,需要向服务端发送登录请求,也就是消息类型:LOGIN,服务端接收到LOGIN请求后,会将客户端加入到队列中,

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,想服务端发送消息就是发送的这个对象的数据。

接下来,实现客户端队列代码:

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());
            }
        }
	}
	
}
服务端:

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();
                //字符串类解析
		//这里只能添加字符串的编码和解码器,
		//网上有很多例子是这样写的:
		//这种写法只能所有客户端都用netty写,否则其他框架实现的客户端无法发送消息到服务端,因为他是转换的netty自己的Object
		//p.addLast(new ObjectEncoder());
          //p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));	
                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]));
    	}
    }
}

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;

//最好继承
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来实现客户端:

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));
    } 
}

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框架学习之初始篇---多客户端的实现

前面基础已经搞定,那么解决问题的重点来了,如何使用netty搭建一个可以识别不同客户端的server? 晚上回来经过一个多小时的奋斗,搞定了这个问题,但是我也不知道我这种方式是否有什么问题或者说方向是...
  • chuanren1991
  • chuanren1991
  • 2016年10月26日 23:19
  • 2643

netty 实现 服务器 客户端通信

客户端——服务器连接 先啰嗦两句,如果你还不知道Netty是做什么的能做什么。那可以先简单的搜索了解一下。我只能说Netty是一个NIO的框架,可以用于开发分布式的Java程序。具体能做什么,各...
  • u013378306
  • u013378306
  • 2016年09月05日 18:35
  • 13342

Netty服务端和客户端搭建(入门)

本文根据Netty权威指南(第二版)中的案例编写,有些案例中的类在我使用的jar中找不到,所以有所修改。本文使用的版本是netty-all-4.0.46.Final.jar,可以自行去官网下载。一、服...
  • tao_ssh
  • tao_ssh
  • 2017年05月03日 11:55
  • 884

Netty实例-简单的服务端-客户端实现,注释详细

Netty实例-简单地服务端-客户端实现,注释详细
  • jiangtao_st
  • jiangtao_st
  • 2014年07月25日 14:34
  • 7548

Netty客户端和服务器简单的例子

Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序,阿里腾讯都有用到,这里是个简单的服务器和客户端的例子服务端NettyServerBoots...
  • qq_28883885
  • qq_28883885
  • 2017年04月26日 15:44
  • 2236

Netty4 学习笔记之一:客户端与服务端通信 demo

前言 因为以前在项目中使用过Mina框架,感受到了该框架的强大之处。于是在业余时间也学习了一下Netty。因为Netty的主要版本是Netty3和Netty4(Netty5已经被取消了),所以我就直...
  • qazwsxpcm
  • qazwsxpcm
  • 2017年08月31日 18:28
  • 2370

Netty服务端和客户端调用demo

  • 2015年10月04日 15:09
  • 67KB
  • 下载

基于netty的服务器客户端收发消息代码

  • 2016年06月11日 06:54
  • 7.64MB
  • 下载

netty 包含客户端和服务器端

  • 2016年11月09日 08:21
  • 5.58MB
  • 下载

Netty Http协议栈开发(客户端&服务端)

协议栈 是指网络中各层协议的总和, 反应了一个网络中文件传输的过程: 由上层协议到底层协议,再由底层协议到上层协议. 一.Netty Http+Json 协议栈开发 时序图: 1. 构...
  • qq_25804863
  • qq_25804863
  • 2015年09月24日 00:39
  • 8067
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Netty实现客户端和服务端的通信
举报原因:
原因补充:

(最多只允许输入30个字)