java中nio的socket通信实例程序

虽然Java平台有不少相当不错的一步socket的框架,比如Netty,naga等,但是我们应该知其然并知其所以然。

nio是java New IO 的简称,从jdk1.4里提供的新api,Sun官方标榜的特性如下:

为所有的原始类型提供 (Buffer) 缓存支持
字符集编码解码解决方案
Channel:一个新的原始 I/O 抽象
支持锁和内存映射文件的文件访问接口
提供多路 (non-bloking) 非阻塞式的高伸缩性网络 I/O
基本原理

NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的SocketChannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的SocketChannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。

Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容

基于nio写起来的代码有点罗嗦,不如用框架写起来那么爽。这个服务端实现的功能比较简单,还是只把客户端发过来的数据再返回去。

服务端

初始化Selector

 代码如下 复制代码

private Selector initSelector() throws IOException {
 Selector socketSelector = SelectorProvider.provider().openSelector();
 this.serverChannel = ServerSocketChannel.open();
 serverChannel.configureBlocking(false);
 InetSocketAddress isa = new InetSocketAddress(this.hostAddress, this.port);
 serverChannel.socket().bind(isa);
 serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
 
 return socketSelector;
}

接受传入的连接

 代码如下 复制代码

private void accept(SelectionKey key) throws IOException {
 ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
 SocketChannel socketChannel = serverSocketChannel.accept();
 Socket socket = socketChannel.socket();
 socketChannel.configureBlocking(false);
 socketChannel.register(this.selector, SelectionKey.OP_READ);
 LOGGER.info("ACCEPT: " + socket.getInetAddress().toString());
}

接受数据

 代码如下 复制代码

private void read(SelectionKey key) throws IOException {
 SocketChannel socketChannel = (SocketChannel) key.channel();
 this.readBuffer.clear();
 int numRead;
 try {
  numRead = socketChannel.read(this.readBuffer);
 } catch (IOException e) {
  key.cancel();
  socketChannel.close();
  return;
 }
 
 if (numRead == -1) {
  key.channel().close();
  key.cancel();
  return;
 }
 
 // Hand the data off to our worker thread
 this.worker.processData(this, socketChannel, this.readBuffer.array(), numRead);
}

发送数据

 代码如下 复制代码

private void write(SelectionKey key) throws IOException {
 SocketChannel socketChannel = (SocketChannel) key.channel();
 List<ByteBuffer> queue = this.pendingData.get(socketChannel);
 
 while (!queue.isEmpty()) {
  ByteBuffer buf = queue.get(0);
  socketChannel.write(buf);
  if (buf.remaining() > 0) {
   break;
  }
  queue.remove(0);
 }
 
 if (queue.isEmpty()) {
  key.interestOps(SelectionKey.OP_READ);
 }
}

客户端

客户端的初始化相当简单

 代码如下 复制代码

private Selector initSelector() throws IOException {
 return SelectorProvider.provider().openSelector();
}

接受数据

 代码如下 复制代码

private void read(SelectionKey key) throws IOException {
 SocketChannel socketChannel = (SocketChannel) key.channel();
 this.readBuffer.clear();
 
 int numRead;
 try {
  numRead = socketChannel.read(this.readBuffer);
 } catch (IOException e) {
  key.cancel();
  socketChannel.close();
  return;
 }
 
 if (numRead == -1) {
  key.channel().close();
  key.cancel();
  return;
 }
 
 // Handle the response
 this.handleResponse(socketChannel, this.readBuffer.array(), numRead);
}

发送数据

 代码如下 复制代码

private void write(SelectionKey key) throws IOException {
 SocketChannel socketChannel = (SocketChannel) key.channel();
 List<ByteBuffer> queue = this.pendingData.get(socketChannel);
 
 while (!queue.isEmpty()) {
  ByteBuffer buf = queue.get(0);
  socketChannel.write(buf);
  if (buf.remaining() > 0) {
   break;
  }
  queue.remove(0);
 }
 
 if (queue.isEmpty()) {
  key.interestOps(SelectionKey.OP_READ);
 }
}

辅助类

EchoWorker负责把接受到的客户端数据再发送回去,你具体的业务逻辑可以通过类似的方法来实现。

 代码如下 复制代码
import java.nio.channels.SocketChannel;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
 
public class EchoWorker implements Runnable {
 private BlockingQueue<ServerDataEvent> queue = new LinkedBlockingQueue<ServerDataEvent>();
 
 public void processData(NioServer server, SocketChannel socket, byte[] data, int count) {
  byte[] dataCopy = new byte[count];
  System.arraycopy(data, 0, dataCopy, 0, count);
  try {
   queue.put(new ServerDataEvent(server, socket, dataCopy));
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
 
 public void run() {
 
  while (true) {
   try {
    ServerDataEvent dataEvent = queue.take();
    // Return to sender
    dataEvent.server.send(dataEvent.socket, dataEvent.data);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
 }
}

ChangeRequest负责处理SocketChannel的状态

 代码如下 复制代码

import java.nio.channels.SocketChannel;
 
public class ChangeRequest {
 public static final int REGISTER = 1;
 public static final int CHANGEOPS = 2;
 
 public SocketChannel socket;
 public int type;
 public int ops;
 
 public ChangeRequest(SocketChannel socket, int type, int ops) {
  this.socket = socket;
  this.type = type;
  this.ops = ops;
 }
}

RspHandler是负责处理客户端数据,在实际的案例中,可以在此类中处理你自己的业务逻辑。

 代码如下 复制代码

public class RspHandler {
 private byte[] rsp = null;
 
 public synchronized boolean handleResponse(byte[] rsp) {
  this.rsp = rsp;
  this.notify();
  return true;
 }
 
 public synchronized void waitForResponse() {
  while (this.rsp == null) {
   try {
    this.wait();
   } catch (InterruptedException e) {
   }
  }
 
  System.out.println("PROCESS MESSAGE: " + new String(this.rsp));
 }
}

启动

 代码如下 复制代码

 // 客户端启动
public static void main(String[] args) throws UnknownHostException, IOException {
 for (int i = 0; i < 10; i++) {
  try {
   NioClient client = new NioClient(InetAddress.getLocalHost(), 9090);
   Thread t = new Thread(client);
   t.setDaemon(true);
   t.start();
   RspHandler handler = new RspHandler();
   client.send(("TEST MESSAGE " + i + 10).getBytes(), handler);
   handler.waitForResponse();
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}
 
// 服务端启动
public static void main(String[] args) {
 try {
  EchoWorker worker = new EchoWorker();
  new Thread(worker).start();
  new Thread(new NioServer(null, 9090, worker)).start();
 } catch (IOException e) {
  e.printStackTrace();
 }
}

时间: 2024-10-06 05:18:36

java中nio的socket通信实例程序的相关文章

Java和C#的socket通信相关(转)

这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己开发的一套组件. Java socket通信的组件也有很多,常用的大多数都是用的mina或者netty.游戏行业使用也是居多. 关于socket的底层写法,实在太多,我就不在BB. 这里我想说,C#和C++或者叫VC++把是使用小端序作为字节序.而java使用的是大端序作为字节序. 也就是说比如一个

关于java与c的socket通信问题

问题描述 关于java与c的socket通信问题 服务器端是java,客户端是c,通过socket,tcp通信,客户端将一张图片传输到服务器,服务器接收完成后返回一个'ok'字符串作为应答.运行的时候,服务器可以接收到客户端发来的图片,但是客户端无法接收到服务器的'ok'应答.对socket了解不深,求大神指点,谢谢了~代码如下: server(java): import java.net.*; import java.io.*; public class ServerTcp { public

Java中的MessageFormat.format用法实例

  这篇文章主要介绍了Java中的MessageFormat.format用法实例,本文先是讲解了MessageFormat的语法,然后给出了多个操作实例,需要的朋友可以参考下 MessageFormat本身与语言环境无关,而与用户提供给MessageFormat的模式和用于已插入参数的子格式模式有关,以生成适用于不同语言环境的消息. MessageFormat模式(主要部分): 代码如下: FormatElement: { ArgumentIndex }:是从0开始的入参位置索引. { Arg

关于java中applet问题,我的程序无法在html中显示出来,请问哪出问题了。

问题描述 关于java中applet问题,我的程序无法在html中显示出来,请问哪出问题了. 该程序是要画一个余弦图形. import java.applet.*; import java.awt.*; public class test3 extends Applet { public void start() { int x,y; Graphics g=getGraphics(); for(x=0;x<=750;x++) { g.drawString(".",x,200);

python服务器与android客户端socket通信实例_python

本文实例讲述了python服务器与android客户端socket通信的方法.分享给大家供大家参考.具体实现方法如下: 首先,服务器端使用python完成,下面为python代码: 复制代码 代码如下: #server.py  import socket  def getipaddrs(hostname):#只是为了显示IP,仅仅测试一下      result = socket.getaddrinfo(hostname, None, 0, socket.SOCK_STREAM)      re

Java中的对象和对象引用实例浅析

  本文实例讲述了Java中的对象和对象引用.分享给大家供大家参考.具体分析如下: 在Java中,有一组名词经常一起出现,它们就是"对象和对象引用",很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起了解一下对象和对象引用之间的区别和联系. 1.何谓对象? 在Java中有一句比较流行的话,叫做"万物皆对象",这是Java语言设计之初的理念之一.要理解什么是对象,需要跟类一起结合起来理解.下面这段话引自<Jav

JAVA中的final关键字用法实例详解_java

本文实例讲述了JAVA中的final关键字用法.分享给大家供大家参考,具体如下: 根据上下文环境,java的关键字final也存在着细微的区别,但通常指的是"这是无法改变的."不想改变的理由有两种:一种是效率,另一种是设计.由于两个原因相差很远,所以关键子final可能被误用. 接下来介绍一下使用到final的三中情况:数据,方法,类 final数据 许多编程语言都有某种方法,来向编译器告知一块数据是恒定不变的.有时数据的恒定不变是很有用的,例如: 1. 一个编译时恒定不变的常量 2.

Java中的对象和对象引用实例浅析_java

本文实例讲述了Java中的对象和对象引用.分享给大家供大家参考.具体分析如下: 在Java中,有一组名词经常一起出现,它们就是"对象和对象引用",很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起了解一下对象和对象引用之间的区别和联系. 1.何谓对象? 在Java中有一句比较流行的话,叫做"万物皆对象",这是Java语言设计之初的理念之一.要理解什么是对象,需要跟类一起结合起来理解.下面这段话引自<Java编

java中输出流OutputStream 类应用实例(转)

  OutputStream类该类是字节输出流的抽象类,定义了输出流的各种操作方法.这些方法的说明如表1所示.下面通过实例介绍如何使用OutputStream类向控制台输出字符串信息.步骤如下.(1)创建OutputStreamWriteStr类,在类中创建OutputStream类的实例对象out,并为其赋值为System类的out属性,该属性是控制台的输出流,再定义一个字节数组,该数组是将要输出到控制台的字符串信息,并通过out对象在控制台输出该字节数组的内容.关键代码如下:src\com\