├── .gitignore ├── README.md ├── pom.xml └── src ├── scalableIO ├── Logger.java ├── ServerContext.java ├── classic │ ├── Handler.java │ └── Server.java ├── reactor │ ├── Acceptor.java │ ├── Handler.java │ ├── Reactor.java │ ├── echo │ │ ├── EchoAcceptor.java │ │ ├── EchoHandler.java │ │ └── EchoReactor.java │ └── enter │ │ ├── EnterAcceptor.java │ │ ├── EnterHandler.java │ │ └── EnterReactor.java └── simple │ ├── Server.java │ ├── ServerDispatcher.java │ ├── ServerReactor.java │ ├── SocketAcceptHandler.java │ ├── SocketHandler.java │ ├── SocketReadHandler.java │ └── SocketWriteHandler.java └── socket ├── Constant.java ├── echo └── bio │ ├── ByteArrayrEchoServer.java │ ├── CharacterEchoServer.java │ └── OneByteEchoServer.java └── frame ├── DelimFramer.java ├── Framer.java ├── LengthFramer.java └── vote ├── VoteClientTCP.java ├── VoteClientUDP.java ├── VoteMsg.java ├── VoteMsgBinCoder.java ├── VoteMsgCoder.java ├── VoteMsgTextCoder.java ├── VoteServerTCP.java ├── VoteServerUDP.java └── VoteService.java /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /target/ 3 | .classpath 4 | .settings/ 5 | .project -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # learning-java-socket 2 | learning java socket 3 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.whg 5 | learning-java-socket 6 | 0.0.1-SNAPSHOT 7 | 8 | UTF-8 9 | 10 | 11 | src 12 | 13 | 14 | src 15 | 16 | **/*.java 17 | 18 | 19 | 20 | 21 | 22 | maven-compiler-plugin 23 | 3.3 24 | 25 | 1.6 26 | 1.6 27 | UTF-8 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/scalableIO/Logger.java: -------------------------------------------------------------------------------- 1 | package scalableIO; 2 | 3 | import static scalableIO.ServerContext.isLog; 4 | 5 | public class Logger { 6 | 7 | public static void log(String info){ 8 | if(isLog){ 9 | System.out.println(info); 10 | } 11 | } 12 | 13 | public static void err(String info){ 14 | if(isLog){ 15 | System.err.println(info); 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/scalableIO/ServerContext.java: -------------------------------------------------------------------------------- 1 | package scalableIO; 2 | 3 | import java.io.IOException; 4 | import java.lang.reflect.Constructor; 5 | import java.lang.reflect.InvocationTargetException; 6 | import java.nio.channels.ServerSocketChannel; 7 | import java.util.concurrent.Callable; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.Future; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.concurrent.atomic.AtomicLong; 13 | 14 | import scalableIO.reactor.Reactor; 15 | 16 | public class ServerContext{ 17 | 18 | public static final boolean isLog = true; 19 | 20 | private static final int subReactorSize = 3; 21 | public static final long selectTimeOut = TimeUnit.MILLISECONDS.toMillis(10); 22 | private static final AtomicLong nextIndex = new AtomicLong(); 23 | 24 | private static ServerSocketChannel serverChannel; 25 | private static Reactor mainReactor; 26 | private static Reactor[] subReactors; 27 | 28 | public static final boolean useThreadPool = true; 29 | private static final ExecutorService executor = Executors.newCachedThreadPool(); 30 | 31 | public static void startSingleReactor(int port, Class clazz){ 32 | start(port, clazz, false, subReactorSize); 33 | } 34 | 35 | public static void startMultipleReactor(int port, Class clazz){ 36 | start(port, clazz, true, subReactorSize); 37 | } 38 | 39 | public static void startMultipleReactor(int port, Class clazz, int subReactorSize){ 40 | start(port, clazz, true, subReactorSize); 41 | } 42 | 43 | private static void start(int port, Class clazz, boolean useMultipleReactors, int subReactorSize){ 44 | try{ 45 | serverChannel = ServerSocketChannel.open(); 46 | }catch(IOException e){ 47 | e.printStackTrace(); 48 | } 49 | 50 | try { 51 | Constructor constructor = clazz.getConstructor(int.class, ServerSocketChannel.class, boolean.class, boolean.class, long.class); 52 | mainReactor = constructor.newInstance(port, serverChannel, true, useMultipleReactors, selectTimeOut); 53 | 54 | if(useMultipleReactors){ 55 | subReactors = new Reactor[subReactorSize]; 56 | for(int i=0;i Future submit(Callable task){ 82 | return executor.submit(task); 83 | } 84 | 85 | public static void execute(Runnable runnable){ 86 | executor.execute(runnable); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/scalableIO/classic/Handler.java: -------------------------------------------------------------------------------- 1 | package scalableIO.classic; 2 | 3 | import static socket.Constant.BUF_SIZE; 4 | 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.net.Socket; 8 | 9 | public class Handler implements Runnable{ 10 | 11 | private final Socket clientSocket; 12 | 13 | public Handler(Socket clientSocket){ 14 | this.clientSocket = clientSocket; 15 | } 16 | 17 | @Override 18 | public void run() { 19 | int readSize; 20 | byte[] readBuf = new byte[BUF_SIZE]; 21 | try { 22 | InputStream in = clientSocket.getInputStream(); 23 | OutputStream out = clientSocket.getOutputStream(); 24 | while ((readSize = in.read(readBuf)) != -1) { 25 | out.write(readBuf, 0, readSize); 26 | } 27 | } catch (Exception e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/scalableIO/classic/Server.java: -------------------------------------------------------------------------------- 1 | package scalableIO.classic; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | 6 | public class Server implements Runnable{ 7 | 8 | private final int port; 9 | private ServerSocket serverSocket; 10 | 11 | public Server(int port){ 12 | this.port = port; 13 | try { 14 | this.serverSocket = new ServerSocket(port); 15 | } catch (IOException e) { 16 | e.printStackTrace(); 17 | } 18 | } 19 | 20 | @Override 21 | public void run(){ 22 | try { 23 | while (!Thread.interrupted()) { 24 | new Thread(new Handler(serverSocket.accept())).start(); 25 | } 26 | } catch (IOException e) { 27 | e.printStackTrace(); 28 | } 29 | } 30 | 31 | public int getPort() { 32 | return port; 33 | } 34 | 35 | public static void main(String[] args) { 36 | new Thread(new Server(9001)).start(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/scalableIO/reactor/Acceptor.java: -------------------------------------------------------------------------------- 1 | package scalableIO.reactor; 2 | 3 | import static scalableIO.Logger.log; 4 | import static scalableIO.ServerContext.nextSubReactor; 5 | 6 | import java.io.IOException; 7 | import java.nio.channels.Selector; 8 | import java.nio.channels.ServerSocketChannel; 9 | import java.nio.channels.SocketChannel; 10 | 11 | /** 12 | * 连接接收者 13 | *
    14 | *
  • 处理TCP/IP网络的连接,子SubReactor即在此处做分配
  • 15 | *
16 | */ 17 | public abstract class Acceptor extends Thread { 18 | 19 | protected final Selector selector; 20 | protected final ServerSocketChannel serverChannel; 21 | protected final boolean useMultipleReactors; 22 | 23 | public Acceptor(Selector selector, ServerSocketChannel serverChannel, boolean useMultipleReactors){ 24 | this.selector = selector; 25 | this.serverChannel = serverChannel; 26 | this.useMultipleReactors = useMultipleReactors; 27 | } 28 | 29 | @Override 30 | public void run() { 31 | log(selector+" accept..."); 32 | try { 33 | SocketChannel clientChannel = serverChannel.accept(); 34 | if(clientChannel != null){ 35 | log(selector+" clientChannel not null..."); 36 | //如果使用阻塞的select方式,且目的是开启了多个reactor池,而不是mainReactor和subReactor的关系的话, 37 | //则下面就不是nextSubSelector().selector,而是改为传递当前实例的selector对象即可 38 | handle(useMultipleReactors ? nextSubReactor().selector : selector, clientChannel); 39 | }else{ 40 | log(selector+" clientChannel is null..."); 41 | } 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | } 45 | } 46 | 47 | /** 48 | * 在每个具体的Handler下调用run方法是为了令其从connecting状态变为reading状态, 49 | * 和原pdf版本下的做法是一样的,只不过原pdf版本是在构造函数直接修改设置了感兴趣为read事件 50 | */ 51 | public abstract void handle(Selector selector, SocketChannel clientSocket); 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/scalableIO/reactor/Handler.java: -------------------------------------------------------------------------------- 1 | package scalableIO.reactor; 2 | 3 | import static scalableIO.Logger.err; 4 | import static scalableIO.Logger.log; 5 | import static scalableIO.ServerContext.execute; 6 | import static scalableIO.ServerContext.useThreadPool; 7 | 8 | import java.io.IOException; 9 | import java.net.SocketAddress; 10 | import java.nio.ByteBuffer; 11 | import java.nio.channels.SelectionKey; 12 | import java.nio.channels.Selector; 13 | import java.nio.channels.SocketChannel; 14 | import java.util.Arrays; 15 | 16 | /** 17 | * Handlers处理器 18 | *
    19 | *
  • 处理非阻塞读/写IO事件所对应的业务逻辑
  • 20 | *
  • 类似AWT中的ActionListeners处理器
  • 21 | *
22 | */ 23 | public abstract class Handler extends Thread { 24 | 25 | private enum State{ 26 | CONNECTING(0), 27 | READING(SelectionKey.OP_READ), 28 | PROCESSING(2), 29 | WRITING(SelectionKey.OP_WRITE); 30 | 31 | private final int opBit; 32 | private State(int operateBit){ 33 | opBit = operateBit; 34 | } 35 | } 36 | 37 | private State state; 38 | protected final SocketChannel clientChannel; 39 | protected final SelectionKey key; 40 | 41 | protected final ByteBuffer readBuf; 42 | protected final StringBuilder readData = new StringBuilder(); 43 | protected ByteBuffer writeBuf; 44 | 45 | public Handler(Selector selector, SocketChannel clientChannel){ 46 | this.state = State.CONNECTING; 47 | SelectionKey key = null; 48 | try { 49 | clientChannel.configureBlocking(false); 50 | //这里在使用subSelector的时候会阻塞,为什么?是因为使用了阻塞的select方法,非阻塞的才可以 51 | //但如果使用reactor池的话,那是因为需要serverChannel注册selector的accept事件!?必须对应上才可以通过,否则阻塞 52 | key = clientChannel.register(selector, this.state.opBit); 53 | key.attach(this); 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | } 57 | this.clientChannel = clientChannel; 58 | this.key = key; 59 | this.readBuf = ByteBuffer.allocate(byteBufferSize()); 60 | log(selector+" connect success..."); 61 | } 62 | 63 | @Override 64 | public void run() { 65 | switch (state) { 66 | case CONNECTING: 67 | connect(); 68 | break; 69 | case READING: 70 | readAndProcess(); 71 | break; 72 | case WRITING: 73 | write(); 74 | break; 75 | default: 76 | err("\nUnsupported State: "+state+" ! overlap processing with IO..."); 77 | } 78 | } 79 | 80 | private void connect() { 81 | interestOps(State.READING); 82 | } 83 | 84 | /** 85 | * But harder to overlap processing with IO
86 | * Best when can first read all input a buffer
87 | *
88 | * That why we used synchronized on read method!
89 | * Just to protected read buffer And handler state...
90 | *
91 | * 其实就是害怕重叠IO和工作线程处理不一致:例如Reactor单线程读某个key的IO完毕后立马开启工作线程的处理, 92 | * 紧接着Reactor单线程处理第二个IO key的时候发现还是之前的那个key的读IO事件,但是之前同一个key的处理还未完成, 93 | * 不等待之前的处理完成的话,就会出现多个线程同时访问修改Handler里面数据的情况,导致出错, 94 | * 但是最好先把数据都全部读入buffer中就可以规避了!? 95 | * 96 | *

此处的synchronized同步是为了防止state状态以及读写buffer在多线程访问中出现读脏数据, 97 | * Debug调试的时候同时访问一个SelectionKey有2个线程: 98 | *
1、Reactor单线程 99 | *
2、读数据完毕后多线程处理的话,线程池里面执行processAndHandOff的线程 100 | *
101 | * 不能单一使用volatile或者原子变量的原因是因为该方法为复合操作(check and act) 102 | */ 103 | private synchronized void readAndProcess(){ 104 | doRead(); 105 | doProcess(); 106 | } 107 | 108 | private void doRead(){ 109 | int readSize; 110 | try { 111 | while((readSize = clientChannel.read(readBuf)) > 0){ 112 | readData.append(new String(Arrays.copyOfRange(readBuf.array(), 0, readSize))); 113 | readBuf.clear(); 114 | } 115 | if(readSize == -1){ 116 | disconnect(); 117 | return; 118 | } 119 | } catch (IOException e) { 120 | e.printStackTrace(); 121 | disconnect(); 122 | } 123 | 124 | log("readed from client:"+readData+", "+readData.length()); 125 | } 126 | 127 | private void doProcess(){ 128 | if(readIsComplete()){ 129 | state = State.PROCESSING; 130 | processAndInterestWrite(); 131 | } 132 | } 133 | 134 | /** 135 | * 处理过程可能是比较耗时的,所以可考虑将其交由线程池处理,处理完毕后才注册感兴趣的write事件

136 | * 然而正是由于交由线程池处理所以可能造成重叠IO的多线程处理的状态问题,最好能一次性全部读入buffer,否则考虑同步状态处理问题 137 | */ 138 | private void processAndInterestWrite(){ 139 | Processor processor = new Processor(); 140 | if(useThreadPool){ 141 | execute(processor); 142 | }else{ 143 | processor.run(); 144 | } 145 | } 146 | 147 | private final class Processor implements Runnable{ 148 | @Override 149 | public void run() { 150 | processAndHandOff(); 151 | } 152 | } 153 | 154 | private synchronized void processAndHandOff(){ 155 | if(process()){ 156 | interestOps(State.WRITING); 157 | } 158 | } 159 | 160 | //TODO 修改为复用output,即当output容量不足的时候就反复write,而不是每次都使用wrap来new一个新的 161 | public boolean process(){ 162 | log("process readData="+readData.toString()); 163 | if(isQuit()){ 164 | disconnect(); 165 | return false; 166 | } 167 | 168 | writeBuf = ByteBuffer.wrap(readData.toString().getBytes()); 169 | readData.delete(0, readData.length()); 170 | return true; 171 | } 172 | 173 | private void write(){ 174 | try { 175 | do{ 176 | clientChannel.write(writeBuf); 177 | }while(!writeIsComplete()); 178 | } catch (IOException e) { 179 | e.printStackTrace(); 180 | disconnect(); 181 | } 182 | 183 | String writeData = new String(Arrays.copyOf(writeBuf.array(), writeBuf.array().length)); 184 | log("writed to client:"+writeData+", "+writeData.length()); 185 | interestOps(State.READING); 186 | } 187 | 188 | /** 189 | * 事件和事件处理器的绑定 190 | *

    191 | *
  • 类似AWT中的addActionListener添加监听器/观察者
  • 192 | *
193 | * 不需要重置key的附件(key.attach)是因为key一直绑定使用的是当前this实例, 194 | * 在Reactor dispatch的时候如果是接受(accept)该附件就是Acceptor实例, 195 | * 否则就是绑定到该key的同一个Handler实例 196 | */ 197 | private void interestOps(State state){ 198 | this.state = state; 199 | key.interestOps(state.opBit); 200 | } 201 | 202 | public boolean isQuit(){ 203 | return false; 204 | } 205 | 206 | private void disconnect(){ 207 | try { 208 | clientChannel.close(); 209 | } catch (IOException e) { 210 | e.printStackTrace(); 211 | } 212 | log("\nclient Address=【"+clientAddress(clientChannel)+"】 had already closed!!! "); 213 | } 214 | 215 | private static SocketAddress clientAddress(SocketChannel clientChannel){ 216 | return clientChannel.socket().getRemoteSocketAddress(); 217 | } 218 | 219 | public abstract int byteBufferSize(); 220 | 221 | public abstract boolean readIsComplete(); 222 | 223 | public abstract boolean writeIsComplete(); 224 | 225 | } 226 | -------------------------------------------------------------------------------- /src/scalableIO/reactor/Reactor.java: -------------------------------------------------------------------------------- 1 | package scalableIO.reactor; 2 | 3 | import static scalableIO.Logger.log; 4 | 5 | import java.io.IOException; 6 | import java.net.InetSocketAddress; 7 | import java.nio.channels.SelectionKey; 8 | import java.nio.channels.Selector; 9 | import java.nio.channels.ServerSocketChannel; 10 | import java.util.Iterator; 11 | 12 | /** 13 | * Reactor反应器 14 | *
    15 | *
  • 及时响应相对于的读/写IO事件
  • 16 | *
  • 并分发到合适的Handler处理器上进行业务处理
  • 17 | *
  • 类似AWT中的单例事件分发线程
  • 18 | *
19 | */ 20 | public abstract class Reactor extends Thread{ 21 | 22 | protected final int port; 23 | protected final ServerSocketChannel serverChannel; 24 | protected final boolean isMainReactor; 25 | protected final boolean useMultipleReactors; 26 | protected final long timeout; 27 | protected Selector selector; 28 | 29 | public Reactor(int port, ServerSocketChannel serverChannel, boolean isMainReactor, boolean useMultipleReactors, long timeout){ 30 | this.port = port; 31 | this.serverChannel = serverChannel; 32 | this.isMainReactor = isMainReactor; 33 | this.useMultipleReactors = useMultipleReactors; 34 | this.timeout = timeout; 35 | } 36 | 37 | @Override 38 | public void run(){ 39 | try { 40 | init(); 41 | while(!Thread.interrupted()){ 42 | //不可以使用阻塞的select方式,否则accept后subReactor的selector在register的时候会一直阻塞 43 | //但是修改为带有超时的select或者selectNow后,subReactor的selector在register就不会阻塞了 44 | //最终选择了带有超时的select是因为使用selectNow的无限循环会导致CPU飙高特别快 45 | //并且如果使用阻塞的select方式,还需要知道在哪里调用wakeup,否则会一直阻塞,使用非阻塞方式就不需要wakeup了 46 | //selector.select(); 47 | //if(selector.selectNow() > 0){ 48 | if(selector.select(timeout) > 0){ 49 | log(selector+" isMainReactor="+isMainReactor+" select..."); 50 | Iterator keyIt = selector.selectedKeys().iterator(); 51 | while(keyIt.hasNext()){ 52 | SelectionKey key = keyIt.next(); 53 | dispatch(key); 54 | keyIt.remove(); 55 | } 56 | } 57 | } 58 | log(getClass().getSimpleName()+" end on "+port+" ..."+"\n"); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | private void init() throws IOException{ 65 | selector = Selector.open(); 66 | log(selector+" isMainReactor="+isMainReactor); 67 | 68 | if(isMainReactor){ 69 | //serverChannel = ServerSocketChannel.open(); 70 | serverChannel.socket().bind(new InetSocketAddress(port)); 71 | serverChannel.configureBlocking(false); 72 | SelectionKey key = serverChannel.register(selector, SelectionKey.OP_ACCEPT); 73 | key.attach(newAcceptor(selector)); 74 | log(getClass().getSimpleName()+" start on "+port+" ..."+"\n"); 75 | }else{ 76 | 77 | } 78 | 79 | //如果使用阻塞的select方式,且开启下面的代码的话,相当于开启了多个reactor池,而不是mainReactor和subReactor的关系了 80 | //SelectionKey key = serverChannel.register(selector, SelectionKey.OP_ACCEPT); 81 | //key.attach(newAcceptor(selector, serverChannel)); 82 | } 83 | 84 | public abstract Acceptor newAcceptor(Selector selector); 85 | 86 | /** 87 | * 事件和事件处理器的绑定 88 | *
    89 | *
  • 管理IO读/写事件到事件处理器的一一对应的绑定
  • 90 | *
91 | */ 92 | private void dispatch(SelectionKey key){ 93 | Runnable r = (Runnable)key.attachment(); 94 | if(r != null){ 95 | r.run(); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/scalableIO/reactor/echo/EchoAcceptor.java: -------------------------------------------------------------------------------- 1 | package scalableIO.reactor.echo; 2 | 3 | import java.nio.channels.Selector; 4 | import java.nio.channels.ServerSocketChannel; 5 | import java.nio.channels.SocketChannel; 6 | 7 | import scalableIO.reactor.Acceptor; 8 | 9 | public class EchoAcceptor extends Acceptor { 10 | 11 | EchoAcceptor(Selector selector, ServerSocketChannel serverChannel, boolean useMultipleReactors){ 12 | super(selector, serverChannel, useMultipleReactors); 13 | } 14 | 15 | @Override 16 | public void handle(Selector selector, SocketChannel clientChannel) { 17 | new EchoHandler(selector, clientChannel).run(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/scalableIO/reactor/echo/EchoHandler.java: -------------------------------------------------------------------------------- 1 | package scalableIO.reactor.echo; 2 | 3 | import java.nio.channels.Selector; 4 | import java.nio.channels.SocketChannel; 5 | 6 | import scalableIO.reactor.Handler; 7 | 8 | public class EchoHandler extends Handler { 9 | 10 | EchoHandler(Selector selector, SocketChannel clientChannel){ 11 | super(selector, clientChannel); 12 | } 13 | 14 | @Override 15 | public int byteBufferSize() { 16 | return 1; 17 | } 18 | 19 | @Override 20 | public boolean readIsComplete() { 21 | return readData.length() > 0; 22 | } 23 | 24 | @Override 25 | public boolean writeIsComplete() { 26 | return !writeBuf.hasRemaining(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/scalableIO/reactor/echo/EchoReactor.java: -------------------------------------------------------------------------------- 1 | package scalableIO.reactor.echo; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.Selector; 5 | import java.nio.channels.ServerSocketChannel; 6 | 7 | import scalableIO.ServerContext; 8 | import scalableIO.reactor.Acceptor; 9 | import scalableIO.reactor.Reactor; 10 | 11 | public class EchoReactor extends Reactor { 12 | 13 | public EchoReactor(int port, ServerSocketChannel serverChannel, boolean isMainReactor, boolean useMultipleReactors, long timeout){ 14 | super(port, serverChannel, isMainReactor, useMultipleReactors, timeout); 15 | } 16 | 17 | @Override 18 | public Acceptor newAcceptor(Selector selector) { 19 | return new EchoAcceptor(selector, serverChannel, useMultipleReactors); 20 | } 21 | 22 | public static void main(String[] args) throws IOException { 23 | //new EchoReactor(9003, ServerSocketChannel.open(), true, false, TimeUnit.MILLISECONDS.toMillis(10)).start(); 24 | //ServerContext.startMultipleReactor(9004, EchoReactor.class); 25 | ServerContext.startSingleReactor(9004, EchoReactor.class); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/scalableIO/reactor/enter/EnterAcceptor.java: -------------------------------------------------------------------------------- 1 | package scalableIO.reactor.enter; 2 | 3 | import java.nio.channels.Selector; 4 | import java.nio.channels.ServerSocketChannel; 5 | import java.nio.channels.SocketChannel; 6 | 7 | import scalableIO.reactor.Acceptor; 8 | 9 | public class EnterAcceptor extends Acceptor { 10 | 11 | EnterAcceptor(Selector selector, ServerSocketChannel serverChannel, boolean useMultipleReactors){ 12 | super(selector, serverChannel, useMultipleReactors); 13 | } 14 | 15 | @Override 16 | public void handle(Selector selector, SocketChannel clientChannel) { 17 | new EnterHandler(selector, clientChannel).run(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/scalableIO/reactor/enter/EnterHandler.java: -------------------------------------------------------------------------------- 1 | package scalableIO.reactor.enter; 2 | 3 | import java.nio.channels.Selector; 4 | import java.nio.channels.SocketChannel; 5 | 6 | import scalableIO.reactor.Handler; 7 | 8 | public class EnterHandler extends Handler { 9 | 10 | private static final String ENTER = "\r\n"; 11 | private static final String QUIT = "quit"; 12 | 13 | EnterHandler(Selector selector, SocketChannel clientChannel){ 14 | super(selector, clientChannel); 15 | } 16 | 17 | @Override 18 | public int byteBufferSize() { 19 | return 1; 20 | } 21 | 22 | @Override 23 | public boolean readIsComplete() { 24 | return readData.lastIndexOf(ENTER) != -1; 25 | } 26 | 27 | @Override 28 | public boolean isQuit(){ 29 | return QUIT.equals(readData.toString().trim()); 30 | } 31 | 32 | @Override 33 | public boolean writeIsComplete() { 34 | return !writeBuf.hasRemaining(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/scalableIO/reactor/enter/EnterReactor.java: -------------------------------------------------------------------------------- 1 | package scalableIO.reactor.enter; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.Selector; 5 | import java.nio.channels.ServerSocketChannel; 6 | 7 | import scalableIO.ServerContext; 8 | import scalableIO.reactor.Acceptor; 9 | import scalableIO.reactor.Reactor; 10 | 11 | public class EnterReactor extends Reactor { 12 | 13 | public EnterReactor(int port, ServerSocketChannel serverChannel, boolean isMainReactor, boolean useMultipleReactors, long timeout){ 14 | super(port, serverChannel, isMainReactor, useMultipleReactors, timeout); 15 | } 16 | 17 | @Override 18 | public Acceptor newAcceptor(Selector selector) { 19 | return new EnterAcceptor(selector, serverChannel, useMultipleReactors); 20 | } 21 | 22 | public static void main(String[] args) throws IOException { 23 | //new EnterReactor(9003, ServerSocketChannel.open(), true, false, TimeUnit.MILLISECONDS.toMillis(10)).start(); 24 | ServerContext.startMultipleReactor(9003, EnterReactor.class); 25 | //ServerContext.startSingleReactor(9003, EnterReactor.class); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/scalableIO/simple/Server.java: -------------------------------------------------------------------------------- 1 | package scalableIO.simple; 2 | 3 | import java.io.IOException; 4 | 5 | public class Server { 6 | 7 | public static void main(String[] args) throws IOException { 8 | new Thread(new ServerReactor(9003)).start(); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/scalableIO/simple/ServerDispatcher.java: -------------------------------------------------------------------------------- 1 | package scalableIO.simple; 2 | 3 | import java.io.IOException; 4 | import java.nio.channels.Selector; 5 | import java.nio.channels.ServerSocketChannel; 6 | import java.nio.channels.spi.SelectorProvider; 7 | 8 | public class ServerDispatcher { 9 | 10 | private ServerSocketChannel ssc; 11 | private Selector[] selectors = new Selector[3]; 12 | 13 | public ServerDispatcher(ServerSocketChannel ssc, SelectorProvider selectorProvider) throws IOException { 14 | this.ssc = ssc; 15 | for (int i = 0; i < 3; i++) { 16 | selectors[i] = selectorProvider.openSelector(); 17 | } 18 | } 19 | 20 | public Selector getAcceptSelector() { 21 | return selectors[0]; 22 | } 23 | 24 | public Selector getReadSelector() { 25 | return selectors[1]; 26 | } 27 | 28 | public Selector getWriteSelector() { 29 | return selectors[1]; 30 | } 31 | 32 | public void execute() throws IOException { 33 | SocketHandler r = new SocketAcceptHandler(this, ssc, getAcceptSelector()); 34 | new Thread(r).start(); 35 | 36 | r = new SocketReadHandler(this, ssc, getReadSelector()); 37 | new Thread(r).start(); 38 | 39 | r = new SocketWriteHandler(this, ssc, getWriteSelector()); 40 | new Thread(r).start(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/scalableIO/simple/ServerReactor.java: -------------------------------------------------------------------------------- 1 | package scalableIO.simple; 2 | 3 | import static scalableIO.Logger.log; 4 | 5 | import java.io.IOException; 6 | import java.net.InetSocketAddress; 7 | import java.net.ServerSocket; 8 | import java.nio.channels.ServerSocketChannel; 9 | import java.nio.channels.spi.SelectorProvider; 10 | 11 | public class ServerReactor implements Runnable { 12 | private SelectorProvider selectorProvider = SelectorProvider.provider(); 13 | private ServerSocketChannel serverSocketChannel; 14 | public ServerReactor(int port) throws IOException { 15 | serverSocketChannel = selectorProvider.openServerSocketChannel(); //ServerSocketChannel.open(); 16 | ServerSocket serverSocket = serverSocketChannel.socket(); 17 | serverSocket.bind(new InetSocketAddress("localhost", port), 1024); 18 | serverSocketChannel.configureBlocking(false); 19 | log(String.format("Server : Server Start.----%d", port)); 20 | } 21 | 22 | public void run() { 23 | try { 24 | new ServerDispatcher(serverSocketChannel, SelectorProvider.provider()).execute(); 25 | } catch (IOException e) { 26 | // TODO Auto-generated catch block 27 | e.printStackTrace(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/scalableIO/simple/SocketAcceptHandler.java: -------------------------------------------------------------------------------- 1 | package scalableIO.simple; 2 | 3 | import static scalableIO.Logger.log; 4 | 5 | import java.io.IOException; 6 | import java.nio.channels.SelectionKey; 7 | import java.nio.channels.Selector; 8 | import java.nio.channels.ServerSocketChannel; 9 | 10 | public class SocketAcceptHandler extends SocketHandler{ 11 | 12 | public SocketAcceptHandler(ServerDispatcher dispatcher, ServerSocketChannel sc, Selector selector) 13 | throws IOException { 14 | super(dispatcher, sc, selector); 15 | serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT, this); 16 | } 17 | 18 | @Override 19 | public void runnerExecute(int readyKeyOps) throws IOException { 20 | // TODO Auto-generated method stub 21 | if (readyKeyOps == SelectionKey.OP_ACCEPT) 22 | { 23 | socketChannel = serverSocketChannel.accept(); 24 | socketChannel.configureBlocking(false); 25 | 26 | socketChannel.register(dispatcher.getReadSelector(), SelectionKey.OP_READ); 27 | log("Server accept"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/scalableIO/simple/SocketHandler.java: -------------------------------------------------------------------------------- 1 | package scalableIO.simple; 2 | 3 | import static scalableIO.Logger.log; 4 | 5 | import java.io.IOException; 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.Set; 12 | import java.util.concurrent.locks.ReadWriteLock; 13 | import java.util.concurrent.locks.ReentrantReadWriteLock; 14 | 15 | public abstract class SocketHandler implements Runnable{ 16 | protected Selector selector; 17 | protected SocketChannel socketChannel = null; 18 | protected ServerSocketChannel serverSocketChannel; 19 | protected ServerDispatcher dispatcher; 20 | private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();; 21 | public SocketHandler(ServerDispatcher dispatcher, ServerSocketChannel sc, Selector selector) throws IOException{ 22 | this.selector = selector; 23 | this.serverSocketChannel = sc; 24 | this.dispatcher = dispatcher; 25 | } 26 | 27 | public abstract void runnerExecute(int readyKeyOps) throws IOException; 28 | 29 | public final void run() 30 | { 31 | while(true) 32 | { 33 | readWriteLock.readLock().lock(); 34 | try { 35 | int keyOps = this.Select(); 36 | 37 | runnerExecute(keyOps); 38 | 39 | Thread.sleep(1000); 40 | } catch (Exception e) { 41 | // TODO: handle exception 42 | log(e.getMessage()); 43 | } 44 | finally { 45 | readWriteLock.readLock().unlock(); 46 | } 47 | } 48 | } 49 | 50 | private int Select() throws IOException 51 | { 52 | int keyOps = selector.select(); 53 | // int keyOps = this.selector.selectNow(); 54 | // 55 | // boolean flag = keyOps > 0; 56 | // 57 | // if (flag) 58 | // { 59 | SetreadyKeySet = selector.selectedKeys(); 60 | Iterator iterator = readyKeySet.iterator(); 61 | while (iterator.hasNext()) { 62 | SelectionKey key = iterator.next(); 63 | iterator.remove(); 64 | keyOps = key.readyOps(); 65 | if (keyOps == SelectionKey.OP_READ || keyOps == SelectionKey.OP_WRITE) 66 | { 67 | socketChannel = (SocketChannel)key.channel(); 68 | socketChannel.configureBlocking(false); 69 | } 70 | } 71 | // } 72 | 73 | return keyOps; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/scalableIO/simple/SocketReadHandler.java: -------------------------------------------------------------------------------- 1 | package scalableIO.simple; 2 | 3 | import static scalableIO.Logger.log; 4 | 5 | import java.io.IOException; 6 | import java.nio.ByteBuffer; 7 | import java.nio.channels.SelectionKey; 8 | import java.nio.channels.Selector; 9 | import java.nio.channels.ServerSocketChannel; 10 | 11 | public class SocketReadHandler extends SocketHandler{ 12 | private SelectionKey selectionKey; 13 | private int BLOCK = 4096; 14 | private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK); 15 | 16 | public SocketReadHandler(ServerDispatcher dispatcher, ServerSocketChannel sc, Selector selector) throws IOException{ 17 | super(dispatcher, sc, selector); 18 | } 19 | 20 | @Override 21 | public void runnerExecute(int readyKeyOps) throws IOException { 22 | // TODO Auto-generated method stub 23 | int count = 0; 24 | if (SelectionKey.OP_READ == readyKeyOps) 25 | { 26 | receivebuffer.clear(); 27 | count = socketChannel.read(receivebuffer); 28 | if (count > 0) { 29 | log("Server : Readable."); 30 | receivebuffer.flip(); 31 | byte[] bytes = new byte[receivebuffer.remaining()]; 32 | receivebuffer.get(bytes); 33 | String body = new String(bytes, "UTF-8"); 34 | log("Server : Receive :" + body); 35 | socketChannel.register(dispatcher.getWriteSelector(), SelectionKey.OP_WRITE); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/scalableIO/simple/SocketWriteHandler.java: -------------------------------------------------------------------------------- 1 | package scalableIO.simple; 2 | 3 | import static scalableIO.Logger.log; 4 | 5 | import java.io.IOException; 6 | import java.nio.ByteBuffer; 7 | import java.nio.channels.SelectionKey; 8 | import java.nio.channels.Selector; 9 | import java.nio.channels.ServerSocketChannel; 10 | 11 | public class SocketWriteHandler extends SocketHandler{ 12 | private int BLOCK = 4096; 13 | private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK); 14 | private static int Index = 1; 15 | public SocketWriteHandler(ServerDispatcher dispatcher, ServerSocketChannel sc, Selector selector) throws IOException{ 16 | super(dispatcher, sc, selector); 17 | } 18 | 19 | @Override 20 | public void runnerExecute(int readyKeyOps) throws IOException { 21 | // TODO Auto-generated method stub 22 | if (readyKeyOps == SelectionKey.OP_WRITE) 23 | { 24 | log("Server : Writable."); 25 | String data = String.format("%d", Index); 26 | byte[] req = data.getBytes(); 27 | sendbuffer.clear(); 28 | 29 | log(String.format("Server : Write %s", data)); 30 | 31 | sendbuffer = ByteBuffer.allocate(req.length); 32 | sendbuffer.put(req); 33 | sendbuffer.flip(); 34 | socketChannel.write(sendbuffer); 35 | Index++; 36 | socketChannel.register(dispatcher.getReadSelector(), SelectionKey.OP_READ); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/socket/Constant.java: -------------------------------------------------------------------------------- 1 | package socket; 2 | 3 | public class Constant { 4 | 5 | public static final String LOCALHOST = "localhost"; 6 | public static final int TCP_PORT = 8001; 7 | public static final int BUF_SIZE = 8; 8 | 9 | public static final int UDP_PORT = 8002; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/socket/echo/bio/ByteArrayrEchoServer.java: -------------------------------------------------------------------------------- 1 | package socket.echo.bio; 2 | 3 | import static socket.Constant.BUF_SIZE; 4 | import static socket.Constant.TCP_PORT; 5 | 6 | import java.io.InputStream; 7 | import java.io.OutputStream; 8 | import java.net.ServerSocket; 9 | import java.net.Socket; 10 | 11 | public class ByteArrayrEchoServer { 12 | 13 | public static void main(String[] args) throws Exception { 14 | int readSize; 15 | byte[] readBuf = new byte[BUF_SIZE]; 16 | ServerSocket ss = new ServerSocket(TCP_PORT); 17 | while (true) { 18 | Socket s = ss.accept(); 19 | InputStream in = s.getInputStream(); 20 | OutputStream out = s.getOutputStream(); 21 | while ((readSize = in.read(readBuf)) != -1) { 22 | out.write(readBuf, 0, readSize); 23 | } 24 | in.close(); 25 | out.close(); 26 | s.close(); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/socket/echo/bio/CharacterEchoServer.java: -------------------------------------------------------------------------------- 1 | package socket.echo.bio; 2 | 3 | import static socket.Constant.TCP_PORT; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.IOException; 7 | import java.io.InputStreamReader; 8 | import java.io.PrintWriter; 9 | import java.net.ServerSocket; 10 | import java.net.Socket; 11 | 12 | public class CharacterEchoServer { 13 | 14 | public static void main(String[] args) throws IOException { 15 | ServerSocket ss = new ServerSocket(TCP_PORT); 16 | while (true) { 17 | Socket s = ss.accept(); 18 | BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream())); 19 | PrintWriter writer = new PrintWriter(s.getOutputStream(), true); 20 | String line; 21 | while ((line = reader.readLine()) != null) { 22 | writer.println(line); 23 | writer.flush(); 24 | } 25 | reader.close(); 26 | writer.close(); 27 | s.close(); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/socket/echo/bio/OneByteEchoServer.java: -------------------------------------------------------------------------------- 1 | package socket.echo.bio; 2 | 3 | import static socket.Constant.TCP_PORT; 4 | 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | 10 | public class OneByteEchoServer { 11 | 12 | public static void main(String[] args) throws Exception { 13 | ServerSocket ss = new ServerSocket(TCP_PORT); 14 | while (true) { 15 | Socket s = ss.accept(); 16 | InputStream in = s.getInputStream(); 17 | OutputStream out = s.getOutputStream(); 18 | int c; 19 | while ((c = in.read()) != -1) { 20 | out.write(c); 21 | } 22 | in.close(); 23 | out.close(); 24 | s.close(); 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/socket/frame/DelimFramer.java: -------------------------------------------------------------------------------- 1 | package socket.frame; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | 8 | public class DelimFramer implements Framer { 9 | 10 | private static final byte DELIMITER = '\n'; 11 | 12 | private final InputStream in; 13 | private final OutputStream out; 14 | 15 | public DelimFramer(InputStream in, OutputStream out){ 16 | this.in = in; 17 | this.out = out; 18 | } 19 | 20 | @Override 21 | public void writeFrame(byte[] msg) { 22 | for(byte b:msg){ 23 | if(b == DELIMITER){ 24 | throw new IllegalArgumentException("message contains delimiter"); 25 | } 26 | } 27 | try { 28 | out.write(msg); 29 | out.write(DELIMITER); 30 | out.flush(); 31 | } catch (IOException e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | 36 | @Override 37 | public byte[] readFrame() { 38 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 39 | int readByte; 40 | 41 | try { 42 | while((readByte = in.read()) != DELIMITER){ 43 | if(readByte == -1){ 44 | if(buffer.size() == 0){ 45 | return null; 46 | }else{ 47 | throw new IllegalArgumentException("Non-empty message without delimiter"); 48 | } 49 | } 50 | buffer.write(readByte); 51 | } 52 | } catch (IOException e) { 53 | e.printStackTrace(); 54 | } 55 | 56 | return buffer.toByteArray(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/socket/frame/Framer.java: -------------------------------------------------------------------------------- 1 | package socket.frame; 2 | 3 | public interface Framer { 4 | 5 | void writeFrame(byte[] msg); 6 | byte[] readFrame(); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/socket/frame/LengthFramer.java: -------------------------------------------------------------------------------- 1 | package socket.frame; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedOutputStream; 5 | import java.io.DataInputStream; 6 | import java.io.DataOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | 11 | public class LengthFramer implements Framer { 12 | 13 | private final DataInputStream in; 14 | private final OutputStream out; 15 | 16 | public LengthFramer(InputStream in, OutputStream out){ 17 | this.in = new DataInputStream(new BufferedInputStream(in)); 18 | this.out = out; 19 | } 20 | 21 | @Override 22 | public void writeFrame(byte[] msg) { 23 | if(msg.length > Math.pow(2, Short.SIZE)){ 24 | throw new IllegalArgumentException("message too long"); 25 | } 26 | 27 | DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out)); 28 | try { 29 | dataOut.writeShort(msg.length); 30 | dataOut.write(msg); 31 | dataOut.flush(); 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | } 36 | 37 | @Override 38 | public byte[] readFrame() { 39 | int length; 40 | try { 41 | length = in.readUnsignedShort(); 42 | } catch (IOException e) { 43 | e.printStackTrace(); 44 | return null; 45 | } 46 | 47 | byte[] msg = new byte[length]; 48 | try { 49 | in.readFully(msg); 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | return null; 53 | } 54 | return msg; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/socket/frame/vote/VoteClientTCP.java: -------------------------------------------------------------------------------- 1 | package socket.frame.vote; 2 | 3 | import java.io.IOException; 4 | import java.net.Socket; 5 | import java.net.UnknownHostException; 6 | 7 | import socket.Constant; 8 | import socket.frame.DelimFramer; 9 | import socket.frame.Framer; 10 | 11 | public class VoteClientTCP { 12 | 13 | private static final int VOTE_ID = 888; 14 | 15 | public static void main(String[] args) throws UnknownHostException, IOException { 16 | VoteMsgCoder coder = new VoteMsgBinCoder(); 17 | Socket socket = new Socket(Constant.LOCALHOST, Constant.TCP_PORT); 18 | Framer framer = new DelimFramer(socket.getInputStream(), socket.getOutputStream()); 19 | 20 | VoteMsg msg = new VoteMsg(false, true, VOTE_ID, 0); 21 | framer.writeFrame(coder.encode(msg)); 22 | 23 | msg.setInquiry(false); 24 | framer.writeFrame(coder.encode(msg)); 25 | 26 | msg = coder.decode(framer.readFrame()); 27 | System.out.println("received query:"+msg); 28 | 29 | msg = coder.decode(framer.readFrame()); 30 | System.out.println("received vote:"+msg); 31 | 32 | socket.close(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/socket/frame/vote/VoteClientUDP.java: -------------------------------------------------------------------------------- 1 | package socket.frame.vote; 2 | 3 | import static socket.frame.vote.VoteMsgTextCoder.MAX_MSG_LENGTH; 4 | 5 | import java.io.IOException; 6 | import java.net.DatagramPacket; 7 | import java.net.DatagramSocket; 8 | import java.net.InetAddress; 9 | import java.util.Arrays; 10 | 11 | import socket.Constant; 12 | 13 | public class VoteClientUDP { 14 | 15 | private static final int VOTE_ID = 888; 16 | 17 | public static void main(String[] args) throws IOException { 18 | VoteMsgCoder coder = new VoteMsgBinCoder(); 19 | DatagramSocket socket = new DatagramSocket(); 20 | socket.connect(InetAddress.getByName(Constant.LOCALHOST), Constant.UDP_PORT); 21 | 22 | VoteMsg msg = new VoteMsg(false, true, VOTE_ID, 0); 23 | byte[] msgBytes = coder.encode(msg); 24 | DatagramPacket reqPacket = new DatagramPacket(msgBytes, msgBytes.length); 25 | socket.send(reqPacket); 26 | 27 | msg.setInquiry(false); 28 | msgBytes = coder.encode(msg); 29 | //reqPacket = new DatagramPacket(msgBytes, msgBytes.length); 30 | //注释掉上面的new的DatagramPacket,send的时候复用packet,但需要设置Data 31 | reqPacket.setData(msgBytes); 32 | socket.send(reqPacket); 33 | 34 | DatagramPacket respPacket = new DatagramPacket(new byte[MAX_MSG_LENGTH], MAX_MSG_LENGTH); 35 | socket.receive(respPacket); 36 | msgBytes = Arrays.copyOfRange(respPacket.getData(), 0, respPacket.getLength()); 37 | msg = coder.decode(msgBytes); 38 | System.out.println("received query:"+msg); 39 | 40 | //respPacket = new DatagramPacket(new byte[MAX_MSG_LENGTH], MAX_MSG_LENGTH); 41 | //注释掉上面的new的DatagramPacket,receive的时候复用packet,但需要设置缓冲区的长度避免前一个receive的截断 42 | respPacket.setLength(MAX_MSG_LENGTH); 43 | socket.receive(respPacket); 44 | msgBytes = Arrays.copyOfRange(respPacket.getData(), 0, respPacket.getLength()); 45 | msg = coder.decode(msgBytes); 46 | System.out.println("received vote:"+msg); 47 | 48 | socket.close(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/socket/frame/vote/VoteMsg.java: -------------------------------------------------------------------------------- 1 | package socket.frame.vote; 2 | 3 | public class VoteMsg { 4 | 5 | private boolean isInquiry; // true if inquiry; false if vote 6 | private boolean isResponse;// true if response from server 7 | private int candidateID; // in [0,1000] 8 | private long voteCount; // nonzero only in response 9 | 10 | public static final int MAX_CANDIDATE_ID = 1000; 11 | 12 | public VoteMsg(boolean isResponse, boolean isInquiry, int candidateID, 13 | long voteCount) throws IllegalArgumentException { 14 | // check invariants 15 | if (voteCount != 0 && !isResponse) { 16 | throw new IllegalArgumentException( 17 | "Request vote count must be zero"); 18 | } 19 | if (candidateID < 0 || candidateID > MAX_CANDIDATE_ID) { 20 | throw new IllegalArgumentException("Bad Candidate ID: " 21 | + candidateID); 22 | } 23 | if (voteCount < 0) { 24 | throw new IllegalArgumentException("Total must be >= zero"); 25 | } 26 | this.candidateID = candidateID; 27 | this.isResponse = isResponse; 28 | this.isInquiry = isInquiry; 29 | this.voteCount = voteCount; 30 | } 31 | 32 | public void setInquiry(boolean isInquiry) { 33 | this.isInquiry = isInquiry; 34 | } 35 | 36 | public void setResponse(boolean isResponse) { 37 | this.isResponse = isResponse; 38 | } 39 | 40 | public boolean isInquiry() { 41 | return isInquiry; 42 | } 43 | 44 | public boolean isResponse() { 45 | return isResponse; 46 | } 47 | 48 | public void setCandidateID(int candidateID) throws IllegalArgumentException { 49 | if (candidateID < 0 || candidateID > MAX_CANDIDATE_ID) { 50 | throw new IllegalArgumentException("Bad Candidate ID: " 51 | + candidateID); 52 | } 53 | this.candidateID = candidateID; 54 | } 55 | 56 | public int getCandidateID() { 57 | return candidateID; 58 | } 59 | 60 | public void setVoteCount(long count) { 61 | if ((count != 0 && !isResponse) || count < 0) { 62 | throw new IllegalArgumentException("Bad vote count"); 63 | } 64 | voteCount = count; 65 | } 66 | 67 | public long getVoteCount() { 68 | return voteCount; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return "VoteMsg [isResponse=" + isResponse + ", isInquiry=" + isInquiry 74 | + ", candidateID=" + candidateID + ", voteCount=" + voteCount 75 | + "]"; 76 | } 77 | 78 | // public String toString() { 79 | // String res = (isInquiry ? "inquiry" : "vote") + " for candidate " + candidateID; 80 | // if (isResponse) { 81 | // res = "response to " + res + " who now has " + voteCount + " vote(s)"; 82 | // } 83 | // return res; 84 | // } 85 | 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/socket/frame/vote/VoteMsgBinCoder.java: -------------------------------------------------------------------------------- 1 | package socket.frame.vote; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.DataInputStream; 6 | import java.io.DataOutputStream; 7 | import java.io.IOException; 8 | 9 | public class VoteMsgBinCoder implements VoteMsgCoder { 10 | 11 | private static final byte MAGIC_BYTE = 16+4+1; 12 | private static final short MAGIC_SHORT = MAGIC_BYTE << (2+8); 13 | private static final short RESP = 2 << 8; 14 | private static final short QUERY = 1 << 8; 15 | 16 | private static final short RESP_FLAG = 0x0200; 17 | private static final short QUERY_FLAG = 0x0100; 18 | 19 | @Override 20 | public byte[] encode(VoteMsg msg) { 21 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 22 | DataOutputStream out = new DataOutputStream(buffer); 23 | 24 | short magicAndFlag = MAGIC_SHORT; 25 | if(msg.isResponse()){ 26 | magicAndFlag |= RESP; 27 | } 28 | if(msg.isInquiry()){ 29 | magicAndFlag |= QUERY; 30 | } 31 | 32 | try { 33 | out.writeShort(magicAndFlag); 34 | out.writeShort((short)msg.getCandidateID()); 35 | if(msg.isResponse()){ 36 | out.writeLong(msg.getVoteCount()); 37 | } 38 | out.flush(); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | return null; 42 | } 43 | 44 | return buffer.toByteArray(); 45 | } 46 | 47 | @Override 48 | public VoteMsg decode(byte[] input) { 49 | DataInputStream in = new DataInputStream(new ByteArrayInputStream(input)); 50 | try { 51 | short magicAndFlag = in.readShort(); 52 | checkLegal(magicAndFlag); 53 | boolean isResp = ((magicAndFlag & RESP_FLAG) != 0); 54 | boolean isQuery = ((magicAndFlag & QUERY_FLAG) != 0); 55 | short voteId = in.readShort(); 56 | long voteCount = 0; 57 | if(isResp){ 58 | voteCount = in.readLong(); 59 | } 60 | return new VoteMsg(isResp, isQuery, voteId, voteCount); 61 | } catch (IOException e) { 62 | e.printStackTrace(); 63 | return null; 64 | } 65 | } 66 | 67 | private void checkLegal(short magicAndFlag){ 68 | byte magic = (byte)(magicAndFlag >> 10); 69 | if(magic != MAGIC_BYTE){ 70 | throw new IllegalArgumentException("bag magic byte:"+magic); 71 | } 72 | } 73 | 74 | public static void main(String[] args) { 75 | System.out.println(MAGIC_SHORT); 76 | System.out.println(Integer.toBinaryString(MAGIC_SHORT)); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/socket/frame/vote/VoteMsgCoder.java: -------------------------------------------------------------------------------- 1 | package socket.frame.vote; 2 | 3 | public interface VoteMsgCoder { 4 | 5 | byte[] encode(VoteMsg msg); 6 | VoteMsg decode(byte[] input); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/socket/frame/vote/VoteMsgTextCoder.java: -------------------------------------------------------------------------------- 1 | package socket.frame.vote; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.InputStreamReader; 5 | import java.io.UnsupportedEncodingException; 6 | import java.util.Scanner; 7 | 8 | /** 9 | * VOTE PROTO 10 | * magic | RESPFLAG | | VOTEID | VOTECOUNT 11 | * ensure charset on read and write is same 12 | */ 13 | public class VoteMsgTextCoder implements VoteMsgCoder { 14 | 15 | private static final String MAGIC = "Voting"; 16 | 17 | private static final String VOTE = "v"; 18 | private static final String QUERY = "q"; 19 | 20 | private static final String RESP = "R"; 21 | private static final String NO_RESP = "N"; 22 | 23 | private static final String CHARSET = "US-ASCII"; 24 | private static final String DELIMSTR = " "; 25 | 26 | public static final int MAX_MSG_LENGTH = 2000; 27 | 28 | @Override 29 | public byte[] encode(VoteMsg msg) { 30 | StringBuilder sb = new StringBuilder(); 31 | sb.append(MAGIC).append(DELIMSTR); 32 | sb.append(msg.isResponse() ? RESP : NO_RESP).append(DELIMSTR); 33 | sb.append(msg.isInquiry() ? QUERY : VOTE).append(DELIMSTR); 34 | sb.append(Integer.toString(msg.getCandidateID())).append(DELIMSTR); 35 | sb.append(Long.toString(msg.getVoteCount())); 36 | 37 | String msgStr = sb.toString(); 38 | try { 39 | return msgStr.getBytes(CHARSET); 40 | } catch (UnsupportedEncodingException e) { 41 | e.printStackTrace(); 42 | return null; 43 | } 44 | } 45 | 46 | @Override 47 | public VoteMsg decode(byte[] input) { 48 | Scanner scanner = null; 49 | try { 50 | scanner = new Scanner(new InputStreamReader(new ByteArrayInputStream(input), CHARSET)); 51 | } catch (UnsupportedEncodingException e) { 52 | e.printStackTrace(); 53 | return null; 54 | } 55 | checkLegal(scanner); 56 | 57 | boolean isResp = parseIsResp(scanner); 58 | boolean isQuery = parseIsQuery(scanner); 59 | int voteId = parseVoteId(scanner); 60 | long voteCount = parseVoteCount(scanner, isResp); 61 | 62 | return new VoteMsg(isResp, isQuery, voteId, voteCount); 63 | } 64 | 65 | private void checkLegal(Scanner scanner) { 66 | String token = scanner.next(); 67 | if(!MAGIC.equals(token)){ 68 | throw new IllegalArgumentException("bag magic string:"+token); 69 | } 70 | } 71 | 72 | private boolean parseIsResp(Scanner scanner) { 73 | if(RESP.equals(scanner.next())){ 74 | return true; 75 | }else{ 76 | return false; 77 | } 78 | } 79 | 80 | private boolean parseIsQuery(Scanner scanner) { 81 | String token = scanner.next(); 82 | if(VOTE.equals(token)){ 83 | return false; 84 | } 85 | if(!QUERY.equals(token)){ 86 | throw new IllegalArgumentException("bag vote/query indicator:"+token); 87 | } 88 | return true; 89 | } 90 | 91 | private int parseVoteId(Scanner scanner) { 92 | return scanner.nextInt(); 93 | } 94 | 95 | private long parseVoteCount(Scanner scanner, boolean isResp) { 96 | return isResp ? scanner.nextLong() : 0; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/socket/frame/vote/VoteServerTCP.java: -------------------------------------------------------------------------------- 1 | package socket.frame.vote; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | 7 | import socket.Constant; 8 | import socket.frame.DelimFramer; 9 | import socket.frame.Framer; 10 | 11 | public class VoteServerTCP { 12 | 13 | public static void main(String[] args) throws IOException { 14 | VoteService service = new VoteService(); 15 | VoteMsgCoder coder = new VoteMsgBinCoder(); 16 | ServerSocket serverSocket = new ServerSocket(Constant.TCP_PORT); 17 | System.out.println("VoteServerTCP listen on "+Constant.TCP_PORT); 18 | 19 | while(true){ 20 | Socket socket = serverSocket.accept(); 21 | Framer framer = new DelimFramer(socket.getInputStream(), socket.getOutputStream()); 22 | try { 23 | byte[] req; 24 | while((req=framer.readFrame()) != null){ 25 | VoteMsg msg = service.handleRequest(coder.decode(req)); 26 | framer.writeFrame(coder.encode(msg)); 27 | } 28 | } finally { 29 | socket.close(); 30 | } 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/socket/frame/vote/VoteServerUDP.java: -------------------------------------------------------------------------------- 1 | package socket.frame.vote; 2 | 3 | import static socket.frame.vote.VoteMsgTextCoder.MAX_MSG_LENGTH; 4 | 5 | import java.io.IOException; 6 | import java.net.DatagramPacket; 7 | import java.net.DatagramSocket; 8 | import java.util.Arrays; 9 | 10 | import socket.Constant; 11 | 12 | public class VoteServerUDP { 13 | 14 | public static void main(String[] args) throws IOException { 15 | VoteService service = new VoteService(); 16 | VoteMsgCoder coder = new VoteMsgBinCoder(); 17 | DatagramSocket socket = new DatagramSocket(Constant.UDP_PORT); 18 | System.out.println("VoteServerUDP listen on "+Constant.UDP_PORT); 19 | 20 | byte[] buffer = new byte[MAX_MSG_LENGTH]; 21 | while(true){ 22 | DatagramPacket packet = new DatagramPacket(buffer, buffer.length); 23 | socket.receive(packet); 24 | 25 | byte[] req = Arrays.copyOfRange(packet.getData(), 0, packet.getLength()); 26 | VoteMsg msg = service.handleRequest(coder.decode(req)); 27 | 28 | packet.setData(coder.encode(msg)); 29 | socket.send(packet); 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/socket/frame/vote/VoteService.java: -------------------------------------------------------------------------------- 1 | package socket.frame.vote; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class VoteService { 7 | 8 | // Map of candidates to number of votes 9 | private Map results = new HashMap(); 10 | 11 | public VoteMsg handleRequest(VoteMsg msg) { 12 | if (msg.isResponse()) { // If response, just send it back 13 | return msg; 14 | } 15 | msg.setResponse(true); // Make message a response 16 | // Get candidate ID and vote count 17 | int candidate = msg.getCandidateID(); 18 | Long count = results.get(candidate); 19 | if (count == null) { 20 | count = 0L; // Candidate does not exist 21 | } 22 | if (!msg.isInquiry()) { 23 | results.put(candidate, ++count); // If vote, increment count 24 | } 25 | msg.setVoteCount(count); 26 | return msg; 27 | } 28 | } 29 | --------------------------------------------------------------------------------