想要学习Java的Socket通信,首先要学习Java的IO和NIO基础,这方面可以阅读《Java NIO 系列教程》。
下面展示自己代码熟悉Java的NIO编程的笔记。
1、缓冲区(Buffer)
/*
* 一、缓冲区(Buffer):在Java 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
*
* 根据数据类型不同(boolean除外),提供了相应类型的缓冲区
* ByteBuffer 那么实际上最常用的,因为网络传输的字节就是Byte
* CharBuffer
* ShortBuffer
* IntBuffer
* LongBuffer
* FloatBuffer
* DoubleBuffer
*
* 上述缓冲区的管理方式几乎一致,通过allocate()获取缓冲区
*
* 二、缓冲区存储数据的两个核心方法
* put() : 存入数据到缓冲区中
* get() : 获取缓冲区中的数据
*
* 三、缓冲区中的四个核心属性
* capacity : 容量,表示缓冲区中最大存储数据的容量
* limit : 界限,标识缓冲区中可以操作数据的大小(limit后面的数据不能进行读写的)
* position : 位置,标识缓冲区中正在操作数据的位置
*
* mark : 标记,标识记录当前position的位置,可以通过reset()恢复到mark的位置
*
* 0 <= mark <= position <= limit <= capacity
* 四、直接缓冲区和非直接缓冲区
* 非直接缓冲区: 通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中。
* 直接缓冲区 : 通过allockateDirect()方法分配直接缓存区,将缓冲区建立在物理内存中。可以提高效率。
* 如果有数据需要一直在内存空间中重复用,并且数据量不大的情况下,就用allockateDirect()这种方法。
* 毕竟内存空间是有限的。
*/
相关代码:
1 package com.demo.test; 2 3 import java.nio.ByteBuffer; 4 5 public class TestBuffer { 6 7 public void test3(){ 8 // 分配直接缓冲区 9 ByteBuffer buf = ByteBuffer.allocate(1024); 10 System.out.println(buf.isDirect());// 判断这个缓冲区是直接还是非直接的。 11 } 12 13 public void test2(){ 14 String str = "abcde"; 15 16 // 1、分配一个指定大小的缓冲区 17 ByteBuffer buf = ByteBuffer.allocate(1024); 18 19 // 2、利用put()存入数据到缓冲区中 20 System.out.println("往buf插入所有字符串的bytes是:"+str.getBytes()); 21 buf.put(str.getBytes()); 22 23 buf.flip();// 将插入模式转为查询模式,就是查询position位置会回到0 24 25 System.out.println("创建和缓冲区等长度的字节数组,用来从缓冲区取出字节并存储以待读取操作"); 26 byte[] dst = new byte[buf.limit()]; 27 System.out.println("从缓冲区中去除0开始的2位字节数据放进数组dst中"); 28 buf.get(dst, 0, 2); 29 System.out.println("打印dst字节数组"+new String(dst, 0, 2)); 30 System.out.println("现在缓冲区的操作起始位置:"+buf.position()); 31 32 // mark(); 标记 33 System.out.println("标记"); 34 buf.mark(); 35 buf.get(dst, 2, 2);// 取出从2开始的2位字节数据放进 36 System.out.println("打印dst字节数组"+new String(dst, 2, 2)); 37 System.out.println("现在缓冲区的操作起始位置:"+buf.position()); 38 39 // reset(); 回复到mark的位置 40 buf.reset(); 41 System.out.println("reset重置之后,缓冲区的操作起始位置回到:"+buf.position()); 42 43 // 判断缓冲区中是否还有剩余的数据 44 if(buf.hasRemaining()){ 45 // 获取缓冲区还可以操作的数量 46 System.out.println("缓冲区还有可以操作的数量"+buf.remaining()); 47 } 48 } 49 50 public void test(){ 51 String str = "abcde"; 52 53 // 1、分配一个指定大小的缓冲区 54 ByteBuffer buf = ByteBuffer.allocate(1024); 55 System.out.println("--调用allocate之后--"); 56 System.out.println(buf.capacity()); 57 System.out.println(buf.limit()); 58 System.out.println("position:"+buf.position()); 59 60 // 2、利用put()存入数据到缓冲区中 61 System.out.println("字符串的bytes是:"+str.getBytes()); 62 buf.put(str.getBytes()); 63 64 System.out.println("--调用put之后--"); 65 System.out.println(buf.capacity()); 66 System.out.println(buf.limit()); 67 System.out.println("position:"+buf.position()); 68 69 // 3、前面是存入数据的模式,存入5个字节之后,position的位置从原本0现在到5了 70 buf.flip(); 71 // 现在要将存储数据模式改成读取模式,position的位置会变回为0 72 73 System.out.println("--调用flip切换模式之后--"); 74 System.out.println(buf.capacity()); 75 System.out.println(buf.limit()); 76 System.out.println("position:"+buf.position()); 77 78 // 4、利用get()读取缓冲区中的数据 79 byte[] dst = new byte[buf.limit()];// 创建和缓冲区一样大的字节数据 80 buf.get(dst); 81 System.out.println(new String(dst,0,dst.length)); 82 83 System.out.println("--调用get()切换模式之后--"); 84 System.out.println(buf.capacity()); 85 System.out.println(buf.limit()); 86 System.out.println("position:"+buf.position());// 打印输出的结果会发现position变回5了 87 88 // 5、可重复读取 89 buf.rewind(); 90 91 System.out.println("--调用rewind()切换模式之后--"); 92 System.out.println(buf.capacity()); 93 System.out.println(buf.limit()); 94 System.out.println("position:"+buf.position()); 95 96 // 6、清空缓冲区。但是缓冲区中的数据依然存在,但是出于“被遗忘”状态 97 buf.clear(); 98 99 System.out.println("--调用clear()切换模式之后--"); 100 System.out.println(buf.capacity()); 101 System.out.println(buf.limit()); 102 System.out.println("position:"+buf.position()); 103 104 // 看看缓冲区有没有数据 105 System.out.println((char)(buf.get()+1)); 106 107 } 108 }
2、通道(Channel)
由java.nio.channels包定义的。Channel表示IO源与目标打开的连接。Channel类似于传统的“流”。只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互。
先完成文件的复制:
1 /* 2 * 一、通道(Channel):用于源节点与目标节点的连接。在Java NIO中负责缓冲区中数据的传输。 3 * Channel本身不存储数据,因此需要配合缓冲区进行传输 4 * 5 * 二、通道的主要实现类 6 * java.nio.channels.Channel接口 7 * |--FileChannel 8 * |--SocketChannel 9 * |--ServerSocketChannel 10 * |--DatagramChannel 11 * 12 * 三、获取通道 13 * 1、Java针对支持通道的类提供了getChannel()方法 14 * 本地: 15 * FileInputStream/FileOutputStream 16 * RandomAccessFile随机存储文件流 17 * 网络: 18 * Socket 19 * ServerSocket 20 * DatagramSocket 21 * 22 * 2、在JDK1.7中的NIO.2针对各个通道提供了一个静态的方法 23 * 3、在JDK1.7中的NIO.2的Files工具类的newByteChannel() 24 * 25 * 四、通道之间的数据传输 26 * transferFrom() 27 * transferTo() 28 * 29 * 五、分散(Scatter)和聚集(Gather) 30 * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中 31 * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中 32 */ 33 public class TestChannel { 34 // 1、利用通道完成文件的复制(非直接缓冲区) 35 public void test(){ 36 long start = System.currentTimeMillis(); 37 38 FileInputStream fis = null; 39 FileOutputStream fos = null; 40 // 获取通道 41 FileChannel inChannel = null; 42 FileChannel outChannel = null; 43 try { 44 fis = new FileInputStream("1.png"); 45 fos = new FileOutputStream("2.png"); 46 inChannel = fis.getChannel(); 47 outChannel = fos.getChannel(); 48 // 分批额指定大小的缓冲区 49 ByteBuffer buf = ByteBuffer.allocate(1024); 50 51 // 将通道中的数据存入缓冲区中 52 while(inChannel.read(buf)!=-1){ 53 buf.flip();//切换读取数据的模式 54 // 将缓冲区中的数据写入通道中 55 outChannel.write(buf); 56 buf.clear(); 57 } 58 } catch (IOException e) { 59 // TODO Auto-generated catch block 60 e.printStackTrace(); 61 } finally { 62 if(outChannel != null){ 63 try { 64 outChannel.close(); 65 } catch (IOException e) { 66 // TODO Auto-generated catch block 67 e.printStackTrace(); 68 } 69 } 70 if(inChannel != null){ 71 try { 72 inChannel.close(); 73 } catch (IOException e) { 74 // TODO Auto-generated catch block 75 e.printStackTrace(); 76 } 77 } 78 if(fos != null){ 79 try { 80 fos.close(); 81 } catch (IOException e) { 82 // TODO Auto-generated catch block 83 e.printStackTrace(); 84 } 85 } 86 if(fis != null){ 87 try { 88 fis.close(); 89 } catch (IOException e) { 90 // TODO Auto-generated catch block 91 e.printStackTrace(); 92 } 93 } 94 } 95 long end = System.currentTimeMillis(); 96 System.out.println("1、耗费的时间:"+(end - start)); 97 98 } 99 100 // 2、利用通道完成文件的复制(直接缓冲区,内存映射文件的方式) 101 public void test2(){ 102 103 long start = System.currentTimeMillis(); 104 105 FileChannel inChannel = null; 106 // CREATE_NEW 存在就报错 107 FileChannel outChannel = null; 108 try { 109 inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ); 110 outChannel = FileChannel.open(Paths.get("3.png"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); 111 112 // 物理内存映射文件 113 MappedByteBuffer inMapByteBuffer = inChannel.map(MapMode.READ_ONLY,0,inChannel.size()); 114 MappedByteBuffer outMapByteBuffer = outChannel.map(MapMode.READ_WRITE,0,inChannel.size()); 115 116 // 直接对缓冲区进行数据的读写操作 117 byte[] dst = new byte[inMapByteBuffer.limit()]; 118 inMapByteBuffer.get(dst); 119 outMapByteBuffer.put(dst); 120 } catch (IOException e) { 121 // TODO Auto-generated catch block 122 e.printStackTrace(); 123 } finally{ 124 if(inChannel!=null){ 125 try { 126 inChannel.close(); 127 } catch (IOException e) { 128 // TODO Auto-generated catch block 129 e.printStackTrace(); 130 } 131 } 132 if(outChannel!=null){ 133 try { 134 outChannel.close(); 135 } catch (IOException e) { 136 // TODO Auto-generated catch block 137 e.printStackTrace(); 138 } 139 } 140 } 141 142 long end = System.currentTimeMillis(); 143 System.out.println("2、耗费的时间:"+(end - start)); 144 145 } 146 147 // 通道之间的数据传输 148 public void test3(){ 149 FileChannel inChannel = null; 150 // CREATE_NEW 存在就报错 151 FileChannel outChannel = null; 152 153 try { 154 inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ); 155 outChannel = FileChannel.open(Paths.get("4.png"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); 156 157 // inChannel.transferTo(0, inChannel.size(), outChannel); 158 outChannel.transferFrom(inChannel, 0, inChannel.size()); 159 } catch (IOException e) { 160 // TODO Auto-generated catch block 161 e.printStackTrace(); 162 } finally{ 163 if(inChannel!=null){ 164 try { 165 inChannel.close(); 166 } catch (IOException e) { 167 // TODO Auto-generated catch block 168 e.printStackTrace(); 169 } 170 } 171 if(outChannel!=null){ 172 try { 173 outChannel.close(); 174 } catch (IOException e) { 175 // TODO Auto-generated catch block 176 e.printStackTrace(); 177 } 178 } 179 } 180 181 } 182 183 // 分散(Scatter)和聚集(Gather) 184 public void test4(){ 185 // 文件路径: webapp/index.html 186 try { 187 RandomAccessFile raFile = new RandomAccessFile("webapp/index.html", "rw"); 188 FileChannel fileChannel = raFile.getChannel(); 189 190 ByteBuffer tBuffer = ByteBuffer.allocate(24); 191 ByteBuffer tBuffer2 = ByteBuffer.allocate(80); 192 193 // 分散读取到两个字节缓冲区数组中 194 ByteBuffer[] bBuffers = {tBuffer,tBuffer2}; 195 fileChannel.read(bBuffers); 196 // 打印之前需要修改一下缓冲区的写入模式为读取模式 197 for (ByteBuffer byteBuffer : bBuffers) { 198 byteBuffer.flip(); 199 } 200 System.out.println(new String(bBuffers[0].array(),0,bBuffers[0].limit())); 201 System.out.println("==========="); 202 System.out.println(new String(bBuffers[1].array(),0,bBuffers[1].limit())); 203 204 205 // 聚集写入到通道中 206 RandomAccessFile randomAccFile2 = new RandomAccessFile("test.html", "rw"); 207 FileChannel fileChannel2 = randomAccFile2.getChannel(); 208 209 fileChannel2.write(bBuffers); 210 211 212 } catch (IOException e) { 213 // TODO Auto-generated catch block 214 e.printStackTrace(); 215 } 216 } 217 }
>>1、字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则Java虚拟机会尽最大努力直接在 此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后), 虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
>>2、直接字节缓冲区可以通过调用此类的allocateDirect()工厂方法来创建。此方法返回的缓冲区进行分配和取消 分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对 应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的 本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好 处时分配它们。
>>3、直接字节缓冲区还可以通过FileChannel的map()方法将文件区域直接映射到内存中来创建。该方法返回 MappedByteBuffer 。Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区 中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在 访问期间或稍后的某个时间导致抛出不确定的异常。
>>4、字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其isDirect()方法来确定。提供此方法是为了能够在 性能关键型代码中执行显式缓冲区管理。
3、字符集(Charset)
编码:字符串 --> 字节数组 将字符序列转为字节序列的过程叫编码
解码:字节数组 --> 字符串 将字节序列转为字符序列的过程叫解码
代码:
1 // 编码解码 2 public void test6(){ 3 // 获取字符集合中的GBK字符集 4 Charset cs1 = Charset.forName("GBK"); 5 6 // 获取编码器 7 CharsetEncoder ce = cs1.newEncoder(); 8 // 获取解码器 9 CharsetDecoder cd = cs1.newDecoder(); 10 11 // 创建字符串缓冲区 12 CharBuffer cBuf = CharBuffer.allocate(1024); 13 cBuf.put("何杨很帅!"); 14 cBuf.flip(); 15 16 try { 17 // 开始编码 18 ByteBuffer bBuf = ce.encode(cBuf); 19 System.out.println("用GBK编码之后:"); 20 for (int i = 0; i < bBuf.limit(); i++) { 21 System.out.println(bBuf.get()); 22 } 23 24 // 然后开始解码回去 25 bBuf.flip(); 26 CharBuffer cBuf2 = cd.decode(bBuf); 27 System.out.println("用GBK解码之后:"); 28 System.out.println(cBuf2.toString()); 29 30 System.out.println("------------"); 31 32 System.out.println("获取UTF-8字符集"); 33 Charset cs2 = Charset.forName("UTF-8"); 34 bBuf.flip(); 35 CharBuffer cBuf3 = cs2.decode(bBuf);// 也可以直接用字符集进行解码 36 37 System.out.println("用字符集进行解码之后:"+cBuf3.toString()); 38 39 40 } catch (CharacterCodingException e) { 41 // TODO Auto-generated catch block 42 e.printStackTrace(); 43 } 44 45 }
4、使用NIO完成网络通信
1 /* 2 * 一、使用NIO完成网络通信的三个核心: 3 * 1、通道(Channel):负责连接 4 * java.nio.channels.Channel 接口: 5 * |--SelectableChannel 6 * |--ServerSocketChannel 7 * |--DatagramChannel 8 * 9 * |--Pipe.SinkChannel 10 * |--Pipe.SourceChannel 11 * 12 * 2、缓冲区(Buffer):负责数据的存取 13 * 14 * 3、选择器(Selector):是SelectableChannel的多路复合器。 15 * 用于监控SelectableChannel的IO状况 16 */
4-1、下面先实现阻塞式通道
1 public class TestBlockingNIO { 2 // 客户端 3 @Test 4 public void client(){ 5 try { 6 System.out.println("客户端开始创建。"); 7 // 1、获取Socket通道 8 SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898)); 9 // 2、分配指定大小的缓冲区 10 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 11 // 3、读取本地文件,并发到服务端 12 // 先需要文件通道获取本地文件 13 FileChannel fileChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.READ); 14 System.out.println("开始发送图片数据"); 15 while (fileChannel.read(byteBuffer) != -1) { 16 byteBuffer.flip(); 17 socketChannel.write(byteBuffer); 18 byteBuffer.clear(); 19 System.out.println("发送一次"); 20 } 21 22 System.out.println("--开始停止输出--"); 23 // 4、客户端发送完数据之后要停止通道的输出 24 socketChannel.shutdownOutput(); 25 // --发送完了图片数据之后才会进行下面一步,其实这里也是单线程阻塞的。-- 26 System.out.println("--发送完了图片数据之后才会进行下面一步,其实这里也是单线程阻塞的。--"); 27 28 // 5、接收服务端的反馈 29 int len = 0; 30 while((len = socketChannel.read(byteBuffer))!=-1){ 31 byteBuffer.flip(); 32 System.out.println(new String(byteBuffer.array(), 0, len)); 33 byteBuffer.clear(); 34 35 } 36 socketChannel.shutdownInput();// 停止客户端的接收输入 37 38 // 6、关闭通道 39 fileChannel.close(); 40 socketChannel.close(); 41 42 } catch (IOException e) { 43 // TODO Auto-generated catch block 44 e.printStackTrace(); 45 } 46 } 47 48 // 服务端 49 @Test 50 public void server(){ 51 try { 52 System.out.println("服务器开始启动。。。"); 53 // 1、获取服务端SocketChannel 54 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 55 // 2、绑定连接端口号 56 serverSocketChannel.bind(new InetSocketAddress(9898)); 57 // 3、获取客户端连接的通道 58 SocketChannel sChannel = serverSocketChannel.accept(); 59 60 // 4、创建一个指定大小的缓冲区 61 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 62 63 // 5、接收的数据通道也要文件通道 StandardOpenOption.CREATE表示有文件就覆盖,没文件就创建 64 FileChannel fileChannel = FileChannel.open(Paths.get("4.png"), StandardOpenOption.WRITE,StandardOpenOption.CREATE); 65 66 System.out.println("开始接收图片数据"); 67 while (sChannel.read(byteBuffer)!=-1) { 68 byteBuffer.flip(); 69 fileChannel.write(byteBuffer); 70 byteBuffer.clear(); 71 System.out.println("接收一次"); 72 } 73 74 System.out.println("接收完毕客户端发来的数据,开始反馈数据给客户端"); 75 76 // 6、发送反馈给客户端 77 byteBuffer.put("服务器接收数据成功".getBytes()); 78 byteBuffer.flip(); 79 sChannel.write(byteBuffer); 80 81 // 7、关闭通道 82 fileChannel.close(); 83 sChannel.close(); 84 serverSocketChannel.close(); 85 86 } catch (IOException e) { 87 // TODO Auto-generated catch block 88 e.printStackTrace(); 89 } 90 } 91 }
4-2、然后是实现非阻塞通道
1 package com.demo.test; 2 3 import java.io.IOException; 4 import java.net.InetSocketAddress; 5 import java.nio.ByteBuffer; 6 import java.nio.channels.SelectionKey; 7 import java.nio.channels.Selector; 8 import java.nio.channels.ServerSocketChannel; 9 import java.nio.channels.SocketChannel; 10 import java.util.Iterator; 11 import java.util.Scanner; 12 13 import org.junit.Test; 14 15 public class TestNonBlockingNIO { 16 17 @Test 18 public void startClient(){ 19 SocketChannel sChannel = null; 20 // 获取通道 21 try { 22 23 System.out.println("启动客户端"); 24 25 sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898)); 26 27 // 切换非阻塞模式 28 sChannel.configureBlocking(false); 29 30 ByteBuffer buf = ByteBuffer.allocate(1024); 31 32 System.out.println("开始通过输入框输入给服务器端发送数据"); 33 Scanner scan = new Scanner(System.in); 34 while (scan.hasNext()) { 35 String str = scan.next(); 36 buf.put(str.getBytes()); 37 buf.flip(); 38 sChannel.write(buf); 39 buf.clear(); 40 } 41 42 scan.close(); 43 44 45 } catch (IOException e) { 46 // TODO Auto-generated catch block 47 e.printStackTrace(); 48 } finally { 49 if (sChannel != null) { 50 try { 51 sChannel.close(); 52 System.out.println("关闭客户端Socket通道"); 53 } catch (IOException e) { 54 // TODO Auto-generated catch block 55 e.printStackTrace(); 56 } 57 } 58 } 59 } 60 61 62 // 启动非阻塞的NIO的Socket服务器程序 63 @Test 64 public void startServer(){ 65 ServerSocketChannel serverSocketChannel = null; 66 try { 67 System.out.println("启动服务器Socket。"); 68 69 // --ServerSocketChannel-- 70 // 直接用 服务器Socket通道类 获取 服务器Socket通道 71 serverSocketChannel = ServerSocketChannel.open(); 72 // 设置为非阻塞模式 73 serverSocketChannel.configureBlocking(false); 74 // 用 服务器Socket通道 来绑定端口号,端口是需要new的对象 75 serverSocketChannel.bind(new InetSocketAddress(9898)); 76 77 // --Selector-- 78 // 获取选择器,选择器也是通过类似类方法的open方法创建 79 Selector sel = Selector.open(); 80 81 // --注册-- 82 // 服务器Socket通道注册选择器,指定的接收事件 83 serverSocketChannel.register(sel, SelectionKey.OP_ACCEPT); 84 85 // --开始处理接收到的-- 86 // 轮询式的获取选择器上已经准备就绪的事件 87 while (sel.select() > 0) { 88 // 使用迭代器获取所有的 "已经接收准备就绪的" 选择键 89 Iterator<SelectionKey> it = sel.selectedKeys().iterator(); 90 91 while (it.hasNext()) { 92 SelectionKey selKey = it.next(); 93 // 如果是接收就绪 94 if (selKey.isAcceptable()) { 95 System.out.println("接收就绪,接收客户端的Socket通道。"); 96 // 通过服务器Socket通道获取客户端的连接的Socket通道 97 SocketChannel socketChannel = serverSocketChannel.accept(); 98 // 设置这个客户端发过来的通道为非阻塞模式 99 socketChannel.configureBlocking(false); 100 // 再将这个客户端发过来的通道注册选择器,因为已经准备好了,那就注册设置为读就绪状态 101 socketChannel.register(sel, SelectionKey.OP_READ); 102 103 // 如果是读就绪状态 104 }else if (selKey.isReadable()) { 105 // System.out.println("读就绪,开始读取客户端的Socket通道里的数据。"); 106 // 通过选择器获取这个通道 107 SocketChannel socketChannel = (SocketChannel) selKey.channel(); 108 109 // 读取数据 110 // 创建一个ByteBuffer 111 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); 112 113 // 从通道中读取数据到字节缓冲 114 int len; 115 while ((len = socketChannel.read(byteBuffer))>0) { 116 byteBuffer.flip(); 117 System.out.println("开始读取的数据是:"); 118 System.out.println(new String(byteBuffer.array(),0,len)); 119 byteBuffer.clear(); 120 } 121 122 } 123 // 记得用完it之后要取消掉 124 it.remove(); 125 } 126 } 127 128 129 } catch (IOException e) { 130 // TODO Auto-generated catch block 131 e.printStackTrace(); 132 }finally{ 133 try { 134 if (serverSocketChannel != null) { 135 serverSocketChannel.close(); 136 } 137 } catch (IOException e) { 138 // TODO Auto-generated catch block 139 e.printStackTrace(); 140 } 141 } 142 } 143 144 145 }