├── README ├── testssl.ks └── src └── org └── nio └── socket ├── ClientHandler.java ├── test ├── TestClient.java └── TestServer.java ├── NIOSocketOutputStream.java ├── NIOSocketInputStream.java ├── SocketServer.java ├── SSLHandler.java └── SocketClient.java /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testssl.ks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buksy/java-nio-socket/HEAD/testssl.ks -------------------------------------------------------------------------------- /src/org/nio/socket/ClientHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright [2012] [Gihan Munasinghe ayeshka@gmail.com ] 3 | 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package org.nio.socket; 19 | 20 | public interface ClientHandler { 21 | 22 | public void handle(SocketClient cleint); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/org/nio/socket/test/TestClient.java: -------------------------------------------------------------------------------- 1 | package org.nio.socket.test; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStreamReader; 5 | import java.io.OutputStream; 6 | import java.net.InetSocketAddress; 7 | 8 | import org.nio.socket.SocketClient; 9 | 10 | public class TestClient { 11 | 12 | public static void main(String args[]) { 13 | for (int i = 0 ; i< 10 ; i++) { 14 | new Thread() { 15 | public void run() { 16 | SocketClient client = new SocketClient(new InetSocketAddress("127.0.0.1", 8080)); 17 | try { 18 | client.setSSLContext(TestServer.getSSLContext()); 19 | client.connect(); 20 | while(!client.isConnected()) { 21 | System.out.println("Cleint not connected"); 22 | sleep(1000); 23 | } 24 | OutputStream out = client.getOutputStream(); 25 | out.write(("Hello from Therad "+Thread.currentThread().getId()).getBytes()); 26 | out.write("\r\n".getBytes()); 27 | out.flush(); 28 | 29 | System.out.println("send hello"); 30 | 31 | BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); 32 | System.out.println(reader.readLine()); 33 | client.close(); 34 | 35 | } catch (Exception e) { 36 | // TODO Auto-generated catch block 37 | e.printStackTrace(); 38 | } 39 | 40 | 41 | } 42 | }.start(); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/org/nio/socket/test/TestServer.java: -------------------------------------------------------------------------------- 1 | package org.nio.socket.test; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedReader; 5 | import java.io.BufferedWriter; 6 | import java.io.FileInputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.io.OutputStream; 11 | import java.io.OutputStreamWriter; 12 | import java.io.Reader; 13 | import java.net.InetSocketAddress; 14 | import java.security.KeyStore; 15 | import java.util.concurrent.Executors; 16 | 17 | import javax.net.ssl.KeyManagerFactory; 18 | import javax.net.ssl.SSLContext; 19 | import javax.net.ssl.TrustManagerFactory; 20 | 21 | import org.nio.socket.ClientHandler; 22 | import org.nio.socket.SocketClient; 23 | import org.nio.socket.SocketServer; 24 | 25 | 26 | public class TestServer implements ClientHandler{ 27 | 28 | @Override 29 | public void handle(SocketClient cleint) { 30 | InputStream in = cleint.getInputStream(); 31 | OutputStream out = cleint.getOutputStream(); 32 | 33 | String str = Thread.currentThread().toString(); 34 | 35 | 36 | BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 37 | 38 | BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out)); 39 | String cli = ""; 40 | try { 41 | // while(in.available() > 0) { 42 | // client += reader.readLine(); 43 | // } 44 | while ((cli = reader.readLine())!=null) { 45 | writer.write("Server "+str+" "+cli); 46 | writer.newLine(); 47 | writer.flush(); 48 | } 49 | 50 | } catch (IOException e) { 51 | // TODO Auto-generated catch block 52 | e.printStackTrace(); 53 | } 54 | 55 | 56 | } 57 | 58 | public static SSLContext getSSLContext() throws Exception{ 59 | KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 60 | ks.load(new FileInputStream("testssl.ks"),"test123".toCharArray()); 61 | SSLContext sslContext = SSLContext.getInstance("TLS"); 62 | //System.out.println(ks.getCertificate("10.10.0.2").toString()); 63 | 64 | KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 65 | kmf.init(ks, "test123".toCharArray()); 66 | TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 67 | tmf.init(ks); 68 | 69 | sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 70 | return sslContext; 71 | } 72 | 73 | 74 | public static void main(String args[]) { 75 | SocketServer server = new SocketServer(new InetSocketAddress("127.0.0.1", 8080)); 76 | server.setClientHandler(new TestServer()); 77 | try { 78 | server.setSSLContext(getSSLContext()); 79 | } catch (Exception e) { 80 | // TODO Auto-generated catch block 81 | e.printStackTrace(); 82 | } 83 | server.setExecuterService(Executors.newFixedThreadPool(3)); 84 | server.start(); 85 | 86 | } 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/org/nio/socket/NIOSocketOutputStream.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright [2012] [Gihan Munasinghe ayeshka@gmail.com ] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package org.nio.socket; 18 | 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import java.nio.ByteBuffer; 24 | import java.util.concurrent.locks.Condition; 25 | import java.util.concurrent.locks.Lock; 26 | import java.util.concurrent.locks.ReentrantLock; 27 | 28 | public class NIOSocketOutputStream extends OutputStream{ 29 | 30 | private ByteArrayOutputStream holder = null; 31 | private SocketClient client = null; 32 | private boolean stremClosed = false; 33 | private Boolean isWriteWait = new Boolean(false); 34 | 35 | private Lock writeLock = new ReentrantLock(); 36 | private Condition cond = writeLock.newCondition(); 37 | 38 | private static final int MAX_BUFF_SIZE = 1024; 39 | 40 | NIOSocketOutputStream(SocketClient cli) { 41 | holder = new ByteArrayOutputStream(); 42 | client = cli; 43 | } 44 | 45 | @Override 46 | public void write(int b) throws IOException { 47 | if(stremClosed) { 48 | throw new IOException("Write stream closed"); 49 | } 50 | synchronized (holder) { 51 | holder.write(b); 52 | checkAndFlush(); 53 | } 54 | 55 | } 56 | 57 | @Override 58 | public void write(byte[] arg0) throws IOException { 59 | if(stremClosed) { 60 | throw new IOException("Write stream closed"); 61 | } 62 | synchronized (holder) { 63 | holder.write(arg0); 64 | checkAndFlush(); 65 | } 66 | 67 | } 68 | 69 | @Override 70 | public void write(byte[] b, int off, int len) throws IOException { 71 | if(stremClosed) { 72 | throw new IOException("Write stream closed"); 73 | } 74 | synchronized (holder) { 75 | holder.write(b, off, len); 76 | checkAndFlush(); 77 | } 78 | 79 | } 80 | 81 | @Override 82 | public void flush() throws IOException { 83 | 84 | if(stremClosed) { 85 | throw new IOException("Write stream closed"); 86 | } 87 | try{ 88 | writeLock.lock(); 89 | synchronized (isWriteWait) { 90 | isWriteWait = true; 91 | } 92 | client.triggerWrite(); 93 | while(isWriteWait) { 94 | cond.await(); 95 | } 96 | 97 | // synchronized (this) { 98 | // try { 99 | // while(isWriteWait) { 100 | // wait(); 101 | // if(stremClosed) { 102 | // throw new IOException("Write stream closed"); 103 | // } 104 | // } 105 | // } catch (InterruptedException e) { 106 | // // TODO Auto-generated catch block 107 | // e.printStackTrace(); 108 | // } 109 | // } 110 | client.doWrite(); 111 | } catch (InterruptedException e) { 112 | // TODO Auto-generated catch block 113 | e.printStackTrace(); 114 | }finally { 115 | writeLock.unlock(); 116 | } 117 | 118 | } 119 | 120 | protected void notifyWrite() { 121 | try { 122 | writeLock.lock(); 123 | synchronized (isWriteWait) { 124 | isWriteWait = false; 125 | } 126 | cond.signal(); 127 | }finally{ 128 | writeLock.unlock(); 129 | } 130 | 131 | } 132 | 133 | 134 | protected ByteBuffer getByteBuffer() { 135 | ByteBuffer buff = null; 136 | synchronized (holder) { 137 | buff = ByteBuffer.wrap(holder.toByteArray()); 138 | holder.reset(); 139 | } 140 | return buff; 141 | } 142 | 143 | @Override 144 | public void close() throws IOException { 145 | if(!stremClosed) { 146 | stremClosed = true; 147 | } 148 | 149 | } 150 | 151 | private void checkAndFlush() throws IOException{ 152 | if (holder.size() > MAX_BUFF_SIZE) 153 | flush(); 154 | } 155 | 156 | protected boolean isWriteWait() { 157 | return isWriteWait; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/org/nio/socket/NIOSocketInputStream.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright [2012] [Gihan Munasinghe ayeshka@gmail.com ] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package org.nio.socket; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.nio.ByteBuffer; 22 | import java.nio.channels.SocketChannel; 23 | import java.util.Vector; 24 | import java.util.concurrent.locks.Lock; 25 | import java.util.concurrent.locks.ReentrantLock; 26 | 27 | public class NIOSocketInputStream extends InputStream { 28 | 29 | private ByteBuffer streamBuffer; 30 | private ByteBuffer channelBuffer; 31 | 32 | private SocketClient readClient; 33 | private boolean stremClosed = false; 34 | private Boolean isReadWait = false; 35 | private Lock readLock = new ReentrantLock(); 36 | 37 | public NIOSocketInputStream (SocketClient client) { 38 | readClient = client; 39 | streamBuffer = ByteBuffer.allocate(1024); 40 | streamBuffer.flip(); 41 | channelBuffer= ByteBuffer.allocate(1024); 42 | } 43 | 44 | @Override 45 | public int read() throws IOException { 46 | 47 | byte b[] = new byte [1]; 48 | read(b,0,b.length); 49 | return (0xFF & b[0]); 50 | 51 | } 52 | 53 | @Override 54 | public int read(byte[] b) throws IOException { 55 | return read(b, 0, b.length); 56 | } 57 | 58 | @Override 59 | public int read(byte[] b, int off, int len) throws IOException { 60 | 61 | try { 62 | readLock.lock(); 63 | int read_size = 0; 64 | int read_length = 0; 65 | read_length = len; 66 | while (true) { 67 | 68 | if(stremClosed) { 69 | throw new IOException("Read stream closed"); 70 | } 71 | 72 | if(streamBuffer.remaining() > 0) { 73 | int toread = Math.min(read_length, streamBuffer.remaining()); 74 | streamBuffer.get(b, off, toread); 75 | read_size +=toread; 76 | off=read_size; 77 | if(read_size == len) { 78 | break; 79 | } 80 | }else { 81 | int available = available(); 82 | if(available > 0) { 83 | continue; 84 | }else if (available == 0 ) { 85 | // Block the caller 86 | try { 87 | if(read_size == 0 ) { 88 | synchronized (isReadWait) { 89 | isReadWait = true; 90 | } 91 | synchronized (this) { 92 | while(isReadWait()) { 93 | wait(); 94 | } 95 | } 96 | } 97 | else { 98 | break; 99 | } 100 | } catch (InterruptedException e) { 101 | e.printStackTrace(); 102 | } 103 | }else { 104 | return -1; 105 | } 106 | } 107 | 108 | } 109 | return read_size; 110 | }finally { 111 | readLock.unlock(); 112 | } 113 | 114 | 115 | } 116 | 117 | @Override 118 | public synchronized int available() throws IOException { 119 | while (true) { 120 | 121 | if(stremClosed) { 122 | throw new IOException("Read stream closed"); 123 | } 124 | 125 | int available = streamBuffer.remaining(); 126 | if(available == 0) { 127 | streamBuffer.rewind(); 128 | streamBuffer.limit(streamBuffer.capacity()); 129 | // Read it from the stream add it to the stream buffer 130 | channelBuffer.rewind(); 131 | channelBuffer.limit(channelBuffer.capacity()); 132 | if(readClient.readToBuffer(channelBuffer) < 0) { 133 | close(); 134 | return -1; 135 | } 136 | channelBuffer.flip(); 137 | streamBuffer.put(channelBuffer); 138 | streamBuffer.flip(); 139 | available = streamBuffer.remaining(); 140 | } 141 | 142 | return available; 143 | } 144 | } 145 | 146 | @Override 147 | public void close() throws IOException { 148 | if(!stremClosed) { 149 | stremClosed = true; 150 | notifyRead(); 151 | } 152 | } 153 | 154 | protected boolean isReadWait(){ 155 | return isReadWait; 156 | } 157 | 158 | protected void notifyRead() { 159 | synchronized (isReadWait) { 160 | isReadWait = false; 161 | } 162 | synchronized (this) { 163 | this.notifyAll(); 164 | } 165 | 166 | } 167 | 168 | protected void lock() { 169 | readLock.lock(); 170 | } 171 | 172 | protected void unlock() { 173 | readLock.unlock(); 174 | } 175 | 176 | protected boolean tryLock(){ 177 | return readLock.tryLock(); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/org/nio/socket/SocketServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright [2012] [Gihan Munasinghe ayeshka@gmail.com ] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package org.nio.socket; 18 | 19 | import java.io.IOException; 20 | import java.net.InetSocketAddress; 21 | import java.net.ServerSocket; 22 | import java.nio.CharBuffer; 23 | import java.nio.channels.ClosedChannelException; 24 | import java.nio.channels.SelectionKey; 25 | import java.nio.channels.Selector; 26 | import java.nio.channels.ServerSocketChannel; 27 | import java.nio.channels.SocketChannel; 28 | import java.util.Date; 29 | import java.util.Iterator; 30 | import java.util.Set; 31 | import java.util.concurrent.ExecutorService; 32 | import java.util.concurrent.Executors; 33 | 34 | import javax.net.ssl.SSLContext; 35 | 36 | 37 | public class SocketServer extends Thread{ 38 | /** 39 | * Holds the listening socket address. 40 | */ 41 | private InetSocketAddress listener; 42 | /** 43 | * If the server needs to be SSL pass the context 44 | */ 45 | private SSLContext sslContext; 46 | 47 | /** 48 | * The executer to handled the incoming requests. 49 | */ 50 | private ExecutorService executer; 51 | 52 | private Selector selector; 53 | 54 | private ServerSocketChannel server; 55 | 56 | private ClientHandler handler = null; 57 | 58 | public SocketServer(InetSocketAddress sockAdd) { 59 | this.listener = sockAdd; 60 | } 61 | 62 | public void setSSLContext(SSLContext context){ 63 | sslContext = context; 64 | } 65 | 66 | public void setClientHandler(ClientHandler handler) { 67 | this.handler = handler; 68 | } 69 | 70 | @Override 71 | public synchronized void start() { 72 | if(executer == null) { 73 | executer = Executors.newSingleThreadExecutor(); 74 | } 75 | super.start(); 76 | } 77 | 78 | @Override 79 | public void run() { 80 | 81 | try { 82 | server = ServerSocketChannel.open(); 83 | server.configureBlocking( false ); 84 | ServerSocket socket = server.socket(); 85 | socket.bind( listener ); 86 | selector = Selector.open(); 87 | server.register( selector, server.validOps() ); 88 | } catch (ClosedChannelException e1) { 89 | // TODO Auto-generated catch block 90 | e1.printStackTrace(); 91 | return; 92 | } catch (IOException e1) { 93 | // TODO Auto-generated catch block 94 | e1.printStackTrace(); 95 | return; 96 | } 97 | 98 | while (true) { 99 | try { 100 | selector.select(); 101 | 102 | Set keys = selector.selectedKeys(); 103 | Iterator ite = keys.iterator(); 104 | while (ite.hasNext()) { 105 | 106 | SelectionKey key = ite.next(); 107 | System.out.println(key); 108 | ite.remove(); 109 | 110 | if(!key.isValid()) 111 | continue; 112 | 113 | if(key.isValid() && key.isAcceptable()) { 114 | System.out.println(new Date() + " Selector Accept "); 115 | SocketChannel channel = server.accept(); 116 | channel.configureBlocking( false ); 117 | SocketClient sc = newSocketClient(channel,selector); 118 | channel.finishConnect(); 119 | if(sc!=null) { 120 | if(sslContext!=null) { 121 | //sc.setSSLContext(sslContext); 122 | sc.buildSSLHandler(sslContext,false); 123 | } 124 | channel.register(selector, SelectionKey.OP_READ, sc); 125 | } 126 | continue; 127 | } 128 | 129 | if(key.isValid() && key.isReadable()) { 130 | 131 | System.out.println(new Date() + " Selector Read "+key.attachment()); 132 | key.interestOps(0); 133 | handleRead(key); 134 | continue; 135 | } 136 | 137 | if(key.isValid() && key.isWritable()) { 138 | System.out.println(new Date() + " Selector Write "); 139 | key.interestOps(0); 140 | handleWrite(key); 141 | continue; 142 | } 143 | } 144 | }catch (IOException e) { 145 | e.printStackTrace(); 146 | } 147 | } 148 | 149 | } 150 | 151 | public void setExecuterService(ExecutorService executer) { 152 | this.executer = executer; 153 | } 154 | 155 | /** 156 | * This method will be triggered by the server each time the there is a 157 | * new connection made by to the sever. 158 | * If you want to handled the connections and check the connections. 159 | * Override this method 160 | * @param sc 161 | * @return 162 | */ 163 | protected SocketClient newSocketClient(SocketChannel sc, Selector key) { 164 | return new SocketClient(sc, key); 165 | } 166 | 167 | private void handleRead(SelectionKey key) { 168 | SocketClient sc = (SocketClient)key.attachment(); 169 | if(sc.isReadBlocked()) { 170 | // If there is block read going on, this means some thread is in a waiting state 171 | // So we should just unblock the thread 172 | sc.unblockRead(); 173 | }else { 174 | NIOSocketInputStream ins = (NIOSocketInputStream)sc.getInputStream(); 175 | if(ins.tryLock()) { 176 | // Get the executer to run the request 177 | ReadHandler rh = new ReadHandler(key); 178 | executer.execute(rh); 179 | ins.unlock(); 180 | }else { 181 | // Some one is already reading the data so do not re initiate a new Handler to work on the same data set 182 | } 183 | } 184 | 185 | } 186 | 187 | /* 188 | * You can either get your server thread to do the writing, 189 | * if you are using the stream based approach. you can get the thread invoking the 190 | * flush to do the writing 191 | */ 192 | private void handleWrite(SelectionKey key) throws IOException { 193 | SocketClient sc = (SocketClient)key.attachment(); 194 | if(sc.isWriteBlocked()) { 195 | // A thread is interested in doing the write, some thread is using streams 196 | sc.unblockWrite(); 197 | }else { 198 | // Just let server thread do the write 199 | sc.doWrite(); 200 | } 201 | if(sc.isConnected()) 202 | key.interestOps(SelectionKey.OP_READ); 203 | 204 | } 205 | 206 | private class ReadHandler implements Runnable { 207 | private SocketClient socketClient; 208 | 209 | 210 | ReadHandler(final SelectionKey sc ) { 211 | socketClient = (SocketClient)sc.attachment(); 212 | } 213 | @Override 214 | public void run() { 215 | try { 216 | if(handler!=null) 217 | handler.handle(socketClient); 218 | } catch (Exception e) { 219 | // TODO Auto-generated catch block 220 | e.printStackTrace(); 221 | } 222 | 223 | } 224 | 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/org/nio/socket/SSLHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright [2012] [Gihan Munasinghe ayeshka@gmail.com ] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package org.nio.socket; 18 | 19 | import java.io.IOException; 20 | import java.nio.ByteBuffer; 21 | import java.nio.channels.SelectionKey; 22 | import java.nio.channels.SocketChannel; 23 | import java.util.concurrent.Executor; 24 | import java.util.concurrent.Executors; 25 | 26 | import javax.net.ssl.SSLEngine; 27 | import javax.net.ssl.SSLEngineResult.HandshakeStatus; 28 | import javax.net.ssl.SSLEngineResult.Status; 29 | import javax.net.ssl.SSLException; 30 | 31 | public class SSLHandler { 32 | 33 | private SSLEngine sslEngine ; 34 | private SocketChannel channel; 35 | //private SelectionKey selectionKey ; 36 | 37 | private ByteBuffer netSendBuffer = null; 38 | 39 | private ByteBuffer netRecvBuffer = null; 40 | 41 | private int unwrap_remain = 0; 42 | 43 | private boolean handShakeDone = false; 44 | 45 | public SSLHandler(SSLEngine engine , SocketChannel channle) throws SSLException { 46 | this.sslEngine = engine; 47 | this.channel = channle; 48 | //this.selectionKey = selector; 49 | this.netSendBuffer = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()); 50 | this.netRecvBuffer = ByteBuffer.allocate(engine.getSession().getPacketBufferSize()); 51 | engine.beginHandshake(); 52 | } 53 | 54 | //Need to write the handler stop method; 55 | public void stop() throws IOException{ 56 | 57 | // sslEngine.closeInbound(); 58 | sslEngine.closeOutbound(); 59 | } 60 | 61 | 62 | protected int doWrite(ByteBuffer buff) throws IOException{ 63 | doHandshake(); 64 | int out = buff.remaining(); 65 | while(buff.remaining() > 0) { 66 | if(wrapAndWrite(buff) < 0) 67 | return -1; 68 | } 69 | return out; 70 | } 71 | 72 | protected int doRead(ByteBuffer buff) throws IOException{ 73 | 74 | assert buff.position() == 0; 75 | if(readAndUnwarp(buff) >= 0) 76 | doHandshake(); 77 | else 78 | return -1; 79 | return buff.position(); 80 | 81 | } 82 | 83 | private int wrapAndWrite(ByteBuffer buff) throws IOException{ 84 | 85 | Status status; 86 | 87 | netSendBuffer.clear(); 88 | do { 89 | status = sslEngine.wrap (buff, netSendBuffer).getStatus(); 90 | if (status == Status.BUFFER_OVERFLOW) { 91 | // There data in the net buffer therefore need to send out the data 92 | flush(); 93 | } 94 | } while (status == Status.BUFFER_OVERFLOW); 95 | if (status == Status.CLOSED ) { 96 | throw new IOException("SSLEngine Closed"); 97 | 98 | } 99 | return flush(); 100 | 101 | } 102 | 103 | private int flush() throws IOException{ 104 | //System.out.println(netSendBuffer.position()); 105 | netSendBuffer.flip(); 106 | int count = 0; 107 | while (netSendBuffer.hasRemaining()) { 108 | int x = channel.write(netSendBuffer); 109 | if(x >= 0) { 110 | count +=x; 111 | } 112 | else { 113 | count = x ; 114 | break; 115 | } 116 | } 117 | netSendBuffer.compact(); 118 | //System.out.println(count); 119 | return count; 120 | } 121 | 122 | private int readAndUnwarp(ByteBuffer buff) throws IOException { 123 | Status status = Status.OK; 124 | if (!channel.isOpen()) { 125 | return -1; 126 | //throw new IOException ("Engine is closed"); 127 | } 128 | boolean needRead; 129 | if (unwrap_remain > 0) { 130 | netRecvBuffer.compact(); 131 | netRecvBuffer.flip(); 132 | needRead = false; 133 | } else { 134 | netRecvBuffer.clear(); 135 | needRead = true; 136 | } 137 | 138 | int x=0; 139 | do { 140 | if (needRead) { 141 | 142 | x = channel.read(netRecvBuffer); 143 | if (x == -1) { 144 | return x; 145 | //throw new IOException ("connection closed for reading"); 146 | } 147 | netRecvBuffer.flip(); 148 | } 149 | status = sslEngine.unwrap (netRecvBuffer, buff).getStatus(); 150 | if(x == 0 && netRecvBuffer.position() == 0) { 151 | netRecvBuffer.clear(); 152 | } 153 | if(x == 0 && handShakeDone) { 154 | return 0; 155 | } 156 | //status = r.result.getStatus(); 157 | if (status == Status.BUFFER_UNDERFLOW) { 158 | // Not enogh data read from the channel need to read more from the socket 159 | needRead = true; 160 | } else if (status == Status.BUFFER_OVERFLOW) { 161 | // Buffer over flow, the caller does not have enough buffer space in the buff 162 | // re do the call after freeing the current data in the buffer 163 | 164 | break; 165 | } else if (status == Status.CLOSED) { 166 | buff.flip(); 167 | return -1; 168 | //throw new IOException("SSLEngine Closed"); 169 | 170 | } 171 | } while (status != Status.OK); 172 | 173 | unwrap_remain = netRecvBuffer.remaining(); 174 | return buff.position(); 175 | } 176 | 177 | protected void doHandshake () throws IOException { 178 | 179 | handShakeDone = false; 180 | ByteBuffer tmpBuff = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize()); 181 | HandshakeStatus hs_status = sslEngine.getHandshakeStatus(); 182 | while (hs_status != HandshakeStatus.FINISHED && 183 | hs_status != HandshakeStatus.NOT_HANDSHAKING) 184 | { 185 | switch (hs_status) { 186 | case NEED_TASK: 187 | 188 | Executor exec = 189 | Executors.newSingleThreadExecutor(); 190 | Runnable task; 191 | 192 | while ((task=sslEngine.getDelegatedTask()) != null) 193 | { 194 | exec.execute(task); 195 | } 196 | break; 197 | /* fall thru - call wrap again */ 198 | case NEED_WRAP: 199 | tmpBuff.clear(); 200 | tmpBuff.flip(); 201 | if(wrapAndWrite(tmpBuff) < 0) 202 | throw new IOException("SSLHandshake failed"); 203 | break; 204 | 205 | case NEED_UNWRAP: 206 | tmpBuff.clear(); 207 | if(readAndUnwarp(tmpBuff) < 0) 208 | throw new IOException("SSLHandshake failed"); 209 | assert tmpBuff.position() == 0; 210 | break; 211 | } 212 | hs_status = sslEngine.getHandshakeStatus(); 213 | } 214 | handShakeDone = true; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/org/nio/socket/SocketClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright [2012] [Gihan Munasinghe ayeshka@gmail.com ] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package org.nio.socket; 18 | 19 | import java.io.IOException; 20 | 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import java.net.InetSocketAddress; 24 | import java.nio.ByteBuffer; 25 | import java.nio.channels.ClosedChannelException; 26 | import java.nio.channels.SelectionKey; 27 | import java.nio.channels.Selector; 28 | import java.nio.channels.SocketChannel; 29 | import java.util.Iterator; 30 | import java.util.Set; 31 | 32 | import javax.net.ssl.SSLContext; 33 | import javax.net.ssl.SSLEngine; 34 | import javax.net.ssl.SSLException; 35 | 36 | 37 | public class SocketClient { 38 | 39 | 40 | //private Selector selector = null; 41 | private SocketChannel client = null; 42 | private Selector selector = null; 43 | private InetSocketAddress address = null; 44 | 45 | private SSLContext sslContext; 46 | private SSLEngine sslEngine = null; 47 | private SSLHandler sslHandler = null; 48 | 49 | private boolean initConnDone = false; 50 | private boolean isClosed = false; 51 | 52 | private NIOSocketInputStream socketInputStream; 53 | private NIOSocketOutputStream socketOutputStream = null; 54 | 55 | public SocketClient(InetSocketAddress address) { 56 | this.address = address; 57 | socketInputStream = new NIOSocketInputStream(this); 58 | socketOutputStream = new NIOSocketOutputStream(this); 59 | } 60 | 61 | 62 | protected SocketClient(SocketChannel client,Selector key) { 63 | this.client = client; 64 | initConnDone = true; 65 | selector = key; 66 | socketInputStream = new NIOSocketInputStream(this); 67 | socketOutputStream = new NIOSocketOutputStream(this); 68 | } 69 | 70 | public void setSSLContext( SSLContext context) { 71 | this.sslContext = context; 72 | } 73 | 74 | protected void buildSSLHandler(SSLContext context, boolean clientmode) throws IOException { 75 | if(context!=null && client!=null ) { 76 | setSSLContext(context); 77 | sslEngine = sslContext.createSSLEngine(client.socket().getInetAddress().getHostName(), client.socket().getPort()); 78 | sslEngine.setUseClientMode(clientmode); 79 | sslHandler = new SSLHandler(sslEngine, client); 80 | //sslHandler.doHandshake(); 81 | //System.out.println(((!clientmode)? "Server ": "Client ") + "Handshake done"); 82 | } 83 | } 84 | 85 | public void connect() throws IOException { 86 | 87 | if(initConnDone) 88 | throw new IOException("Socket Already connected"); 89 | 90 | client = SocketChannel.open(); 91 | client.configureBlocking( false ); 92 | client.connect( address ); 93 | 94 | new Thread() { 95 | 96 | public void run() { 97 | try { 98 | selector = Selector.open(); 99 | client.register(selector, SelectionKey.OP_CONNECT); 100 | 101 | while (!isClosed) { 102 | int nsel = selector.select(); 103 | if (nsel == 0) 104 | continue; 105 | Set selectedKeys = selector.selectedKeys(); 106 | Iterator it = selectedKeys.iterator(); 107 | while (it.hasNext()) 108 | { 109 | SelectionKey key = it.next(); 110 | it.remove(); 111 | if (!key.isValid()) 112 | continue; 113 | if(key.isValid() && key.isConnectable()) { 114 | if(client.finishConnect()) { 115 | if(sslContext!=null) { 116 | // Do the SSL handshake stuff ; 117 | buildSSLHandler(sslContext,true); 118 | } 119 | client.register(selector,SelectionKey.OP_READ); 120 | initConnDone = true; 121 | 122 | } 123 | } 124 | if(key.isValid() && key.isReadable()) { 125 | unblockRead(); 126 | if(client.isOpen()) 127 | client.register(selector,SelectionKey.OP_READ); 128 | } 129 | if(key.isValid() && key.isWritable()) { 130 | unblockWrite(); 131 | if(client.isOpen()) 132 | client.register(selector,SelectionKey.OP_READ); 133 | } 134 | } 135 | } 136 | 137 | } catch (IOException e) { 138 | // TODO Auto-generated catch block 139 | e.printStackTrace(); 140 | }finally { 141 | try { 142 | selector.close(); 143 | client.close(); 144 | socketInputStream.close(); 145 | socketOutputStream.close(); 146 | } catch (IOException e) { 147 | // TODO Auto-generated catch block 148 | e.printStackTrace(); 149 | } 150 | 151 | } 152 | } 153 | 154 | }.start(); 155 | 156 | } 157 | 158 | protected SocketChannel getSocketChannel() { 159 | return client; 160 | } 161 | 162 | protected boolean isReadBlocked() { 163 | return socketInputStream.isReadWait(); 164 | } 165 | 166 | protected void unblockRead() { 167 | socketInputStream.notifyRead(); 168 | } 169 | 170 | 171 | protected boolean isWriteBlocked() { 172 | return socketOutputStream.isWriteWait(); 173 | } 174 | 175 | protected void unblockWrite() { 176 | System.out.println("SocketClient.unblockWrite()"); 177 | socketOutputStream.notifyWrite(); 178 | } 179 | 180 | protected int doWrite() throws IOException{ 181 | System.out.println("SocketClient.doWrite()"); 182 | if(sslHandler!=null) { 183 | return sslHandler.doWrite(socketOutputStream.getByteBuffer()); 184 | }else { 185 | //Write the non SSL bit of the transfer 186 | ByteBuffer buff = socketOutputStream.getByteBuffer(); 187 | int out = buff.remaining(); 188 | while(buff.hasRemaining()) { 189 | int x = client.write(buff); 190 | if(x < 0) 191 | return x; 192 | } 193 | return out; 194 | } 195 | } 196 | 197 | 198 | public boolean isConnected() { 199 | return (initConnDone && client!=null && client.isConnected()) ; 200 | } 201 | 202 | public OutputStream getOutputStream(){ 203 | return socketOutputStream; 204 | } 205 | 206 | public InputStream getInputStream() { 207 | return socketInputStream; 208 | } 209 | 210 | public void close() throws IOException{ 211 | if(!isClosed) { 212 | isClosed = true; 213 | if(sslHandler != null ) { 214 | sslHandler.stop(); 215 | } 216 | client.close(); 217 | socketInputStream.close(); 218 | socketOutputStream.close(); 219 | initConnDone = false; 220 | if(selector!=null) { 221 | selector.wakeup(); 222 | } 223 | } 224 | } 225 | 226 | 227 | protected void triggerWrite() throws IOException { 228 | if (client != null && client.isOpen()) { 229 | System.out.println("SocketClient.triggerWrite()"); 230 | try { 231 | client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this); 232 | selector.wakeup(); 233 | } catch (ClosedChannelException e) { 234 | throw new IOException ("Connection Closed "); 235 | } 236 | } 237 | } 238 | 239 | public InetSocketAddress getRemoteAddress() { 240 | if(client!=null) 241 | return (InetSocketAddress)(client.socket().getRemoteSocketAddress()); 242 | return null; 243 | } 244 | 245 | public InetSocketAddress getLocalAddress() { 246 | if(client!=null) 247 | return (InetSocketAddress)(client.socket().getLocalSocketAddress()); 248 | return null; 249 | } 250 | 251 | 252 | protected int readToBuffer(ByteBuffer buffer) throws IOException{ 253 | int out = 0; 254 | if(sslHandler!=null) { 255 | out = sslHandler.doRead(buffer); 256 | }else { 257 | out = client.read(buffer); 258 | } 259 | if(out < 0) { 260 | close(); 261 | }else { 262 | client.register(selector, SelectionKey.OP_READ,this); 263 | } 264 | return out; 265 | } 266 | 267 | } 268 | --------------------------------------------------------------------------------