Netty之JavaNIO编程模型介绍02,Java开发指南

Set selectionKeys = selector.selectedKeys();

System.out.println("selectionKeys 数量 = " + selectionKeys.size());

//遍历 Set, 使用迭代器遍历

Iterator keyIterator = selectionKeys.iterator();

while (keyIterator.hasNext()) {

//获取到SelectionKey

SelectionKey key = keyIterator.next();

//根据key 对应的通道发生的事件做相应处理

if(key.isAcceptable()) { //如果是 OP_ACCEPT, 有新的客户端连接

//该该客户端生成一个 SocketChannel

SocketChannel socketChannel = serverSocketChannel.accept();

System.out.println("客户端连接成功 生成了一个 socketChannel " + socketChannel.hashCode());

//将 SocketChannel 设置为非阻塞

socketChannel.configureBlocking(false);

//将socketChannel 注册到selector, 关注事件为 OP_READ, 同时给socketChannel

//关联一个Buffer

socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));

System.out.println(“客户端连接后 ,注册的selectionkey 数量=” + selector.keys().size()); //2,3,4…

}

if(key.isReadable()) { //发生 OP_READ

//通过key 反向获取到对应channel

SocketChannel channel = (SocketChannel)key.channel();

//获取到该channel关联的buffer

ByteBuffer buffer = (ByteBuffer)key.attachment();

channel.read(buffer);

System.out.println("form 客户端 " + new String(buffer.array(), CharsetUtil.UTF_8));

}

//手动从集合中移动当前的selectionKey, 防止重复操作

keyIterator.remove();

}

}

}

}

客户端代码

package com.dpb.netty.nio;

import io.netty.util.CharsetUtil;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SocketChannel;

/**

  • @program: netty4demo

  • @description: NIO的客户端

  • @author: 波波烤鸭

  • @create: 2019-12-28 14:18

*/

public class NioClient {

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

//得到一个网络通道

SocketChannel socketChannel = SocketChannel.open();

//设置非阻塞

socketChannel.configureBlocking(false);

//提供服务器端的ip 和 端口

InetSocketAddress inetSocketAddress = new InetSocketAddress(“127.0.0.1”, 6666);

//连接服务器

if (!socketChannel.connect(inetSocketAddress)) {

while (!socketChannel.finishConnect()) {

System.out.println(“因为连接需要时间,客户端不会阻塞,可以做其它工作…”);

}

}

//…如果连接成功,就发送数据

String str = “hello, bobo烤鸭~”;

//Wraps a byte array into a buffer

ByteBuffer buffer = ByteBuffer.wrap(str.getBytes(CharsetUtil.UTF_8));

//发送数据,将 buffer 数据写入 channel

socketChannel.write(buffer);

System.in.read();

}

}

效果

在这里插入图片描述

三、SelectionKey

=============================================================================

SelectionKey,表示 Selector 和网络通道的注册关系, 共四种

int OP_ACCEPT:有新的网络连接可以 accept,值为 16

int OP_CONNECT:代表连接已经建立,值为 8

int OP_READ:代表读操作,值为 1

int OP_WRITE:代表写操作,值为 4

源码中:

public static final int OP_READ = 1 << 0;

public static final int OP_WRITE = 1 << 2;

public static final int OP_CONNECT = 1 << 3;

public static final int OP_ACCEPT = 1 << 4;

SelectionKey相关方法

public abstract class SelectionKey {

public abstract Selector selector();//得到与之关联的 Selector 对象

public abstract SelectableChannel channel();//得到与之关联的通道

public final Object attachment();//得到与之关联的共享数据

public abstract SelectionKey interestOps(int ops);//设置或改变监听事件

public final boolean isAcceptable();//是否可以 accept

public final boolean isReadable();//是否可以读

public final boolean isWritable();//是否可以写

}

在这里插入图片描述

四、ServerSocketChannel

====================================================================================

ServerSocketChannel 在服务器端监听新的客户端 Socket 连接

相关方法如下

public abstract class ServerSocketChannel extends AbstractSelectableChannel implements NetworkChannel{

//得到一个 ServerSocketChannel 通道

public static ServerSocketChannel open();

//设置服务器端端口号

public final ServerSocketChannel bind(SocketAddress local);

//设置阻塞或非阻塞模式,取值 false 表示采用非阻塞模式

public final SelectableChannel configureBlocking(boolean block);

//接受一个连接,返回代表这个连接的通道对象

public SocketChannel accept();

//注册一个选择器并设置监听事件

public final SelectionKey register(Selector sel, int ops);

}

在这里插入图片描述

五、SocketChannel

==============================================================================

SocketChannel,网络 IO 通道,具体负责进行读写操作。NIO 把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。

相关方法如下

public abstract class SocketChannel extends AbstractSelectableChannel implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel{

//得到一个 SocketChannel 通道

public static SocketChannel open();

//设置阻塞或非阻塞模式,取值 false 表示采用非阻塞模式

public final SelectableChannel configureBlocking(boolean block);

//连接服务器

public boolean connect(SocketAddress remote);

//如果上面的方法连接失败,接下来就要通过该方法完成连接操作

public boolean finishConnect();

public int write(ByteBuffer src);//往通道里写数据

public int read(ByteBuffer dst);//从通道里读数据

//注册一个选择器并设置监听事件,最后一个参数可以设置共享数据

public final SelectionKey register(Selector sel, int ops, Object att);

public final void close();//关闭通道

}

在这里插入图片描述

六、群聊系统

=====================================================================

接下来提供一个群聊系统的案例的简单代码。

  1. 编写一个 NIO 群聊系统,实现服务器端和客户端之间的数据简单通讯(非阻塞)
实现多人群聊
  1. 服务器端:可以监测用户上线,离线,并实现消息转发功能
  1. 客户端:通过channel 可以无阻塞发送消息给其它所有用户,同时可以接受其它用户发送的消息(有服务器转发得到)
  1. 目的:进一步理解NIO非阻塞网络编程机制

服务端代码

package com.dpb.netty.nio;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.*;

import java.util.Iterator;

public class GroupChatServer {

//定义属性

private Selector selector;

private ServerSocketChannel listenChannel;

private static final int PORT = 6667;

//构造器

//初始化工作

public GroupChatServer() {

try {

//得到选择器

selector = Selector.open();

//ServerSocketChannel

listenChannel = ServerSocketChannel.open();

//绑定端口

listenChannel.socket().bind(new InetSocketAddress(PORT));

//设置非阻塞模式

listenChannel.configureBlocking(false);

//将该listenChannel 注册到selector

listenChannel.register(selector, SelectionKey.OP_ACCEPT);

}catch (IOException e) {

e.printStackTrace();

}

}

//监听

public void listen() {

System.out.println("监听线程: " + Thread.currentThread().getName());

try {

//循环处理

while (true) {

int count = selector.select();

if(count > 0) {//有事件处理

//遍历得到selectionKey 集合

Iterator iterator = selector.selectedKeys().iterator();

while (iterator.hasNext()) {

//取出selectionkey

SelectionKey key = iterator.next();

//监听到accept

if(key.isAcceptable()) {

SocketChannel sc = listenChannel.accept();

sc.configureBlocking(false);

//将该 sc 注册到seletor

sc.register(selector, SelectionKey.OP_READ);

//提示

System.out.println(sc.getRemoteAddress() + " 上线 ");

}

if(key.isReadable()) { //通道发送read事件,即通道是可读的状态

//处理读 (专门写方法…)

readData(key);

}

//当前的key 删除,防止重复处理

iterator.remove();

}

} else {

System.out.println(“等待…”);

}

}

}catch (Exception e) {

e.printStackTrace();

}finally {

//发生异常处理…

}

}

//读取客户端消息

private void readData(SelectionKey key) {

//取到关联的channle

SocketChannel channel = null;

try {

//得到channel

channel = (SocketChannel) key.channel();

//创建buffer

ByteBuffer buffer = ByteBuffer.allocate(1024);

int count = channel.read(buffer);

//根据count的值做处理

if(count > 0) {

//把缓存区的数据转成字符串

String msg = new String(buffer.array());

//输出该消息

System.out.println("form 客户端: " + msg);

//向其它的客户端转发消息(去掉自己), 专门写一个方法来处理

sendInfoToOtherClients(msg, channel);

}

}catch (IOException e) {

try {

System.out.println(channel.getRemoteAddress() + " 离线了…");

//取消注册

key.cancel();

//关闭通道

channel.close();

}catch (IOException e2) {

e2.printStackTrace();;

}

}

}

//转发消息给其它客户(通道)

private void sendInfoToOtherClients(String msg, SocketChannel self ) throws IOException{

System.out.println(“服务器转发消息中…”);

System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName());

//遍历 所有注册到selector 上的 SocketChannel,并排除 self

for(SelectionKey key: selector.keys()) {

//通过 key 取出对应的 SocketChannel

Channel targetChannel = key.channel();

//排除自己

if(targetChannel instanceof SocketChannel && targetChannel != self) {

//转型

SocketChannel dest = (SocketChannel)targetChannel;

//将msg 存储到buffer

ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());

//将buffer 的数据写入 通道

dest.write(buffer);

}

}

}

public static void main(String[] args) {

//创建服务器对象

GroupChatServer groupChatServer = new GroupChatServer();

groupChatServer.listen();

}

}

//可以写一个Handler

class MyHandler {

public void readData() {

}

public void sendInfoToOtherClients(){

}

}

客户端代码

package com.dpb.netty.nio;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.util.Iterator;

import java.util.Scanner;

import java.util.Set;

public class GroupChatClient {

//定义相关的属性

private final String HOST = “127.0.0.1”; // 服务器的ip

private final int PORT = 6667; //服务器端口

private Selector selector;

private SocketChannel socketChannel;

private String username;

//构造器, 完成初始化工作

public GroupChatClient() throws IOException {

selector = Selector.open();

//连接服务器

socketChannel = socketChannel.open(new InetSocketAddress(“127.0.0.1”, PORT));

//设置非阻塞

socketChannel.configureBlocking(false);

//将channel 注册到selector

socketChannel.register(selector, SelectionKey.OP_READ);

//得到username

username = socketChannel.getLocalAddress().toString().substring(1);

System.out.println(username + " is ok…");

}

//向服务器发送消息

public void sendInfo(String info) {

info = username + " 说:" + info;

try {

socketChannel.write(ByteBuffer.wrap(info.getBytes()));

}catch (IOException e) {

e.printStackTrace();

}

}

//读取从服务器端回复的消息

public void readInfo() {

try {

int readChannels = selector.select();

if(readChannels > 0) {//有可以用的通道

Iterator iterator = selector.selectedKeys().iterator();

while (iterator.hasNext()) {

SelectionKey key = iterator.next();

if(key.isReadable()) {

//得到相关的通道

SocketChannel sc = (SocketChannel) key.channel();

//得到一个Buffer

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分

年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-VNcJv2jF-1711948121132)]
[外链图片转存中…(img-WjLsGCYq-1711948121133)]
[外链图片转存中…(img-VMjNSDWZ-1711948121133)]
[外链图片转存中…(img-2HkDDCzu-1711948121133)]
[外链图片转存中…(img-jMKa0roD-1711948121134)]
[外链图片转存中…(img-zYKUlbWg-1711948121134)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-fW6sCXBO-1711948121135)]

[外链图片转存中…(img-x8cQctop-1711948121135)]

最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分

[外链图片转存中…(img-IPaDy9nC-1711948121135)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值