参考网上的资料:
Java NIO 系列教程:http://www.iteye.com/magazines/132-Java-NIO
SocketChannel续2---各种注意点:http://www.360doc.com/content/12/0902/22/495229_233834423.shtml
Java NIO类库Selector机制解析(下):http://blog.csdn.net/haoel/article/details/2224069
NIO Selector示意图
服务端用java实现,客户端用android实现
简单功能(目的为学习基本原理):实现了手机端发送消息到服务端,服务端成功接收
服务端
package cn; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author gn * @date 2015-8-7 上午11:36:25 */ import java.nio.channels.*; import java.nio.charset.*; import java.net.*; import java.io.*; import java.util.*; import java.nio.*; public class SocketServerDemo { //Socket协议服务端 private int port = 9875; private ServerSocketChannel serverSocketChannel; private Charset charset = Charset.forName("UTF-8"); private Selector selector = null; public SocketServerDemo() throws IOException { selector = Selector.open(); serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().setReuseAddress(true); serverSocketChannel.socket().bind(new InetSocketAddress(port)); System.out.println("服务器启动"); } /* 编码过程 */ public ByteBuffer encode(String str) { return charset.encode(str); } /* 解码过程 */ public String decode(ByteBuffer bb) { return charset.decode(bb).toString(); } /* 服务器服务方法 */ public void service() throws IOException { serverSocketChannel.configureBlocking(false); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); /** 外循环,已经发生了SelectionKey数目 */ while (selector.select() > 0) { /* 得到已经被捕获了的SelectionKey的集合 */ Iterator iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = null; try { key = (SelectionKey) iterator.next(); iterator.remove(); if (key.isAcceptable()) { ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); SocketChannel sc = ssc.accept(); System.out .println("客户端机子的地址是 " + sc.socket().getRemoteSocketAddress() + " 客户端机机子的端口号是 " + sc.socket().getLocalPort()); sc.configureBlocking(false); ByteBuffer buffer = ByteBuffer.allocate(1024); sc.register(selector, SelectionKey.OP_READ , buffer);//buffer通过附件方式,传递 } if (key.isReadable()) { reveice(key); } if (key.isWritable()) { // send(key); } } catch (IOException e) { e.printStackTrace(); try { if (key != null) { key.cancel(); key.channel().close(); } } catch (ClosedChannelException cex) { e.printStackTrace(); } } } /* 内循环完 */ } /* 外循环完 */ } int x = 1; /* 接收 */ public void reveice(SelectionKey key) throws IOException { if (key == null) return; //***用SelectionKey.attachment()获取客户端消息***// //:通过附件方式,接收数据 // ByteBuffer buff = (ByteBuffer) key.attachment(); // SocketChannel sc = (SocketChannel) key.channel(); // buff.limit(buff.capacity()); // buff.position(0); // sc.read(buff); // buff.flip(); // String reviceData = decode(buff); // System.out.println("接收:" + reviceData); //***用channel.read()获取客户端消息***// //:接收时需要考虑字节长度 SocketChannel sc = (SocketChannel) key.channel(); String content = ""; //create buffer with capacity of 48 bytes ByteBuffer buf = ByteBuffer.allocate(3);//java里一个(utf-8)中文3字节,gbk中文占2个字节 int bytesRead = sc.read(buf); //read into buffer. while (bytesRead >0) { buf.flip(); //make buffer ready for read while(buf.hasRemaining()){ buf.get(new byte[buf.limit()]); // read 1 byte at a time content += new String(buf.array()); } buf.clear(); //make buffer ready for writing bytesRead = sc.read(buf); } System.out.println("接收:" + content.trim()); // sc.write(ByteBuffer.wrap(reviceData.getBytes())); // try { // sc.write(ByteBuffer.wrap(new String( // "测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试" + (++x)).getBytes()));// 将消息回送给客户端 // } catch (IOException e1) { // e1.printStackTrace(); // } } int y = 0; public void send(SelectionKey key) { // if (key == null) // return; // ByteBuffer buff = (ByteBuffer) key.attachment(); // SocketChannel sc = (SocketChannel) key.channel(); // try { // sc.write(ByteBuffer.wrap(new String("aaaa").getBytes())); // } catch (IOException e1) { // e1.printStackTrace(); // } System.out.println("send2() " +(++y)); } /* 发送文件 */ public void sendFile(SelectionKey key) { if (key == null) return; ByteBuffer buff = (ByteBuffer) key.attachment(); SocketChannel sc = (SocketChannel) key.channel(); String data = decode(buff); if (data.indexOf("get") == -1) return; String subStr = data.substring(data.indexOf(" "), data.length()); System.out.println("截取之后的字符串是 " + subStr); FileInputStream fileInput = null; try { fileInput = new FileInputStream(subStr); FileChannel fileChannel = fileInput.getChannel(); fileChannel.transferTo(0, fileChannel.size(), sc); fileChannel.close(); } catch (IOException e) { e.printStackTrace(); } finally { try { fileInput.close(); } catch (IOException ex) { ex.printStackTrace(); } } } public static void main(String[] args) throws IOException { new SocketServerDemo().service(); } }
package com.android.mealplus.test; /** * Created by gn on 0029 2015-07-29. */ import android.telephony.TelephonyManager; import android.util.Log; import com.android.mealplus.MealPlusApplication; import com.android.mealplus.ui.common.DataTypeByte; import com.android.mealplus.ui.common.NoInData; import com.android.mealplus.utils.UtilsTools; import com.android.mealplus.utils.ValidateTools; import java.io.IOException; import java.io.UnsupportedEncodingException; 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.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Scanner; public class TCPClient { // 信道选择器 private Selector selector; // 与服务器通信的信道 SocketChannel socketChannel; // 要连接的服务器Ip地址 private String hostIp; // 要连接的远程服务器在监听的端口 private int hostListenningPort; String imei; public TCPClient(String HostIp, int HostListenningPort, String imei) throws IOException { this.hostIp = HostIp; this.hostListenningPort = HostListenningPort; this.imei = imei; initialize(); } /** * 初始化 * * @throws IOException */ private void initialize() throws IOException { try { // 获得一个Socket通道 SocketChannel channel = SocketChannel.open(); // 设置通道为非阻塞 channel.configureBlocking(false); // 获得一个通道管理器 this.selector = Selector.open(); // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调 //用channel.finishConnect();才能完成连接 channel.connect(new InetSocketAddress(hostIp, hostListenningPort)); //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。 channel.register(selector, SelectionKey.OP_CONNECT); } catch (Exception e) { Log.d("a", e.toString()); } // 启动读取线程 new TCPClientReadThread(selector, imei); } // /** // * 发送字符串到服务器 // * // * @param message // * @throws IOException // */ // public void sendMsg(String message) throws IOException { // ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("UTF-8")); // // socketChannel.write(writeBuffer); // } }
package com.android.mealplus.test; /** * Created by gn on 0029 2015-07-29. */ import android.util.Log; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Set; public class TCPClientReadThread implements Runnable { // 监听器,如果缓冲区有数据,通知程序接收 private Selector selector; String imei; public TCPClientReadThread(Selector selector, String imei) { this.selector = selector; this.imei = imei; new Thread(this).start(); } @Override public void run() { try { // 轮询访问selector while (true) { selector.select();//如果队列有新的Channel加入,那么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("我们测试中国ab").getBytes())); //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。 channel.register(this.selector, SelectionKey.OP_READ ); } else if (key.isReadable()) { read(key); } } } } catch (IOException e) { e.printStackTrace(); Log.d("aa", e.toString()); } } /** * 处理读取服务端发来的信息 的事件 * * @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(); Log.d("aa", "服务端收到信息:" + msg); // //发送消息到服务端 // ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); // channel.write(outBuffer);// 将消息回送给客户端 } public static final String btyetoString(byte[] bArray) { StringBuffer sb = new StringBuffer(bArray.length); String sTemp; for (int i = 0; i < bArray.length; i++) { sTemp = Integer.toHexString(0xFF & bArray[i]); if (sTemp.length() == 1) { sb.append(0); } sb.append(sTemp.toUpperCase()); sb.append(" "); } return sb.toString(); } }
new Thread() { @Override public void run() { try { client = new TCPClient("192.168.1.118", port, ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).getDeviceId()); } catch (IOException e) { e.printStackTrace(); } finally { } } }.start();