Netty高级应用之:编解码器与群聊天室开发

news2025/2/24 23:54:35

Netty高级应用之:编解码器与群聊天室开发

文章目录

  • Netty高级应用之:编解码器与群聊天室开发
    • Netty编解码器
      • Java的编解码
      • Netty编解码器
        • 概念
        • 解码器(Decoder)
        • 编码器(Encoder)
        • 编码解码器Codec
    • Netty案例-群聊天室
      • 聊天室服务端编写
      • 聊天室客户端编写

Netty编解码器

Java的编解码

  1. 编码(Encode)称为序列化, 它将对象序列化为字节数组,用于网络传输、数据持久化或者其它用途。
  2. 解码(Decode)称为反序列化,它把从网络、磁盘等读取的字节数组还原成原始对象(通常是原始对象的拷贝),以方便后续的业务逻辑操作。

在这里插入图片描述

java序列化对象只需要实现java.io.Serializable接口并生成序列化ID,这个类就能够通过java.io.ObjectInput和java.io.ObjectOutput序列化和反序列化。

Java序列化目的:1.网络传输。2.对象持久化。

Java序列化缺点:1.无法跨语言。 2.序列化后码流太大。3.序列化性能太低

Java序列化仅仅是Java编解码技术的一种,由于它的种种缺陷,衍生出了多种编解码技术和框架,这些编解码框架实现消息的高效序列化。

Netty编解码器

概念

在网络应用中需要实现某种编解码器,将原始字节数据与自定义的消息对象进行互相转换网络中都是以字节码的数据形式来传输数据的,服务器编码数据后发送到客户端,客户端需要对数据进行解码。

对于Netty而言,编解码器由两部分组成:编码器、解码器。

  • 解码器:负责将消息从字节或其他序列形式转成指定的消息对象。
  • 编码器:将消息对象转成字节或其他序列形式在网络上传输。

Netty 的编(解)码器实现了 ChannelHandlerAdapter,也是一种特殊的 ChannelHandler,所以依赖于 ChannelPipeline,可以将多个编(解)码器链接在一起,以实现复杂的转换逻辑。

Netty里面的编解码:

  • 解码器:负责处理“入站 InboundHandler”数据。
  • 编码器:负责“出站OutboundHandler” 数据。

解码器(Decoder)

解码器负责解码“入站”数据从一种格式到另一种格式,解码器处理入站数据是抽象ChannelInboundHandler的实现。需要将解码器放在ChannelPipeline中。对于解码器,Netty中主要提供了抽象基类ByteToMessageDecoder和MessageToMessageDecoder

在这里插入图片描述

抽象解码器

  • ByteToMessageDecoder: 用于将字节转为消息,需要检查缓冲区是否有足够的字节
  • ReplayingDecoder: 继承ByteToMessageDecoder,不需要检查缓冲区是否有足够的字节,但是 ReplayingDecoder速度略慢于ByteToMessageDecoder,同时不是所有的ByteBuf都支持。项目复杂性高则使用ReplayingDecoder,否则使用ByteToMessageDecoder
  • MessageToMessageDecoder: 用于从一种消息解码为另外一种消息(例如POJO到POJO)

核心方法

decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)

代码实现

解码器:

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.CharsetUtil;

import java.util.List;

/**
 * 消息解码器
 */
public class MessageDecoder extends MessageToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
        System.out.println("正在进行消息解码....");
        ByteBuf byteBuf = (ByteBuf) msg;
        out.add(byteBuf.toString(CharsetUtil.UTF_8));//传递到下一个handler
    }
}

通道读取方法

    /**
     * 通道读取事件
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("客户端发送过来的消息:" + msg);
    }

启动类

	protected void initChannel(SocketChannel ch) throws Exception {
		//8. 向pipeline中添加自定义业务处理handler
		ch.pipeline().addLast(new MessageDecoder());//添加解码器
		ch.pipeline().addLast(new NettyServerHandler());
      	}

编码器(Encoder)

与ByteToMessageDecoder和MessageToMessageDecoder相对应,Netty提供了对应的编码器实现MessageToByteEncoder和MessageToMessageEncoder,二者都实现ChannelOutboundHandler接口。

在这里插入图片描述

抽象编码器

  • MessageToByteEncoder: 将消息转化成字节
  • MessageToMessageEncoder: 用于从一种消息编码为另外一种消息(例如POJO到POJO)

核心方法

encode(ChannelHandlerContext ctx, String msg, List<Object> out)

代码实现

编码器:

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil;

import java.util.List;

/**
 * 消息的编码器
 */
public class MessageEncoder extends MessageToMessageEncoder {
    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
        System.out.println("消息正在进行编码....");
        String str = (String) msg;
        out.add(Unpooled.copiedBuffer(str, CharsetUtil.UTF_8));
    }
}

消息发送:

/**
     * 通道就绪事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ChannelFuture future = ctx.writeAndFlush("你好呀.我是Netty客户端");
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (future.isSuccess()) {
                    System.out.println("数据发送成功!");
                } else {
                    System.out.println("数据发送失败!");
                }
            }
        });
    }

启动类:

@Override
protected void initChannel(SocketChannel ch) throws Exception {
	//6. 向pipeline中添加自定义业务处理handler
	ch.pipeline().addLast(new MessageDecoder());//添加解码器
	ch.pipeline().addLast(new MessageEncoder());//添加编码器
	ch.pipeline().addLast(new NettyClientHandler());
}

编码解码器Codec

编码解码器: 同时具有编码与解码功能,特点同时实现了ChannelInboundHandler和ChannelOutboundHandler接口,因此在数据输入和输出时都能进行处理。

在这里插入图片描述

Netty提供提供了一个ChannelDuplexHandler适配器类,编码解码器的抽象基类ByteToMessageCodec ,MessageToMessageCodec都继承与此类.

代码实现

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.util.CharsetUtil;

import java.util.List;

/**
 * 消息编解码器
 */
public class MessageCodec extends MessageToMessageCodec {
    /**
     * 编码
     *
     * @param ctx
     * @param msg
     * @param out
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
        System.out.println("消息正在进行编码....");
        String str = (String) msg;
        out.add(Unpooled.copiedBuffer(str, CharsetUtil.UTF_8));
    }

    /**
     * 解码
     *
     * @param ctx
     * @param msg
     * @param out
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
        System.out.println("正在进行消息解码....");
        ByteBuf byteBuf = (ByteBuf) msg;
        out.add(byteBuf.toString(CharsetUtil.UTF_8));//传递到下一个handler
    }
}

启动类

protected void initChannel(SocketChannel ch) throws Exception {
	//8. 向pipeline中添加自定义业务处理handler
	ch.pipeline().addLast(new MessageCoder());//添加编解码器
	ch.pipeline().addLast(new NettyServerHandler());
}

Netty案例-群聊天室

案例要求:

  1. 编写一个 Netty 群聊系统,实现服务器端和客户端之间的数据简单通讯
  2. 实现多人群聊
  3. 服务器端:可以监测用户上线,离线,并实现消息转发功能
  4. 客户端:可以发送消息给其它所有用户,同时可以接受其它用户发送的消息

聊天室服务端编写

NettyChatServer

import com.lagou.demo.NettyServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;

/**
 * 聊天室服务端
 */
public class NettyChatServer {
    //端口号
    private int port;

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

    public void run() throws InterruptedException {
        //1. 创建bossGroup线程组: 处理网络事件--连接事件
        EventLoopGroup bossGroup = null;
        //2. 创建workerGroup线程组: 处理网络事件--读写事件 2*处理器线程数
        EventLoopGroup workerGroup = null;
        try {
            bossGroup = new NioEventLoopGroup(1);
            workerGroup = new NioEventLoopGroup();
            //3. 创建服务端启动助手
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //4. 设置bossGroup线程组和workerGroup线程组
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) //5. 设置服务端通道实现为NIO
                    .option(ChannelOption.SO_BACKLOG, 128)//6. 参数设置
                    .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)//6. 参数设置
                    .childHandler(new ChannelInitializer<SocketChannel>() { //7. 创建一个通道初始化对象
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //8. 向pipeline中添加自定义业务处理handler
                            //添加编解码器
                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new StringEncoder());
                            // todo
                            ch.pipeline().addLast(new NettyChatServerHandler());
                        }
                    });
            //9. 启动服务端并绑定端口,同时将异步改为同步
            ChannelFuture future = serverBootstrap.bind(port);
            future.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (future.isSuccess()) {
                        System.out.println("端口绑定成功!");
                    } else {
                        System.out.println("端口绑定失败!");
                    }
                }
            });
            System.out.println("聊天室服务端启动成功.");
            //10. 关闭通道(并不是真正意义上关闭,而是监听通道关闭的状态)和关闭连接池
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new NettyChatServer(9998).run();
    }
}

NettyChatServerHandle

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

import java.util.ArrayList;
import java.util.List;

/**
 * 聊天室业务处理类
 */
public class NettyChatServerHandler extends SimpleChannelInboundHandler<String> {
    public static List<Channel> channelList = new ArrayList<>();

    /**
     * 通道就绪事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //当有新的客户端连接的时候, 将通道放入集合
        channelList.add(channel);
        System.out.println("[Server]:" +
                channel.remoteAddress().toString().substring(1) + "在线.");
    }

    /**
     * 通道未就绪--channel下线
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //当有客户端断开连接的时候,就移除对应的通道
        channelList.remove(channel);
        System.out.println("[Server]:" +
                channel.remoteAddress().toString().substring(1) + "下线.");
    }

    /**
     * 通道读取事件
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        //当前发送消息的通道, 当前发送的客户端连接
        Channel channel = ctx.channel();
        for (Channel channel1 : channelList) {
            //排除自身通道
            if (channel != channel1) {
                channel1.writeAndFlush("[" + channel.remoteAddress().toString().substring(1)
                        + "]说:" + msg);
            }
        }
    }

    /**
     * 异常处理事件
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        Channel channel = ctx.channel();
        //移除集合
        channelList.remove(channel);
        System.out.println("[Server]:" +
                channel.remoteAddress().toString().substring(1) + "异常.");
    }
}

聊天室客户端编写

NettyChatClient

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Scanner;

/**
 * 聊天室的客户端
 */
public class NettyChatClient {

    private String ip;//服务端IP
    private int port;//服务端端口号

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

    public void run() throws InterruptedException {
        //1. 创建线程组
        EventLoopGroup group = null;
        try {
            group = new NioEventLoopGroup();
            //2. 创建客户端启动助手
            Bootstrap bootstrap = new Bootstrap();
            //3. 设置线程组
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)//4. 设置客户端通道实现为NIO
                    .handler(new ChannelInitializer<SocketChannel>() { //5. 创建一个通道初始化对象
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //6. 向pipeline中添加自定义业务处理handler
                            //添加编解码器
                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new StringEncoder());
                            //添加客户端的处理类
                            ch.pipeline().addLast(new NettyChatClientHandler());
                        }
                    });
            //7. 启动客户端,等待连接服务端,同时将异步改为同步
            ChannelFuture channelFuture = bootstrap.connect(ip, port).sync();
            Channel channel = channelFuture.channel();
            System.out.println("-------" + channel.localAddress().toString().substring(1) + "--------");
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()) {
                String msg = scanner.nextLine();
                //向服务端发送消息
                channel.writeAndFlush(msg);
            }
            //8. 关闭通道和关闭连接池
            channelFuture.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new NettyChatClient("127.0.0.1", 9998).run();
    }
}

NettyChatClientHandle

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

/**
 * 聊天室处理类
 */
public class NettyChatClientHandler extends SimpleChannelInboundHandler<String> {

    /**
     * 通道读取就绪事件
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(msg);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/361979.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT爆火背后的真相:学编程已经成为必选项

这一阵最热门的话题&#xff0c;莫过于人工智能新选手——ChatGPT&#xff0c;在推出后只用了两个月就积累了1亿用户&#xff01;它的出现在科技圈掀起了一阵“惊涛骇浪”&#xff0c;有人称ChatGPT的意义&#xff0c;堪比当年蒸汽机的出现&#xff0c;它足以爆发新一轮的“工业…

什么耳机适合跑步、挑选五款最佳的跑步耳机推荐

在进行户外跑步、骑行等运动&#xff0c;往往会感到枯燥乏味&#xff0c;很难坚持下去&#xff0c;就像我经常跑一圈就觉得没了动力&#xff0c;但是当我戴上耳机听音乐跑步时&#xff0c;不知不觉就结束了&#xff0c;就感觉时间过得很快。不过话有说回来&#xff0c;适合跑步…

【Spark分布式内存计算框架——离线综合实战】1. 综合实战概述

离线综合实战 大数据营销是基于多平台的大量数据&#xff0c;依托大数据技术的基础上&#xff0c;应用于互联网广告行业的营销方式。大数据营销的核心在于让网络广告在合适的时间&#xff0c;通过合适的载体&#xff0c;以合适的方式&#xff0c;投给合适的人。大数据营销是精…

buu [WUSTCTF2020]情书 1

题目描述&#xff1a; 题目分析&#xff1a; 翻译一下可知&#xff1a; 前提&#xff1a;用0、1、2、……枚举字母表25 使用RSA系统 加密&#xff1a;0156 0821 1616 0041 0140 2130 1616 0793 公钥&#xff1a;2537和13 私钥&#xff1a;2537和937 从提示可以得知 n 2537 , …

盘点四种自动化测试模型实例及优缺点

一&#xff0c;线性测试 1.概念&#xff1a; 通过录制或编写对应应用程序的操作步骤产生的线性脚本。单纯的来模拟用户完整的操作场景。 &#xff08;操作&#xff0c;重复操作&#xff0c;数据&#xff09;都混合在一起。 2.优点&#xff1a; 每个脚本相对独立&#xff0…

leetcode1143 最长公共子序列LCS

这道题目核心的一行代码就是判断s1的第i个字符和s2的第j个字符是否相等 是s1.charAt(i-1)s2.charAt(j-1) 而不是s1.charAt(i)s2.charAt(j) 动态规划就是搞清楚dp[i][j]的含义然后写出动态转移方程 (1)dp[i][j]的含义 dp[i][j]表示考虑s1的前i个字符&#xff0c;考虑s2的前j…

【C++】类型转换方法

本篇博客让我们来见识一下C中新增的类型转换方法 文章目录1.C语言中类型转换2.C中的强制类型转换2.1 static_cast2.2 reinterpret_cast2.3 const_castvolatile关键字2.4 dynamic_cast3.C强制类型转换的作用4.RTTI1.C语言中类型转换 在C语言中&#xff0c;类型转换有下面两种形…

音视频基础之ffmpeg命令实战一

一&#xff1a;概念区别 ffmpeg/ffmplay/ffprobe区别 ◼ffmpeg: Hyper fast Audio and Video encoder 超快音视频编码器&#xff08;类似爱剪辑&#xff09; 如&#xff1a;把10分钟的视频剪辑为2分钟。 ◼ffplay: Simple media player简单媒体播放器 ◼ffprobe: Simple multi…

0202插入删除-算法第四版红黑树-红黑树-数据结构和算法(Java)

文章目录4 插入4.1 序4.2 向单个2-结点插入新键4.3 向树底部的2-结点插入新键4.4 向一棵双键树&#xff08;3-结点&#xff09;中插入新键4.5 颜色调整4.6 根结点总是黑色4.7 向树底部的3-结点插入新键4.8 将红链接在树中向上传递4.9 实现5 删除5.1 删除最小键5.2 删除6 有序性…

ASE4N65SE-ASEMI高压MOS管ASE4N65SE

编辑-Z ASE4N65SE在TO-220F封装里的静态漏极源导通电阻&#xff08;RDS(ON)&#xff09;为2.5Ω&#xff0c;是一款N沟道高压MOS管。ASE4N65SE的最大脉冲正向电流ISM为16A&#xff0c;零栅极电压漏极电流(IDSS)为10uA&#xff0c;其工作时耐温度范围为-55~150摄氏度。ASE4N65S…

面试官:说说react的渲染过程

hello&#xff0c;这里是潇晨&#xff0c;大家在面试的过程中有没有遇到过一些和react相关的问题呢&#xff0c;比如面试官让你说说react渲染的过程&#xff0c;这到题目比较开放&#xff0c;也比较考验大家对react渲染原理以及源码的整体架构的理解。 整体流程&#xff1a; r…

AcWing语法基础课笔记 第八章 C++ STL 第九章 位运算与常用库函数

第八章 C STL 第八章 C STL 1.#include <vector> 2.#include<queue> 3.#include <stack> 4.#include <deque> 5.#include <set> 6.#include<map> 第九章 位运算与常用库函数 STL是提高C编写效率的一个利器。 ——闫…

文献阅读笔记 # 区块链在软件供应链管理中的应用探索

崔宝江, 宋绪言. 区块链在软件供应链管理中的应用探索[J]. 保密科学技术, 2019(5): 41-44.主要作者来自北京邮电大学网络空间安全学院 移动互联网安全技术国家工程实验室&#xff1b; 摘要 探索用区块链技术保障软件供应链安全 1 引言 略。 2 软件供应链面临的安全风险 一…

网络协议(一)应用层(自定制协议、HTTP协议)

目录 应用层&#xff1a;负责应用程序之间的数据沟通 一、自定制协议&#xff08;私有协议&#xff09; 二、HTTP协议 1&#xff09;、请求行解析&#xff1a;GET /index.html HTTP/1.1 第一部分&#xff1a;请求方法&#xff1a;多种多样&#xff0c;描述不同的请求目的 …

吐血整理的网络工程师必懂的26个技术名词,自查一下看看自己知道多少 ?

在做网络工程师的工作时候&#xff0c;虽说那些晦涩难懂的英语以及技术名词很难记 比如我这个英语从不及格的小乐色&#xff0c;但是你会发现很多都会用到&#xff0c;所以多多少少还是要储备一些在脑子里&#xff0c;有点印象就OK了。查起来也方便不是&#xff01;那么今天咱…

基于龙芯 2K1000 的嵌入式 Linux 系统移植和驱动程序设计(二)

第 3 章 嵌入式软件系统移植本课题中嵌入式系统正常工作的前提是嵌入式软件系统完整且能正常工作&#xff0c; 以便为之后的软件开发提供一个能够正常工作的平台。引导程序 PMON 需要完成 内核引导&#xff0c;嵌入式 Linux 内核需要具有完备的功能且能够正常使用&#xff0c;根…

2023年 Android Studio Unable to find bundled Java version 解决方法

学习flutter过程中发现 Unable to find bundled Java version 错误搜索网上的解决方案都不对最后在 b站 https://www.bilibili.com/video/BV1S4411E7LY?p17&vd_sourced7cf0e2cd70b3cc57314d2efcb598c3d 教程的课件中找到了解决方哦 我的flutter版本 C:\Users\Ken>flu…

MQTT传输JSON数据实例

想跑一个用MQTT传输JSON的实例&#xff0c;上网找了一下开源代码&#xff0c;找到一个比较合适的&#xff1a;https://blog.csdn.net/ktigerhero3/article/details/107178252&#xff0c;程序源码直接用这个就可以&#xff0c;然后过程中需要进行一下环境的配置&#xff0c;本篇…

本地事务详解

1、事务的基本性质 数据库事务的几个特性&#xff1a;原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation) 和持久性(Durabilily)&#xff0c;简称就是 ACID&#xff1b;  原子性&#xff1a;一系列的操作整体不可拆分&#xff0c;要么同时成功&#x…

【Optional】告别丑陋判空,使用Optional类

一、概述 当项目中充斥着大量的、丑陋的判空语句&#xff0c;如下&#xff1a; if (user ! null) {Address address user.getAddress();if (address ! null) {Country country address.getCountry();if (country ! null) {String isocode country.getIsocode();if (isocod…