|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: java NIO 服务端客户端代码 |
| 4 | +categories: code |
| 5 | +tags: java code |
| 6 | +comments: true |
| 7 | +--- |
| 8 | + |
| 9 | +* content |
| 10 | +{:toc} |
| 11 | + |
| 12 | +**服务器端:** |
| 13 | + |
| 14 | + package cn.nio; |
| 15 | + |
| 16 | + import java.io.IOException; |
| 17 | + import java.net.InetSocketAddress; |
| 18 | + import java.nio.ByteBuffer; |
| 19 | + import java.nio.channels.SelectionKey; |
| 20 | + import java.nio.channels.Selector; |
| 21 | + import java.nio.channels.ServerSocketChannel; |
| 22 | + import java.nio.channels.SocketChannel; |
| 23 | + import java.util.Iterator; |
| 24 | + |
| 25 | + /** |
| 26 | + * NIO服务端 |
| 27 | + */ |
| 28 | + public class NIOServer { |
| 29 | + //通道管理器 |
| 30 | + private Selector selector; |
| 31 | + |
| 32 | + /** |
| 33 | + * 获得一个ServerSocket通道,并对该通道做一些初始化的工作 |
| 34 | + * @param port 绑定的端口号 |
| 35 | + * @throws IOException |
| 36 | + */ |
| 37 | + public void initServer(int port) throws IOException { |
| 38 | + // 获得一个ServerSocket通道 |
| 39 | + ServerSocketChannel serverChannel = ServerSocketChannel.open(); |
| 40 | + // 设置通道为非阻塞 |
| 41 | + serverChannel.configureBlocking(false); |
| 42 | + // 将该通道对应的ServerSocket绑定到port端口 |
| 43 | + serverChannel.socket().bind(new InetSocketAddress(port)); |
| 44 | + // 获得一个通道管理器 |
| 45 | + this.selector = Selector.open(); |
| 46 | + //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后, |
| 47 | + //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。 |
| 48 | + serverChannel.register(selector, SelectionKey.OP_ACCEPT); |
| 49 | + } |
| 50 | + |
| 51 | + /** |
| 52 | + * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 |
| 53 | + * @throws IOException |
| 54 | + */ |
| 55 | + @SuppressWarnings("unchecked") |
| 56 | + public void listen() throws IOException { |
| 57 | + System.out.println("服务端启动成功!"); |
| 58 | + // 轮询访问selector |
| 59 | + while (true) { |
| 60 | + //当注册的事件到达时,方法返回;否则,该方法会一直阻塞 |
| 61 | + selector.select(); |
| 62 | + // 获得selector中选中的项的迭代器,选中的项为注册的事件 |
| 63 | + Iterator ite = this.selector.selectedKeys().iterator(); |
| 64 | + while (ite.hasNext()) { |
| 65 | + SelectionKey key = (SelectionKey) ite.next(); |
| 66 | + // 删除已选的key,以防重复处理 |
| 67 | + ite.remove(); |
| 68 | + // 客户端请求连接事件 |
| 69 | + if (key.isAcceptable()) { |
| 70 | + ServerSocketChannel server = (ServerSocketChannel) key |
| 71 | + .channel(); |
| 72 | + // 获得和客户端连接的通道 |
| 73 | + SocketChannel channel = server.accept(); |
| 74 | + // 设置成非阻塞 |
| 75 | + channel.configureBlocking(false); |
| 76 | + |
| 77 | + //在这里可以给客户端发送信息哦 |
| 78 | + channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes())); |
| 79 | + //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 |
| 80 | + channel.register(this.selector, SelectionKey.OP_READ); |
| 81 | + |
| 82 | + // 获得了可读的事件 |
| 83 | + } else if (key.isReadable()) { |
| 84 | + read(key); |
| 85 | + } |
| 86 | + |
| 87 | + } |
| 88 | + |
| 89 | + } |
| 90 | + } |
| 91 | + /** |
| 92 | + * 处理读取客户端发来的信息 的事件 |
| 93 | + * @param key |
| 94 | + * @throws IOException |
| 95 | + */ |
| 96 | + public void read(SelectionKey key) throws IOException{ |
| 97 | + // 服务器可读取消息:得到事件发生的Socket通道 |
| 98 | + SocketChannel channel = (SocketChannel) key.channel(); |
| 99 | + // 创建读取的缓冲区 |
| 100 | + ByteBuffer buffer = ByteBuffer.allocate(10); |
| 101 | + channel.read(buffer); |
| 102 | + byte[] data = buffer.array(); |
| 103 | + String msg = new String(data).trim(); |
| 104 | + System.out.println("服务端收到信息:"+msg); |
| 105 | + ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); |
| 106 | + channel.write(outBuffer);// 将消息回送给客户端 |
| 107 | + } |
| 108 | + |
| 109 | + /** |
| 110 | + * 启动服务端测试 |
| 111 | + * @throws IOException |
| 112 | + */ |
| 113 | + public static void main(String[] args) throws IOException { |
| 114 | + NIOServer server = new NIOServer(); |
| 115 | + server.initServer(8000); |
| 116 | + server.listen(); |
| 117 | + } |
| 118 | + |
| 119 | + } |
| 120 | + |
| 121 | +**客户端:** |
| 122 | + |
| 123 | + package cn.nio; |
| 124 | + |
| 125 | + import java.io.IOException; |
| 126 | + import java.net.InetSocketAddress; |
| 127 | + import java.nio.ByteBuffer; |
| 128 | + import java.nio.channels.SelectionKey; |
| 129 | + import java.nio.channels.Selector; |
| 130 | + import java.nio.channels.SocketChannel; |
| 131 | + import java.util.Iterator; |
| 132 | + |
| 133 | + /** |
| 134 | + * NIO客户端 |
| 135 | + */ |
| 136 | + public class NIOClient { |
| 137 | + //通道管理器 |
| 138 | + private Selector selector; |
| 139 | + |
| 140 | + /** |
| 141 | + * 获得一个Socket通道,并对该通道做一些初始化的工作 |
| 142 | + * @param ip 连接的服务器的ip |
| 143 | + * @param port 连接的服务器的端口号 |
| 144 | + * @throws IOException |
| 145 | + */ |
| 146 | + public void initClient(String ip,int port) throws IOException { |
| 147 | + // 获得一个Socket通道 |
| 148 | + SocketChannel channel = SocketChannel.open(); |
| 149 | + // 设置通道为非阻塞 |
| 150 | + channel.configureBlocking(false); |
| 151 | + // 获得一个通道管理器 |
| 152 | + this.selector = Selector.open(); |
| 153 | + |
| 154 | + // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调 |
| 155 | + //用channel.finishConnect();才能完成连接 |
| 156 | + channel.connect(new InetSocketAddress(ip,port)); |
| 157 | + //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。 |
| 158 | + channel.register(selector, SelectionKey.OP_CONNECT); |
| 159 | + } |
| 160 | + |
| 161 | + /** |
| 162 | + * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 |
| 163 | + * @throws IOException |
| 164 | + */ |
| 165 | + @SuppressWarnings("unchecked") |
| 166 | + public void listen() throws IOException { |
| 167 | + // 轮询访问selector |
| 168 | + while (true) { |
| 169 | + selector.select(); |
| 170 | + // 获得selector中选中的项的迭代器 |
| 171 | + Iterator ite = this.selector.selectedKeys().iterator(); |
| 172 | + while (ite.hasNext()) { |
| 173 | + SelectionKey key = (SelectionKey) ite.next(); |
| 174 | + // 删除已选的key,以防重复处理 |
| 175 | + ite.remove(); |
| 176 | + // 连接事件发生 |
| 177 | + if (key.isConnectable()) { |
| 178 | + SocketChannel channel = (SocketChannel) key |
| 179 | + .channel(); |
| 180 | + // 如果正在连接,则完成连接 |
| 181 | + if(channel.isConnectionPending()){ |
| 182 | + channel.finishConnect(); |
| 183 | + |
| 184 | + } |
| 185 | + // 设置成非阻塞 |
| 186 | + channel.configureBlocking(false); |
| 187 | + |
| 188 | + //在这里可以给服务端发送信息哦 |
| 189 | + channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes())); |
| 190 | + //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。 |
| 191 | + channel.register(this.selector, SelectionKey.OP_READ); |
| 192 | + |
| 193 | + // 获得了可读的事件 |
| 194 | + } else if (key.isReadable()) { |
| 195 | + read(key); |
| 196 | + } |
| 197 | + |
| 198 | + } |
| 199 | + |
| 200 | + } |
| 201 | + } |
| 202 | + /** |
| 203 | + * 处理读取服务端发来的信息 的事件 |
| 204 | + * @param key |
| 205 | + * @throws IOException |
| 206 | + */ |
| 207 | + public void read(SelectionKey key) throws IOException{ |
| 208 | + //和服务端的read方法一样 |
| 209 | + } |
| 210 | + |
| 211 | + |
| 212 | + /** |
| 213 | + * 启动客户端测试 |
| 214 | + * @throws IOException |
| 215 | + */ |
| 216 | + public static void main(String[] args) throws IOException { |
| 217 | + NIOClient client = new NIOClient(); |
| 218 | + client.initClient("localhost",8000); |
| 219 | + client.listen(); |
| 220 | + } |
| 221 | + |
| 222 | + } |
0 commit comments