Java Nio 笔记
网上的很多关于NIO的资料是不正确的,nio 支持阻塞和非阻塞模式
- 关于读写状态切换
在读写状态切换的情况下是不能使用regedit 方法注册,而应该使用以下方式进行
selectionKey.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
- setsoTimeout
只是说明允许连接阻塞时间,而不是连接持续时间。
- select(timeOut)
表示选择器阻塞时间,超过该事件关闭全部的channal
- serverSocketChannel.socket().setReuseAddress(true);
该设置应在绑定端口之前,否则该设置无效
如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用 端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息, 抛出“Address already in use: JVM_Bind”。
如果你的服务程序停止后想立即重启,不等60秒,而新套接字依旧 使用同一端口,此时 SO_REUSEADDR 选项非常有用。
- ConcurrentHashMap 与 HashMap 区别
ConcurrentHashMap会自动加锁,避免遍历时,map内容发生变化
*hashmap遍历时,发生插入,remove时会抛出异常
- tcp通讯协议的名词解释
1、send-Q 表示网路发送队列
对方没有收到的数据或者说没有Ack的,还是本地缓冲区.如果发送队列Send-Q不能很快的清零,可能是有应用向外发送数据包过快,或者是对方接收数据包不够快。
这两个值通常应该为0,如果不为0可能是有问题的。packets在两个队列里都不应该有堆积状态。可接受短暂的非0情况
2、recv-q 非由用户进程连接到此socket的复制的总字节数
3、send-q 非由远程主机传送过来的acknowledged总字节数
- nio通讯实例
/**
* 1、socket通讯
* 2、通过BlockingQueue实现多线程共享队列,先进先出,队列满了排队等待
* */
public class CommunicationServer implements Runnable{
private final static Logger log = LoggerFactory.getLogger(CommunicationServer.class); private int DEFAULT_SIZE = 1024;
private boolean isStartListen = false;
private String message = "";
private String serverKey = null;
private String serverIp = null;
private String serverPort = null;
private InetAddress clientIp = null;
private int clientPort = 0; /*事件选择器*/
private Selector selector = null;
private ServerSocketChannel serverSocketChannel;
private BlockingQueue<Attence> attenceQueue;
public CommunicationServer(String serverKey, String serverIP, String port, BlockingQueue<Attence> attenceQueue) {
try{
/*在linux下InetAddress.getLocalHost().getHostAddress()获取到的是127.0.0.1,只能本机监听访问,因此不能使用该代码*/
/*0.0.0.0表示全网监听端口*/
this.serverIp = serverIP;
}catch(Exception e){
e.printStackTrace();
log.error("获取本机Ip地址失败");
this.serverIp = serverIP;
}
this.serverPort = port;
this.serverKey = serverKey;
this.attenceQueue = attenceQueue;
}
@Override
public void run() {
/*启动监听服务器的监听服务*/
startAttenceServer();
listen();
message = "serverKey:"+ serverKey +",serverIp:"+ serverIp +", serverPort:"+ serverPort +",正常关闭数据";
/*停止接收数据服务线程*/
stopAttenceServer(message);
log.info("服务器线程执行完毕停止工作");
}
/*启动监听服务器的监听服务*/
private void startAttenceServer(){
message = "";
/*监听服务器端口*/
if(Common.isInteger(serverPort)){
try{
//创建一个新的selector
selector = Selector.open();
// 创建一个新的serverSocketChannel
serverSocketChannel = ServerSocketChannel.open(); /*
* 该设置应在绑定端口之前,否则该设置无效
* 如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用 端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息, 抛出“Address already in use: JVM_Bind”。
* 如果你的服务程序停止后想立即重启,不等60秒,而新套接字依旧 使用同一端口,此时 SO_REUSEADDR 选项非常有用。
* */
serverSocketChannel.socket().setReuseAddress(true); // 设置为非堵塞模式,异步处理
serverSocketChannel.configureBlocking(false);
// 绑定到端口
serverSocketChannel.socket().bind(new InetSocketAddress("0.0.0.0", Integer.valueOf(serverPort))); // 在选择器里面注册关注这个服务器套接字通道的accept事件
// ServerSocketChannel只有OP_ACCEPT可用,OP_CONNECT,OP_READ,OP_WRITE用于SocketChannel
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
isStartListen = true;
message = "Server:服务已经启动,serverKey:"+ serverKey +",监听IP为"+ serverIp +",监听端口:"+ serverPort;
log.info(message);
}catch(Exception e){
message = "启动服务失败:serverKey:"+ serverKey +",监听serverIp:"+ serverIp +",监听端口serverPort:"+ serverPort +" 原因:"+ e.getMessage();
/*尝试关闭socket通讯*/
stopAttenceServer(message);
log.error(message);
}
}else{
message = "Server:端口错误";
log.info(message);
}
/*标记服务器是否启动成功*/
Server server = new Server();
server.setServerKey(serverKey);
server.setIsStart(isStartListen ? "1" : "0");
server.setIsCanStart(isStartListen ? "1" : "0");
server.setReason(message);
/*将服务器启动状态记录到数据库*/
ServerService serverService = SpringApplicationContextHolder.getBean(ServerService.class);
serverService.updateServerStatus(server);
}
/*启动监听*/
public void listen() {
while (isStartListen) {
try {
if(selector != null){
if(selector.select() == 0){
continue;
}
}else{
continue;
}
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = (SelectionKey) iterator.next();
iterator.remove();
handleKey(selectionKey);
}
selectedKeys.clear();
} catch (ClosedChannelException e) {
e.printStackTrace();
log.info(e.getMessage());
} catch (Exception e) {
e.printStackTrace();
log.info(e.getMessage());
}
try{
Thread.sleep(50);
}catch (InterruptedException ie) {
ie.printStackTrace();
log.info("sleep错误:"+ie.getMessage());
}
}
}
/*事件处理*/
private void handleKey(SelectionKey selectionKey){
try{
if(selectionKey.isValid()){
if(selectionKey.isAcceptable() ){
/*新的连接来临*/
/*得到和Selectionkey关联的Channel*/
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
/*得到与客户端的套接字通道*/
SocketChannel socketChannel = serverSocketChannel.accept();
clientIp = socketChannel.socket().getInetAddress();
clientPort = socketChannel.socket().getPort();
log.info("serverKey:"+ serverKey +",监听serverIp:"+ serverIp +",监听端口serverPort:"+ serverPort + "接收客户端设备的新连接,来自于:"+ clientIp +":"+ clientPort);
//设置socketChannel为非阻塞的socketChannel
socketChannel.configureBlocking(false);
//在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读|写的权限。
//同样将于客户端的通道在selector上注册,OP_READ对应可读事件(对方有写入数据),可以通过key获取关联的选择器
//socketChannel.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
socketChannel.register(selector, SelectionKey.OP_READ);
/*往客户端会写数据*/
sendDataToClient(socketChannel, clientIp, clientPort);
}else if(selectionKey.isReadable() ){
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
/*读取socket数据*/
byte[] socketData = readData(socketChannel, selectionKey);
/*解析数据*/
if(socketData != null && socketData.length > 0){
clientIp = socketChannel.socket().getInetAddress();
clientPort = socketChannel.socket().getPort();
String hexData = AttenceUtil.bytesToHexString(socketData);
parseData(hexData, socketChannel.getLocalAddress(), String.valueOf(clientIp),String.valueOf(clientPort));
}else{
log.info("serverKey:"+ serverKey +",监听serverIp:"+ serverIp +",监听端口serverPort:"+ serverPort + ";socketData为空");
}
if(selectionKey != null && selectionKey.isValid()){
selectionKey.interestOps(SelectionKey.OP_READ);
}
/*立即回应写内容*/
try{
String responseString = "response";
ByteBuffer buffer = Common.encode(responseString);
long bytes = socketChannel.write(buffer);
log.info("readable读取后立即返回写入字节数:"+ bytes);
}catch(Exception e){
String msg = "客户端设备"+ clientIp + ":"+ clientPort +"数据读取失败,移除连接";
log.info(msg);
try{
socketChannel.socket().close();
socketChannel.close();
}catch(Exception e1){
e1.printStackTrace();
msg = "尝试关闭客户端设备"+ clientIp +":"+ clientPort +"连接失败:"+ e1.getMessage();
log.info(msg);
}
}
}else if(selectionKey.isWritable()){
/*通过回写方式验证是否连接正常*/
selectionKey.interestOps(SelectionKey.OP_READ);
log.info(clientIp + ":"+ clientPort +"进入写状态");
}
}
}catch(IOException e){
e.printStackTrace();
try {
if(selectionKey != null){
selectionKey.cancel();
selectionKey.channel().close();
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
log.info(e1.getMessage());
}
log.info(e.getMessage());
}catch(Exception e){
e.printStackTrace();
try {
if(selectionKey != null){
selectionKey.cancel();
selectionKey.channel().close();
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
log.info(e1.getMessage());
}
log.info(e.getMessage());
}
}
/*数据读取*/
private byte[] readData(SocketChannel socketChannel, SelectionKey selectionKey) {
//创建一个用来读取socketChannel的readbuffer
ByteBuffer readbuffer = ByteBuffer.allocate(DEFAULT_SIZE);
readbuffer.clear();
try {
//用readbuffer来读取socketChannel的数据
int nbytes = socketChannel.read(readbuffer);
/*如果read()方法返回-1,则表示底层连接已经关闭,此时需要关闭信道。 关闭信道时,将从选择器的各种集合中移除与该信道关联的键。 */
if(nbytes == -1){
socketChannel.socket().close();
socketChannel.close();
return null;
}
//在readbuffer读取过数据之后,将readbuffer的位置设为0
readbuffer.flip();
byte[]data = new byte[readbuffer.limit()];
readbuffer.get(data, 0, readbuffer.limit());
return data;
} catch(ClosedChannelException e){
e.printStackTrace();
log.info(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
clientIp = socketChannel.socket().getInetAddress();
clientPort = socketChannel.socket().getPort();
message = "serverKey:"+ serverKey +",serverIP:"+ serverIp +",serverPort:"+ serverPort +"。clientIp:"+ clientIp +",clientPort:"+ clientPort +",考勤设备网络连接异常中断,可能被断电:"+ e.getMessage();
log.info(message);
try{
socketChannel.socket().close();
socketChannel.close();
selectionKey.cancel();
}catch(Exception e0){
e0.printStackTrace();
log.info("serverKey:"+ serverKey +",serverIP:"+ serverIp +",serverPort:"+ serverPort +",clientPort:"+ clientPort +",。selectionKey.cancel()时,错误:"+e0.getMessage());
}
} return null;
}
/*数据解析*/
private void parseData(String hexData, SocketAddress socketAddress, String clientIP, String clientPort){
if (hexData != null){
log.info("来自"+ clientIP +":"+ clientPort +"原始数据:"+hexData);
}
}
/*停止服务*/
public void stopAttenceServer(String closeMsg) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
/*停止监听*/
this.isStartListen = false;
/*关闭selector*/
if(selector != null && selector.isOpen()){
try {
selector.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
log.info(e.getMessage());
}
}
/*关闭serverSocketChannel*/
if(serverSocketChannel != null && serverSocketChannel.isOpen() ){
try {
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
log.info(e.getMessage());
}
}
closeMsg = this.serverIp + ":" + this.serverPort + "服务连接关闭:"+closeMsg;
log.info(closeMsg);
/*标记该服务端服务停止运行*/
Server server = new Server();
server.setServerKey(serverKey);
server.setIsStart("0");
server.setIsCanStart("1");
server.setReason(closeMsg);
/*将服务停止运行消息写入数据库*/
ServerService serverService = SpringApplicationContextHolder.getBean(ServerService.class);
serverService.updateServerStatus(server);
}
/*往客户端回写数据*/
public void sendDataToClient(SocketChannel socketChannel, InetAddress clientIp, int clientPort){
String data = "";
ByteBuffer byteBuffer = Common.encode(data);
try {
socketChannel.write(byteBuffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
try {
socketChannel.socket().close();
socketChannel.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
message = e1.getMessage();
log.info(message);
}
message = e.getMessage();
log.info(message);
}
log.info(message);
}
}
- 参考资料
http://my.oschina.net/javagg/blog/3361
http://blog.csdn.net/weibing_huang/article/details/7368635
http://blog.csdn.net/rootsuper/article/details/8537498
http://blog.csdn.net/rootsuper/article/details/8537682
http://blog.csdn.net/rootsuper/article/details/8542236
Java Nio 笔记的更多相关文章
- Java nio 笔记:系统IO、缓冲区、流IO、socket通道
一.Java IO 和 系统 IO 不匹配 在大多数情况下,Java 应用程序并非真的受着 I/O 的束缚.操作系统并非不能快速传送数据,让 Java 有事可做:相反,是 JVM 自身在 I/O 方面 ...
- Java NIo 笔记001
1. Channel Channel接口只提供了两个方法: package java.nio.channels; public interface Channel { public boolean i ...
- 【Java nio】java nio笔记
缓冲区操作:缓冲区,以及缓冲区如何工作,是所有I/O的基础.所谓“输入/输出”讲的无非就是把数据移出货移进缓冲区.进程执行I/O操作,归纳起来也就是向操作系统发出请求,让它要么把缓冲区里的数据排干,要 ...
- Java NIO笔记(一):NIO介绍
Java NIO即Java Non-blocking IO(Java非堵塞I/O),由于是在Jdk1.4之后添加的一套新的操作I/O工具包,所以通常会被叫做Java New IO.NIO是为提供I/O ...
- Java NIO 完全学习笔记(转)
本篇博客依照 Java NIO Tutorial翻译,算是学习 Java NIO 的一个读书笔记.建议大家可以去阅读原文,相信你肯定会受益良多. 1. Java NIO Tutorial Java N ...
- Java NIO 核心组件学习笔记
背景知识 同步.异步.阻塞.非阻塞 首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下[1]. 同步:API调用返回时调用者就知道操作的结果如何了(实际读取/写入了多少字节). 异步:相 ...
- Java NIO学习笔记
Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...
- Java开发笔记(九十五)NIO配套的文件工具Files
NIO不但引进了高效的文件通道,而且新增了更加好用的文件工具家族,包括路径组工具Paths.路径工具Path.文件组工具Files.先看路径组工具Paths,该工具提供了静态方法get,输入某个文件的 ...
随机推荐
- Performance Test of List<T>, LinkedList<T>, Queue<T>, ConcurrentQueue<T>
//Test Group 1 { var watch = Stopwatch.StartNew(); var list = new List<int>(); ; j < ; j++) ...
- 常用模式之Command模式入门
package com.zhao.cmd.a; /** * 客户端调用 * 烧烤摊 * * @author LuZhao * */ public class App { public static v ...
- RTSP
相关博客: RTSP 很详细的英文文档 RTSP交互命令简介及过程参数描述 RTSP协议 http://blog.csdn.net/andyweike/article/details/621071 ...
- ruby迭代器iterator和枚举器Enumerator
编写自定义的迭代器 The defining feature of an iterator method is that it invokes a block of code associatedwi ...
- android Spinner的使用
首先是MainActivity package com.example.spinnertest; import java.util.ArrayList; import java.util.List; ...
- 【转】 Android SDK无法更新解决方法---不错
原文网址:http://blog.csdn.net/shi_weihappy/article/details/41847997 自己的修改: 203.208.39.238 dl.google.com7 ...
- Unity3D游戏开发入门(一)
视频: 慕课网适合入门 http://www.imooc.com/video/6582 蛮牛网: http://www.manew.com/ 圣殿中文手册 5.3.2破解工具 面试题 pdf 书籍:
- 如何将数据库中已有表导入到powerDesigner生成pdm文件
1.create new PDM: 2.select database menu; 3.click Reverse Engineer database :4.then choose your scr ...
- FileUtils类介绍
Java的文件操作太基础,缺乏很多实用工具,比如对目录的操作,支持就非常的差了.如果你经常用Java操作文件或文件夹,你会觉得反复编写这些代码是令人沮丧的问题,而且要大量用到递归. 下面是的一个解决方 ...
- 2013 ACM区域赛长沙 K Pocket Cube hdu 4801
题意:给了一个2*2的魔方..每步操作可以将任意一面翻转90度..现在问在N(<=7)步内.最多能翻出几面相同的. 直接打表模拟每种翻转情况 #include<cstdio> #in ...