一、NIO模式的基本原理:

服务端:

  首先,服务端打开一个通道(ServerSocketChannel),并向通道中注册一个通道调度器(Selector);然后向通道调度器注册感兴趣的事件SelectionKey(如:OP_ACCEPT),接着就可以使用通道调度器(Selector)轮询通道(ServerSocketChannel)上注册的事件,并进行相应的处理。

客户端:

客户端在请求与服务端通信时,也可以像服务器端一样注册感兴趣的事件(比服务端少了SelectionKey.OP_ACCEPT事件),并通过轮询来处理指定的事件,而不必阻塞。

服务端和客户端各自维护一个通道调度器(Selector)对象,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。

二、基于NIO的简单Socket通信实例

服务端:

package cn.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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator; /**
*
* @author*/
public class NIOServer {
//通道调度器
private Selector selector; /**
* 获得一个ServerSocket通道,并对该通道做一些初始化的工作
* @param port 绑定的端口号
* @throws IOException
*/
public void initServer(int port) throws IOException {
// 获得一个ServerSocket通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
// 设置通道为非阻塞
serverChannel.configureBlocking(false);
// 将该通道对应的ServerSocket绑定到port端口
serverChannel.socket().bind(new InetSocketAddress(port));
// 获得一个通道调度器
this.selector = Selector.open();
//将通道调度器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
//当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} /**
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void listen() throws IOException {
System.out.println("server start success");
// 轮询访问selector
while (true) {
//当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
// 获得selector中选中的项的迭代器,选中的项为注册的事件
Iterator ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 删除已选的key,以防重复处理
ite.remove();
// 客户端请求连接事件
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key
.channel();
// 获得和客户端连接的通道
SocketChannel channel = server.accept();
// 设置成非阻塞
channel.configureBlocking(false); //在这里可以给客户端发送信息哦
channel.write(ByteBuffer.wrap(new String("send a message to client").getBytes()));
//在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ); // 获得了可读的事件
} else if (key.isReadable()) {
read(key);
} } }
}
/**
* 处理读取客户端发来的信息 的事件
* @param key
* @throws IOException
*/
public void read(SelectionKey key) throws IOException{
// 服务器可读取消息:得到事件发生的Socket通道
SocketChannel channel = (SocketChannel) key.channel();
// 创建读取的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
channel.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("received message from client:"+msg);
ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
channel.write(outBuffer);// 将消息回送给客户端
} /**
* 启动服务端测试
* @throws IOException
*/
public static void main(String[] args) throws IOException {
NIOServer server = new NIOServer();
server.initServer(8000);
server.listen();
} }

客户端:

 package com.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; /**
*
* @author */
public class NIOClient {
//通道调度器
private Selector selector; /**
* 获得一个Socket通道,并对该通道做一些初始化的工作
* @param ip 连接的服务器的ip
* @param port 连接的服务器的端口号
* @throws IOException
*/
public void initClient(String ip,int port) throws IOException {
// 获得一个Socket通道
SocketChannel channel = SocketChannel.open();
// 设置通道为非阻塞
channel.configureBlocking(false);
// 获得一个通道调度器
this.selector = Selector.open(); // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
//用channel.finishConnect();才能完成连接
channel.connect(new InetSocketAddress(ip,port));
//将通道调度器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
channel.register(selector, SelectionKey.OP_CONNECT);
} /**
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void listen() throws IOException {
// 轮询访问selector
while (true) {
selector.select();
// 获得selector中选中的项的迭代器
Iterator ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 删除已选的key,以防重复处理
ite.remove();
// 连接事件发生
if (key.isConnectable()) {
SocketChannel channel = (SocketChannel) key
.channel();
// 如果正在连接,则完成连接
if(channel.isConnectionPending()){
channel.finishConnect(); }
// 设置成非阻塞
channel.configureBlocking(false); //在这里可以给服务端发送信息哦
channel.write(ByteBuffer.wrap(new String("send a message to server").getBytes()));
//在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ); // 获得了可读的事件
} else if (key.isReadable()) {
read(key);
} } }
}
/**
* 处理读取服务端发来的信息 的事件
* @param key
* @throws IOException
*/
public void read(SelectionKey key) throws IOException{
//和服务端的read方法一样
} /**
* 启动客户端测试
* @throws IOException
*/
public static void main(String[] args) throws IOException {
NIOClient client = new NIOClient();
client.initClient("localhost",8000);
client.listen();
} }

基于NIO的Socket通信的更多相关文章

  1. 【Java TCP/IP Socket】基于NIO的TCP通信(含代码)

    NIO主要原理及使用 NIO采取通道(Channel)和缓冲区(Buffer)来传输和保存数据,它是非阻塞式的I/O,即在等待连接.读写数据(这些都是在一线程以客户端的程序中会阻塞线程的操作)的时候, ...

  2. 基于android的Socket通信

    一.Socket通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户 ...

  3. IOS 基于TCP的socket通信详解(原创)

    最近在整理通信层相关知识,这篇文章是边整理边写的,有些地方可能不够准确,还请各位路过的大牛专家指出来.这次整理的socket通信是基于TCP的,实现方式是GCD形式,以下记录的都是些理论知识,方便自己 ...

  4. 基于TCP协议Socket通信

    服务器线程处理类 package demo4; import java.io.*; import java.net.Socket; /** * 服务器线程处理类 * @ClassName Server ...

  5. 基于tcp的socket通信

    # socket # socekt是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,socket其实就是一个门面模式,它 # 把复杂的tcp/ip协议族隐藏在socket接 ...

  6. python 之网络编程(基于TCP协议Socket通信的粘包问题及解决)

    8.4 粘包问题 粘包问题发生的原因: 1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包),这样接收端,就难于分辨出来了,必须提供科学的拆包机制. ...

  7. 基于TCP的socket通信过程及例子

    Socket也叫套接字,用来实现网络通讯,通过调用系统提供的API,可以和远程的机子传输数据.Socket有很多种协议,而这篇文章主要讨论TCP部分的内容,也就是说后面说的内容主要是指TCP Sock ...

  8. [java]基于UDP的Socket通信Demo

    java课编程作业:在老师给的demo的基础上实现客户端发送数据到服务器端,服务器端接受客户端后进行数据广播. 整体功能类似于聊天室,代码部分不是太难,但是在本机测试的时候出现这样的问题: 服务端通过 ...

  9. 基于多线程的TCP socket通信经典案例

    服务器端 package com.thinkvenus.study.socket; import java.io.BufferedReader; import java.io.IOException; ...

随机推荐

  1. [2014-12-29]使用Enum位模式进行多重状态(或权限)管理

    前言 对于Enum在AspNet Mvc中的应用,我之前提到一种扩展,如何在 Asp.net Mvc 开发过程中更好的使用Enum.这里将介绍另一种更好的使用Enum的方法. Enum定义 以一个代表 ...

  2. java中的ArrayList 使得集合中的对象不重复

    JAVA中的List接口存放的元素是可以重复的,在这个我重写对象里面的equals()方法,让集合里存放的对象不能重复 首先建一个类,在里面的main()方法中实现 list1中存放的是可以重复对象的 ...

  3. 我的前端故事----关于redux的一些思考

    背景 我一个前端,今年第一份工作就是接手一个 APP 的开发...一个线下 BD 人员用的推广 APP,为了让我这个一天原生开发都没有学过的人能快速开发上线,于是乎就选择了 react-native ...

  4. Bootstrap-table事件使用

    HTML <div class="alert alert-danger" id="eventInfo"></div> <table ...

  5. [转]Java7中的ForkJoin并发框架初探(下)—— ForkJoin的应用

    详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp86   前两篇文章已经对Fork Join的设计和JDK中源码的简要分析 ...

  6. linux每日一练:Enable multithreading to use std::thread: Operation not permitted问题解决

    linux每日一练:Enable multithreading to use std::thread: Operation not permitted问题解决 在linux在需要使用c++11时会遇到 ...

  7. JS内置对象-自定义对象

    1.基本概念: ①对象:对象是拥有一系列无序属性和方法的集合. ②键值对:对象中的数据是以键值对的形式存在,对象的每个属性和方法,都对应值一个键名,以键取值. ③属性:描述对象特征的一系列变量称为属性 ...

  8. 201521123076 《Java程序设计》第6周学习总结

    1.本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖面 ...

  9. Sublime使用Ctrl+`作为快捷键弹出Console没有反映的解决办法

    很多Sublime新人都遇到了这个问题,到网上搜,信息很片面,而且不少都是旧版本的.于是有了这篇文章.       默认Sublime使用Ctrl+`作为快捷键弹出Console,但不同的系统抑或安装 ...

  10. JSP学习(一)之中文乱码问题的解决

    一.响应中的乱码 我们所看到的页面,是由服务器把内容放入响应(response)中,然后发送给浏览器的.如果响应中的数据无法被正常解析,就会出现中文乱码.为什么英文不存在乱码问题?因为无论是ISO-8 ...