netty 点对点聊天程序

使用最新Netty实现一个简单的聊天程序

package com.anxpp.im.server;
import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

import java.io.IOException;
import java.util.Scanner;

import com.anxpp.im.common.IMConfig;
import com.anxpp.im.common.IMMessage;
import com.anxpp.im.common.MsgPackDecode;
import com.anxpp.im.common.MsgPackEncode;
import com.anxpp.im.server.handler.ServerHandler;
public class Server implements Runnable,IMConfig{
    ServerHandler serverHandler = new ServerHandler();
    public static void main(String[] args) throws IOException{
        new Server().start();
    }
    public void start() throws IOException{
        new Thread(this).start();
        runServerCMD();
    }
    /**启动服务端控制台
     * @throws IOException */
    private void runServerCMD() throws IOException {
        //服务端主动推送消息
        int toID = 1;
        IMMessage message = new IMMessage(
                APP_IM,
                CLIENT_VERSION,
                SERVER_ID,
                TYPE_MSG_TEXT,
                toID,
                MSG_EMPTY);
        @SuppressWarnings("resource")
        Scanner scanner = new Scanner(System.in);
        do{
            message.setMsg(scanner.nextLine());
        }
        while (serverHandler.sendMsg(message));
    }
    public void run() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
//                  .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder(65536, 0, 2, 0, 2));
                            ch.pipeline().addLast("msgpack decoder",new MsgPackDecode());
                            ch.pipeline().addLast("frameEncoder", new LengthFieldPrepender(2));
                            ch.pipeline().addLast("msgpack encoder",new MsgPackEncode());
                            ch.pipeline().addLast(serverHandler);
                        }
                    }); 
            ChannelFuture f = b.bind(SERVER_PORT).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}


2.

package com.anxpp.im.server.handler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.io.IOException;
import com.anxpp.im.common.IMMessage;
import com.anxpp.im.common.OnlineUser;
import com.anxpp.im.server.config.BaseConfig;
@ChannelHandler.Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter implements BaseConfig{
    private ChannelHandlerContext ctx;
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.err.println("服务端Handler创建...");
        super.handlerAdded(ctx);
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.err.println("channelInactive");
        super.channelInactive(ctx);
    }
    /**
     * tcp链路建立成功后调用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        this.ctx = ctx;
        System.err.println("有客户端连接:"+ctx.channel().remoteAddress().toString());
    }
    /**
     * 发送消息
     */
    public boolean sendMsg(IMMessage msg) throws IOException {
        System.err.println("服务器推送消息:"+msg);
        ctx.writeAndFlush(msg);
        return msg.getMsg().equals("q") ? false : true;
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws IOException {
        System.err.println("服务器接收到消息:"+msg);
        IMMessage message = (IMMessage)msg;
        if(OnlineUser.get(message.getReceiveId())==null){
            OnlineUser.put(message.getUid(), ctx);
        }
        ChannelHandlerContext c = OnlineUser.get(message.getReceiveId());
        if(c==null){
            message.setMsg("对方不在线!");
            OnlineUser.get(message.getUid()).writeAndFlush(message);
        }
        else
            c.writeAndFlush(message);
    }
    /**
     * 异常处理
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        System.err.println("与客户端断开连接:"+cause.getMessage());
        cause.printStackTrace();
        ctx.close();
    }
}

3

package com.anxpp.im.common;
public interface IMConfig {
    /**客户端配置*/
    int     CLIENT_VERSION = 1;         //版本号
    /**服务端配置*/
    String  SERVER_HOST = "127.0.0.1";  //服务器IP
    int     SERVER_PORT = 9090;         //服务器端口
    /**消息相关*/
    int     SERVER_ID   = 0;            //表示服务器消息
    byte    APP_IM = 1;         //即时通信应用ID为1
    byte    TYPE_CONNECT = 0;   //连接后第一次消息确认建立连接和发送认证信息
    byte    TYPE_MSG_TEXT = 1;  //文本消息
    String  MSG_EMPTY = "";     //空消息
}

4.

package com.anxpp.im.common;

import org.msgpack.annotation.Message;

@Message
public class IMMessage {
    //应用ID
    private byte appId;
    //版本号
    private int version;
    //用户ID
    private int uid;
    //消息类型    0:登陆    1:文字消息
    private byte msgType;
    //接收方
    private int receiveId;
    //消息内容
    private String msg;
    public IMMessage() {}
    /**
     * 构造方法
     * @param appId     应用通道
     * @param version   应用版本
     * @param uid       用户ID
     * @param msgType   消息类型
     * @param receiveId 消息接收者
     * @param msg       消息内容
     */
    public IMMessage(byte appId, int version, int uid, byte msgType, int receiveId, String msg) {
        this.appId = appId;
        this.version = version;
        this.uid = uid;
        this.msgType = msgType;
        this.receiveId = receiveId;
        this.msg = msg;
    }
    public byte getAppId() {
        return appId;
    }
    public void setAppId(byte appId) {
        this.appId = appId;
    }
    public int getVersion() {
        return version;
    }
    public void setVersion(int version) {
        this.version = version;
    }
    public int getUid() {
        return uid;
    }
    public void setUid(int uid) {
        this.uid = uid;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public byte getMsgType() {
        return msgType;
    }
    public void setMsgType(byte msgType) {
        this.msgType = msgType;
    }
    public int getReceiveId() {
        return receiveId;
    }
    public void setReceiveId(int receiveId) {
        this.receiveId = receiveId;
    }
    @Override
    public String toString() {
        return "appId:"+this.appId+",version:"+this.version+",uid:"+this.uid+",msgType:"+this.msgType+",receiveId:"+this.receiveId+",msg:"+this.msg;
    }
}

5.

package com.anxpp.im.common;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.List;
import org.msgpack.MessagePack;
/**
 * 解码工具
 */
public class MsgPackDecode extends MessageToMessageDecoder<ByteBuf>{
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        final int length = msg.readableBytes();
        final byte[] array = new byte[length];
        msg.getBytes(msg.readerIndex(), array, 0, length);
        out.add(new MessagePack().read(array,IMMessage.class));
    }
}


6.

package com.anxpp.im.common;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import java.io.IOException;
import org.msgpack.MessagePack;
/**
 * 编码工具
 */
public class MsgPackEncode extends MessageToByteEncoder<IMMessage>{
    @Override
    protected void encode(ChannelHandlerContext ctx, IMMessage msg, ByteBuf out) throws IOException {
        out.writeBytes(new MessagePack().write(msg));
    }
}


7.

package com.anxpp.im.common;
import io.netty.channel.ChannelHandlerContext;
import java.util.HashMap;
/**
 * 在线用户表
 * @author Administrator
 *
 */
public class OnlineUser {
    //用户表
    private static HashMap<Integer, ChannelHandlerContext> onlineUser = new HashMap<Integer, ChannelHandlerContext>();
    public static void put(Integer uid, ChannelHandlerContext uchc){
        onlineUser.put(uid, uchc);
    }
    public static void remove(Integer uid){
        onlineUser.remove(uid);
    }
    public static ChannelHandlerContext get(Integer uid){
        return onlineUser.get(uid);
    }
}


8,。

package com.anxpp.im.client;
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.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

import java.io.IOException;
import java.util.Scanner;


import com.anxpp.im.client.handler.ClientHandler;
import com.anxpp.im.common.IMConfig;
import com.anxpp.im.common.IMMessage;
import com.anxpp.im.common.MsgPackDecode;
import com.anxpp.im.common.MsgPackEncode;
public class Client implements Runnable,IMConfig {
    public static int UID = 8889;
    public static int toID = 8888;
    private ClientHandler clientHandler = new ClientHandler();
    public static void main(String[] args) throws IOException{
        new Client().start();
    }
    public void start() throws IOException{
        new Thread(this).start();
        runServerCMD();
    }
    public void sendMsg(IMMessage msg) throws IOException {
        clientHandler.sendMsg(msg);
    }
    /**启动客户端控制台*/
    private void runServerCMD() throws IOException {
        IMMessage message = new IMMessage(
                APP_IM,
                CLIENT_VERSION,
                UID,
                TYPE_MSG_TEXT,
                toID,
                MSG_EMPTY);
        @SuppressWarnings("resource")
        Scanner scanner = new Scanner(System.in);
        do{
            message.setMsg(scanner.nextLine());
        }
        while (clientHandler.sendMsg(message));
    }
    @Override
    public void run() {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.SO_KEEPALIVE, true);
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder(65536, 0, 2, 0, 2));
                    ch.pipeline().addLast("msgpack decoder",new MsgPackDecode());
                    ch.pipeline().addLast("frameEncoder", new LengthFieldPrepender(2));
                    ch.pipeline().addLast("msgpack encoder",new MsgPackEncode());
                    ch.pipeline().addLast(clientHandler);
                }
            });
            ChannelFuture f = b.connect(SERVER_HOST, SERVER_PORT).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }
}

9.

package com.anxpp.im.client.handler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.io.IOException;

import com.anxpp.im.client.Client;
import com.anxpp.im.common.IMConfig;
import com.anxpp.im.common.IMMessage;
public class ClientHandler extends ChannelInboundHandlerAdapter implements IMConfig{
    private ChannelHandlerContext ctx;
    /**
     * tcp链路简历成功后调用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("成功连接服务器");
        this.ctx = ctx;
        IMMessage message = new IMMessage(
                APP_IM,
                CLIENT_VERSION,
                Client.UID,
                TYPE_CONNECT,
                SERVER_ID,
                MSG_EMPTY);
        sendMsg(message);
    }
    /**
     * 发送消息
     * @param msg
     * @return
     * @throws IOException 
     */
    public boolean sendMsg(IMMessage msg) throws IOException {
        System.out.println("client:" + msg);
        ctx.channel().writeAndFlush(msg);
        return msg.getMsg().equals("q") ? false : true;
    }
    /**
     * 收到消息后调用
     * @throws IOException 
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws IOException {
        IMMessage m = (IMMessage)msg;
        System.out.println(m.getUid() + ":" + m.getMsg());
    }
    /**
     * 发生异常时调用
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        System.err.println("与服务器断开连接:"+cause.getMessage());
        ctx.close();
    }
}

10 代码结构



 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值