Java基础之《netty(6)—NIO快速入门》

news2024/11/24 16:45:55

一、案例
1、编写一个NIO入门案例,实现服务器端和客户端之间的数据简单通讯(非阻塞)
2、目的:理解NIO非阻塞网络编程机制
3、代码

NIOServer.java

package netty.niostart;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOServer {
	public static void main(String[] args) throws Exception {
		//创建ServerSocketChannel -> 类似于ServerSocket
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		
		//得到一个Selector对象
		Selector selector = Selector.open();
		
		//绑定一个端口6666,在服务器端监听
		serverSocketChannel.socket().bind(new InetSocketAddress(6666));
		//设置为非阻塞
		serverSocketChannel.configureBlocking(false);
		
		//把serverSocketChannel注册到selector,关心事件为OP_ACCEPT
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		
		//循环等待客户端连接
		while(true) {
			if (selector.select(5*1000) == 0) {
				//等待5秒钟,如果没有事件发生,返回
				System.out.println("服务器等待了5秒,无连接");
				continue;
			}
			
			//如果返回的>0,就获取到相关的selectionKey集合
			//1.如果返回的>0,表示已经获取到关注的事件
			//2.selector.selectedKeys()返回关注的集合
			//3.通过selectionKeys反向获取通道
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			
			//遍历集合
			Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
			
			while (keyIterator.hasNext()) {
				//获取到SelectionKey
				SelectionKey key = keyIterator.next();
				
				//根据key,对应的通道发生的事件,做相应的处理
				if (key.isAcceptable()) {
					//如果是OP_ACCEPT,有新的客户端连接
					//给该客户端生成一个SocketChannel
					SocketChannel socketChannel = serverSocketChannel.accept();
					//将socketChannel设置为非阻塞模式
					socketChannel.configureBlocking(false);
					//将客户端的socketChannel也注册到selector,关注事件为SelectionKey.OP_READ
					//同时给该socketChannel关联一个buffer
					socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(10240));
					
					System.out.println("from " + socketChannel.hashCode() + " 客户端建立了连接");
					
				} else if (key.isReadable()) {
					//发生OP_READ
					//通过key反向获取到对应的channel
					SocketChannel socketChannel = (SocketChannel)key.channel();
					//获取到该channel关联的buffer
					ByteBuffer byteBuffer = (ByteBuffer)key.attachment();
					
					byteBuffer.clear();
					//把当前通道的数据读到buffer中
					try {
						if (socketChannel.read(byteBuffer) == -1) {
							System.out.println("from " + socketChannel.hashCode() + " 客户端断开了连接");
							keyIterator.remove();
							socketChannel.close();
							continue;
						}
						
						//解析客户端数据
						//socket通讯格式可以自己定义:4字节报文长度+报文体
						String request = new String(byteBuffer.array(), 0, byteBuffer.position(), "utf-8");
						System.out.println("from " + socketChannel.hashCode() + " 客户端:" + request);
						
						
						//----------------------------------------------------------------//
						/**
						 * 业务模块处理(能否做成异步,传入socketChannel对象)主线程只负责网络IO读写
						 */
						
						//业务处理
						System.out.println("业务处理...");
						
						//使用新buffer返回
						ByteBuffer newByteBuffer = ByteBuffer.allocate(10240);
						newByteBuffer.clear();
						
						//获取返回数据
						String response = "result ok";
						newByteBuffer.put(response.getBytes("utf-8"));
						newByteBuffer.put((byte)'9');
						
						//注册写事件
						socketChannel.register(selector, SelectionKey.OP_WRITE, newByteBuffer);
						//----------------------------------------------------------------//
						
						
					} catch (IOException ioe) {
						ioe.printStackTrace();
						keyIterator.remove();
						socketChannel.close();
						continue;
					}
					
				} else if (key.isWritable()) {
					//通过key反向获取到对应的channel
					SocketChannel socketChannel = (SocketChannel)key.channel();
					//获取到该channel关联的buffer
					ByteBuffer byteBuffer = (ByteBuffer)key.attachment();
					
					//读写切换
					byteBuffer.flip(); //只有buffer出数据需要切换
					
					//把buffer数据写入到channel
					try {
						socketChannel.write(byteBuffer);
					} catch (IOException ioe) {
						ioe.printStackTrace();
						keyIterator.remove();
						socketChannel.close();
						continue;
					}
					
					String response = new String(byteBuffer.array(), 0, byteBuffer.position(), "utf-8");
					System.out.println("to " + socketChannel.hashCode() + " 服务端:" + response);
					
					//如果长连接继续注册事件等待
					//注册读事件
					//socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(10240));
					//如果短连接则断开
					socketChannel.close();
					
				} else if (key.isConnectable()) {
					System.out.println("Connectable");
				} else if (key.isValid()) {
					System.out.println("Valid");
				}
				
				//手动从集合中移除当前的SelectionKey,防止重复操作
				keyIterator.remove();
				
			}
			
		}
		
	}
}

NIOClient.java

package netty.niostart;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

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 = "111";
		//客户端也要关联buffer
		ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes("utf-8")); //根据字节数组产生buffer
		
		//Thread.sleep(30*1000); //模拟线程阻塞
		
		//发送数据
		//将buffer数据写入channel
		socketChannel.write(byteBuffer);
		
		//获取返回
		byteBuffer.clear();
		
		int numBytesRead;
		while ((numBytesRead = socketChannel.read(byteBuffer)) != -1) { //-1是读完
			if (numBytesRead == 0) { //0是读到0个
				if (byteBuffer.limit() == byteBuffer.position()) {
                    byteBuffer.clear();
                }
				continue;
			}
			
			//buffer是数组用这个
			//System.out.println("客户端收到:" + new String(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.arrayOffset()+byteBuffer.position(), "utf-8"));
			//单个buffer用这个
			System.out.println("客户端收到:" + new String(byteBuffer.array(), 0, byteBuffer.position(), "utf-8"));
		}
		
		System.out.println("断开连接...");
		socketChannel.close();
		
		//System.in.read();
		
	}
}

二、用telnet测试
1、打开cmd
2、执行:chcp 65001,将编码方式改为“utf-8”
3、telnet连接
4、按Ctrl + ]
5、执行send xxx
6、但是telnet传输字符长度有限制?

三、服务端设想

1、前端请求建立socket连接
2、网络通讯为NIO模型
3、将请求消息和socketChannel对象传到一个队列
4、业务模块维护一个线程池从队列获取请求并处理,还是一请求一线程模式
5、业务线程处理完成后注册写事件,结束
这样网络读写是异步的,通讯和业务处理也是异步的
 

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

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

相关文章

死锁问题【javaEE初阶】

什么是死锁&#xff1f; 所谓死锁&#xff0c;是指多个进程在运行过程中因争夺资源而造成的一种僵局&#xff0c;当进程处于这种僵持状态时&#xff0c;若无外力作用&#xff0c;它们都将无法再向前推进。 因此我们举个例子来描述&#xff0c;如果此时有一个线程A&…

【pen200-lab】10.11.1.217

pen200-lab 学习笔记 【pen200-lab】10.11.1.217 &#x1f525;系列专栏&#xff1a;pen200-lab &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月30日&#x1f334; &#x1f36d;作…

node.js的认识与安装

一、node.js的认识 &#x1f4d6; 简单的说 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于 Chrome JavaScript 运行时建立的一个开源的、跨平台的JavaScript 运行时环境。 Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境&#xff0c;基于 Google 的 V8 引…

ZMQ之脱机可靠性--巨人模式

当你意识到管家模式是一种非常可靠的消息代理时&#xff0c;你可能会想要使用磁盘做一下消息中转&#xff0c;从而进一步提升可靠性。这种方式虽然在很多企业级消息系统中应用&#xff0c;但我还是有些反对的&#xff0c;原因有&#xff1a; 1、我们可以看到&#xff0c;懒惰海…

【JS】数据结构之栈

文章目录基本介绍代码实现基本介绍 内存中的堆栈和数据机构中的堆栈不是一个概念&#xff0c;内存中的堆栈是真实存在的物理区&#xff0c;数据结构中的堆栈是抽象数据存储结构。 栈&#xff1a;是一种受限制的线性表。他遵循后进先出的原则&#xff08;LIFO&#xff09;其限制…

神仙级编程神器,吹爆

Visual Studio 编程领域公认的“最强IDE”&#xff0c;Visual Studio是目前最流行的Windows平台应用程序的集成开发环境&#xff0c;提供了高级开发工具、调试功能、数据库功能和创新功能&#xff0c;帮助在各种平台上快速创建当前最先进的应用程序&#xff0c;开发新的程序。 …

【ODX介绍】-5-用于Flash刷写的ODX-F文件概述

总目录:(单击下方链接皆可跳转至专栏总目录) 《UDS/OBD诊断需求编辑工具》总目录https://blog.csdn.net/qfmzhu/article/details/123697014 共9页精讲:在第二章节中,附上了一个完整的,且详细的ODX-F文件层级结构图。 目录 1 什么是ODX-F?

【在Spring MVC框架和Spring Boot项目中,控制器的响应结果】

目录 1. 控制器的响应结果 2. 相关配置 3. 使用枚举优化代码 1. 控制器的响应结果 当控制器处理了请求之后&#xff0c;向客户端响应的结果中&#xff0c;应该至少包含&#xff1a; 业务状态码&#xff1a;通常是数值类型的&#xff0c;客户端可以根据此数值来判断操作成功…

docke部署nodejs程序及Dockerfile详解

目录参考一、Dockerfile二、部署1、程序结构2、新建Dockerfile3、新建.dockerignore4、构建镜像5、创建容器6、关闭镜像参考 重点参考&#xff1a;把一个 Node.js web 应用程序给 Docker 化 Docker部署Node.js的方法步骤&#xff08;nodejs docker部署&#xff09; 一、Docke…

Linux服务器启动tomcat的三种方式

直接进入主题&#xff0c;首先cd进入tomcat的bin文件夹下&#xff0c;然后可以尝试以下三种启动方式&#xff1a; 第一种&#xff08;当前会话启动&#xff09;&#xff1a; ./startup.sh 效果: 然后tomcat就在后台启动了&#xff0c;我们还可以在当前会话中继续输入其它指令…

PHP基于thinkphp的网上书店管理系统#毕业设计

本论文主要论述了如何使用php语言开发一个网上图书管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;将论述网上图书管理系统的当前背景以及系统开发的目的&#xff0c;后续章节将严格按照软…

【python】 16进制字符串转list

def splitStringToByteList(bytesString): # 拆分字符串成字节列表bytesList []for i in range(int(len(bytesString)/2)):bytesList.append(bytesString[i*2:i*22])return bytesListif __name__ __main__:print(splitStringToByteList("1E1E2AEB4ACC4C")) 结果&…

shiro key文件

​下面结合实战以及shiro的CookieRememberMeManaer的调用过程,浅谈获取shiro key文件的几种方式。 shiro key文件的获取方式:1结合Dnslog与URLDNS;2利用时间延迟或报错;3结合CookieRememberMeManaer 1结合Dnslog与URLDNS 在进行漏洞探测的时候,一般会使用ysoserial-URL…

Codeforces Round #835 (Div. 4)A.B.C.D.E.F

A. Medium Number 题目链接&#xff1a; Problem - A - Codeforces 题面&#xff1a; 题意&#xff1a; 给定三个数&#xff0c;求中间那个数的值 思路&#xff1a; 我们可以分别求出三个数的总和&#xff0c;最大值和最小值&#xff0c;在通过总和减最大值和最小值的方…

Promise(微任务)- 让你看完就懂

1. 图示 思维导图链接 https://www.zhixi.com/view/23ff2291 2. 使用promise原因 在没有promise的时候&#xff0c;一直使用setTimeout函数&#xff0c;这样就会造成回调地狱。 3. 基本状态 promise 有三种状态 pending&#xff08;此时 promise还没有调用完成&#xff09…

改进花朵授粉算法的无线传感器网络部署优化(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

某汽车座椅进行的头冲吸能实验的仿真计算

本案例运用hypermesh和lsdyna联合仿真&#xff0c;主要是针对某座椅进行的头冲吸能实验的仿真计算。这个工况考察座椅背部的塑料件的破坏情况&#xff0c;以及头部模块的加速度情况&#xff0c;达到保护人头部的效果。 本案例用户可以学习到&#xff1a; 1、螺栓预紧力的施加…

IntelliJ IDEA + spring-boot+mysql简单实现获取数据库数据接口例子

一、新建一个spring-boot项目 demo 项目结构与文件的含义&#xff1a; 二、项目编译入口代码 DemoApplication.java SpringBootApplication //EnableAutoConfiguration(exclude{DataSourceAutoConfiguration.class}) public class DemoApplication {public static void main(…

Linux系统编程

Linux系统中所见皆文件 bin&#xff1a;所存放二进制可执行文件 boot&#xff1a;存放开机启动程序 dev&#xff1a;存放设备文件 home&#xff1a;存放用户 etc&#xff1a;用户信息和系统配置文件 lib&#xff1a;库文件 root&#xff1a;管理员宿主目录&#xff08;家目录&…

Linux计划任务管理,网络管理

一&#xff0c;计划任务管理&#xff1a; 任务管理很宽泛&#xff0c;这里是指的计划任务管理&#xff0c;在指定的时间执行。 at命令 &#xff1a; 由atd守护进程来执行&#xff0c;atd进程会定期检查系统上的 /var/spool/at 目录&#xff0c;获取at命令写入的任务。 &#x…