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 |
--------------------------------------------------------------------------------