├── README.md
├── pom.xml
├── reactor.png
└── src
├── main
├── assembly
│ ├── assembly.xml
│ └── bin
│ │ ├── startup.bat
│ │ └── startup.sh
└── java
│ └── com
│ └── seaboat
│ └── net
│ └── reactor
│ ├── Acceptor.java
│ ├── BufferPool.java
│ ├── Reactor.java
│ ├── ReactorPool.java
│ ├── connection
│ ├── Connection.java
│ ├── ConnectionEventHandler.java
│ ├── ConnectionEvents.java
│ ├── ConnectionFactory.java
│ └── DefaultConnectionFactory.java
│ └── handler
│ └── Handler.java
└── test
└── java
└── com
└── seaboat
└── net
└── reactor
└── test
├── Bootstrap.java
├── BufferPoolTest.java
├── MyHandler.java
└── SocketClient.java
/README.md:
--------------------------------------------------------------------------------
1 | # net-reactor
2 | it's a simple and easy net framework with nio mode written by java
3 |
4 | # reactor model
5 | 
6 |
7 | # how-to
8 | ## just simply like:
9 | ```
10 | public class MyHandler implements Handler {
11 |
12 | private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class);
13 | private long readSize;
14 |
15 | /**
16 | * The logic to deal with the received data.
17 | *
18 | * It means that reactor will trigger this function once the data is received.
19 | * @throws IOException
20 | */
21 | public void handle(FrontendConnection connection) throws IOException {
22 | Buffer buff = connection.getReadBuffer();
23 | readSize = +readSize + buff.position();
24 | LOGGER.info(connection.getId() + " connection has receive " + readSize);
25 |
26 | }
27 |
28 | }
29 | ```
30 | ```
31 | Handler handler = new MyHandler();
32 | ReactorPool reactorPool = new ReactorPool(Runtime.getRuntime().availableProcessors(), handler);
33 | new Acceptor(reactorPool, acceptorName, host, port).start();
34 | ```
35 |
36 | ## adding a connection event or a connection multi-event:
37 | ```
38 | public class RegisterHandler implements ConnectionEventHandler {
39 | private static final Logger LOGGER = LoggerFactory
40 | .getLogger(RegisterHandler.class);
41 |
42 | private static int INTERESTED = ConnectionEvents.REGISTE;
43 |
44 | public void event(FrontendConnection connection) {
45 | if ((event & INTERESTED) != 0) {
46 | //do something here
47 | }
48 | }
49 |
50 | }
51 |
52 | ```
53 | ```
54 | Handler handler = new NetHandler();
55 | ConnectionEventHandler connectionEventHandler = new RegisterHandler();
56 | ReactorPool reactorPool = new ReactorPool(Runtime.getRuntime().availableProcessors(), handler);
57 | Acceptor acceptor = new Acceptor(reactorPool, acceptorName, host, port);
58 | acceptor.addConnectionEventHandler(connectionEventHandler);
59 | acceptor.start();
60 | ```
61 | ```
62 | public class ConnectionLogHandler implements ConnectionEventHandler {
63 | private static final Logger LOGGER = LoggerFactory
64 | .getLogger(ConnectionLogHandler.class);
65 | private static int INTERESTED = ConnectionEvents.ACCEPT
66 | | ConnectionEvents.CLOSE;
67 |
68 | public void event(Connection connection, int event) {
69 | if ((event & INTERESTED) != 0) {
70 | if ((event & ConnectionEvents.ACCEPT) != 0)
71 | LOGGER.info("accept connection,id is " + connection.getId());
72 | if ((event & ConnectionEvents.CLOSE) != 0)
73 | LOGGER.info("close connection,id is " + connection.getId());
74 | }
75 | }
76 | }
77 |
78 | ```
79 |
80 |
81 | ## implements the connection
82 | ```
83 | public class XXXConnection extends Connection {
84 |
85 | private String name;
86 |
87 | public XXXConnection(SocketChannel channel, long id, Reactor reactor) {
88 | super(channel, id, reactor);
89 | }
90 |
91 | public String getName() {
92 | return name;
93 | }
94 |
95 | public void setName(String name) {
96 | this.name = name;
97 | }
98 |
99 | }
100 | ```
101 | ```
102 | public class XXXConnectionFactory implements ConnectionFactory {
103 |
104 | public XXXConnection createConnection(SocketChannel channel, long id,
105 | Reactor reactor) {
106 | return new XXXConnection(channel, id, reactor);
107 | }
108 |
109 | }
110 | ```
111 | ```
112 | Acceptor acceptor = new Acceptor(reactorPool, acceptorName, host,port);
113 | acceptor.setConnectionFactory(new xxxConnectionFactory());
114 | ```
115 |
116 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.seaboat
5 | net-reactor
6 | 0.0.1-SNAPSHOT
7 |
8 |
9 | UTF-8
10 | 1.1.7
11 |
12 |
13 |
14 |
15 | junit
16 | junit
17 | 4.3
18 |
19 |
20 | ch.qos.logback
21 | logback-classic
22 | ${logback.version}
23 |
24 |
25 |
--------------------------------------------------------------------------------
/reactor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sea-boat/net-reactor/41614ff30747c000085fdbf5abf4155094f5f6ff/reactor.png
--------------------------------------------------------------------------------
/src/main/assembly/assembly.xml:
--------------------------------------------------------------------------------
1 |
5 | release
6 | false
7 |
8 | tar.gz
9 |
10 |
11 |
12 | true
13 | net-reactor/lib
14 | false
15 |
16 |
17 |
18 |
19 | src/main/assembly/bin
20 | net-reactor/bin
21 |
22 |
23 | src/main/assembly/logs
24 | net-reactor/logs
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/main/assembly/bin/startup.bat:
--------------------------------------------------------------------------------
1 | java -cp ../lib/*; com.seaboat.net.reactor.Bootstrap
--------------------------------------------------------------------------------
/src/main/assembly/bin/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #check JAVA_HOME & java
4 | noJavaHome=false
5 | if [ -z "$JAVA_HOME" ] ; then
6 | noJavaHome=true
7 | fi
8 | if [ ! -e "$JAVA_HOME/bin/java" ] ; then
9 | noJavaHome=true
10 | fi
11 | if $noJavaHome ; then
12 | echo
13 | echo "Error: JAVA_HOME environment variable is not set."
14 | echo
15 | exit 1
16 | fi
17 | #==============================================================================
18 | #set JAVA_OPTS
19 | JAVA_OPTS="-server -Xms1G -Xmx2G -XX:MaxPermSize=128M -XX:+AggressiveOpts -XX:MaxDirectMemorySize=1G"
20 | #performance Options
21 | #JAVA_OPTS="$JAVA_OPTS -Xss256k"
22 | #JAVA_OPTS="$JAVA_OPTS -XX:+AggressiveOpts"
23 | #JAVA_OPTS="$JAVA_OPTS -XX:+UseBiasedLocking"
24 | #JAVA_OPTS="$JAVA_OPTS -XX:+UseFastAccessorMethods"
25 | #JAVA_OPTS="$JAVA_OPTS -XX:+DisableExplicitGC"
26 | #JAVA_OPTS="$JAVA_OPTS -XX:+UseParNewGC"
27 | #JAVA_OPTS="$JAVA_OPTS -XX:+UseConcMarkSweepGC"
28 | #JAVA_OPTS="$JAVA_OPTS -XX:+CMSParallelRemarkEnabled"
29 | #JAVA_OPTS="$JAVA_OPTS -XX:+UseCMSCompactAtFullCollection"
30 | #JAVA_OPTS="$JAVA_OPTS -XX:+UseCMSInitiatingOccupancyOnly"
31 | #JAVA_OPTS="$JAVA_OPTS -XX:CMSInitiatingOccupancyFraction=75"
32 | #JAVA_OPTS="$JAVA_OPTS -XX:CMSInitiatingOccupancyFraction=75"
33 | #GC Log Options
34 | #JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCApplicationStoppedTime"
35 | #JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCTimeStamps"
36 | #JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails"
37 | #debug Options
38 | #JAVA_OPTS="$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8065,server=y,suspend=n"
39 | # ZK
40 | #JAVA_OPTS="$JAVA_OPTS -Djava.security.auth.login.config=/etc/security/apps_client_jaas.conf"
41 | #==============================================================================
42 |
43 | #set HOME
44 | CURR_DIR=`pwd`
45 | cd `dirname "$0"`/..
46 | HOME=`pwd`
47 | cd $CURR_DIR
48 |
49 |
50 | NET_CLASSPATH="$HOME/conf:$HOME/lib/*"
51 |
52 |
53 |
54 | #==============================================================================
55 |
56 | #startup Server
57 | RUN_CMD="$JAVA_HOME/bin/java"
58 | RUN_CMD="$RUN_CMD -classpath $NET_CLASSPATH"
59 | RUN_CMD="$RUN_CMD $JAVA_OPTS"
60 | RUN_CMD="$RUN_CMD com.seaboat.net.reactor.Bootstrap $@"
61 | RUN_CMD="$RUN_CMD > $HOME/logs/console.log 2>&1 &"
62 | echo $RUN_CMD
63 | eval $RUN_CMD
64 | echo $! > $HOME/bin/1.pid
65 | #==============================================================================
66 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/Acceptor.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor;
2 |
3 | import java.io.IOException;
4 | import java.net.InetSocketAddress;
5 | import java.net.Socket;
6 | import java.net.StandardSocketOptions;
7 | import java.nio.channels.SelectionKey;
8 | import java.nio.channels.Selector;
9 | import java.nio.channels.ServerSocketChannel;
10 | import java.nio.channels.SocketChannel;
11 | import java.util.LinkedList;
12 | import java.util.List;
13 | import java.util.Set;
14 |
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import com.seaboat.net.reactor.connection.Connection;
19 | import com.seaboat.net.reactor.connection.ConnectionEventHandler;
20 | import com.seaboat.net.reactor.connection.ConnectionEvents;
21 | import com.seaboat.net.reactor.connection.ConnectionFactory;
22 | import com.seaboat.net.reactor.connection.DefaultConnectionFactory;
23 |
24 | /**
25 | *
26 | * @author seaboat
27 | * @date 2016-08-25
28 | * @version 1.0
29 | *
email: 849586227@qq.com
30 | * blog: http://blog.csdn.net/wangyangzhizhou
31 | * This Acceptor provides a NIO mode to accept client sockets.
32 | */
33 | public final class Acceptor extends Thread {
34 |
35 | private static final Logger LOGGER = LoggerFactory
36 | .getLogger(Acceptor.class);
37 | private final int port;
38 | private final Selector selector;
39 | private final ServerSocketChannel serverChannel;
40 | private long acceptCount;
41 | private static final AcceptIdGenerator IdGenerator = new AcceptIdGenerator();
42 | private ReactorPool reactorPool;
43 | private List eventHandlers = new LinkedList();
44 | private ConnectionFactory connectionFactory = new DefaultConnectionFactory();
45 |
46 | public Acceptor(ReactorPool reactorPool, String name, String bindIp,
47 | int port) throws IOException {
48 | super.setName(name);
49 | this.port = port;
50 | this.selector = Selector.open();
51 | this.serverChannel = ServerSocketChannel.open();
52 | this.serverChannel.configureBlocking(false);
53 | this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
54 | this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 1024);
55 | this.serverChannel.bind(new InetSocketAddress(bindIp, port), 100);
56 | this.serverChannel.register(selector, SelectionKey.OP_ACCEPT);
57 | this.reactorPool = reactorPool;
58 | }
59 |
60 | public int getPort() {
61 | return port;
62 | }
63 |
64 | public long getAcceptCount() {
65 | return acceptCount;
66 | }
67 |
68 | @Override
69 | public void run() {
70 | final Selector selector = this.selector;
71 | for (;;) {
72 | ++acceptCount;
73 | try {
74 | selector.select(1000L);
75 | Set keys = selector.selectedKeys();
76 | try {
77 | for (SelectionKey key : keys) {
78 | if (key.isValid() && key.isAcceptable()) {
79 | accept();
80 | } else {
81 | key.cancel();
82 | }
83 | }
84 | } finally {
85 | keys.clear();
86 | }
87 | } catch (Throwable e) {
88 | LOGGER.warn(getName(), e);
89 | }
90 | }
91 | }
92 |
93 | /**
94 | * Accept client sockets.
95 | */
96 | private void accept() {
97 | SocketChannel channel = null;
98 | try {
99 | channel = serverChannel.accept();
100 | channel.configureBlocking(false);
101 | channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
102 | channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
103 | channel.setOption(StandardSocketOptions.SO_RCVBUF, 1024);
104 | channel.setOption(StandardSocketOptions.SO_SNDBUF, 1024);
105 | channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
106 | Reactor reactor = reactorPool.getNextReactor();
107 | Connection connection = connectionFactory.createConnection(channel,
108 | IdGenerator.getId(), reactor);
109 | for (ConnectionEventHandler handler : eventHandlers)
110 | connection.addEventHandler(handler);
111 | connection.processEvent(ConnectionEvents.ACCEPT);
112 | reactor.postRegister(connection);
113 | } catch (Throwable e) {
114 | closeChannel(channel);
115 | LOGGER.warn(getName(), e);
116 | }
117 | }
118 |
119 | /**
120 | * add connection event handler
121 | */
122 | public void addConnectionEventHandler(ConnectionEventHandler handler) {
123 | eventHandlers.add(handler);
124 | }
125 |
126 | /**
127 | * Close a channel.
128 | *
129 | * @param channel
130 | */
131 | private static void closeChannel(SocketChannel channel) {
132 | if (channel == null) {
133 | return;
134 | }
135 | Socket socket = channel.socket();
136 | if (socket != null) {
137 | try {
138 | socket.close();
139 | LOGGER.info("channel close.");
140 | } catch (IOException e) {
141 | LOGGER.warn("IOException happens when closing socket : ", e);
142 | }
143 | }
144 | try {
145 | channel.close();
146 | } catch (IOException e) {
147 | LOGGER.warn("IOException happens when closing channel : ", e);
148 | }
149 | }
150 |
151 | /**
152 | * ID Generator.
153 | */
154 | private static class AcceptIdGenerator {
155 | private static final long MAX_VALUE = 0xffffffffL;
156 | private long acceptId = 0L;
157 | private final Object lock = new Object();
158 |
159 | private long getId() {
160 | synchronized (lock) {
161 | if (acceptId >= MAX_VALUE) {
162 | acceptId = 0L;
163 | }
164 | return ++acceptId;
165 | }
166 | }
167 | }
168 |
169 | public void setConnectionFactory(ConnectionFactory connectionFactory) {
170 | this.connectionFactory = connectionFactory;
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/BufferPool.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.util.concurrent.ConcurrentLinkedQueue;
5 | import java.util.concurrent.atomic.AtomicInteger;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | /**
11 | *
12 | * @author seaboat
13 | * @date 2017-01-02
14 | * @version 1.0
15 | * email: 849586227@qq.com
16 | * blog: http://blog.csdn.net/wangyangzhizhou
17 | * A buffer pool. This pool provide a buffer queue for the operation of offer and poll.
18 | */
19 | public final class BufferPool {
20 | private static final Logger LOGGER = LoggerFactory
21 | .getLogger(BufferPool.class);
22 | private final int chunkSize;
23 | private final ConcurrentLinkedQueue items = new ConcurrentLinkedQueue();
24 | private AtomicInteger newCreated = new AtomicInteger(0);
25 | private final int capactiy;
26 | private long totalBytes = 0;
27 | private long totalCounts = 0;
28 |
29 | public BufferPool(int bufferSize, int chunkSize) {
30 | this.chunkSize = chunkSize;
31 | int size = bufferSize / chunkSize;
32 | size = (bufferSize % chunkSize == 0) ? size : size + 1;
33 | this.capactiy = size;
34 | for (int i = 0; i < capactiy; i++) {
35 | items.offer(ByteBuffer.allocateDirect(chunkSize));
36 | }
37 | }
38 |
39 | public int size() {
40 | return this.items.size();
41 | }
42 |
43 | public int capacity() {
44 | return capactiy + newCreated.get();
45 | }
46 |
47 | public ByteBuffer allocate() {
48 | ByteBuffer node = null;
49 | node = items.poll();
50 | if (node == null) {
51 | newCreated.incrementAndGet();
52 | node = ByteBuffer.allocateDirect(chunkSize);
53 | }
54 | return node;
55 | }
56 |
57 | private boolean checkValidBuffer(ByteBuffer buffer) {
58 | if (buffer == null || !buffer.isDirect()) {
59 | return false;
60 | } else if (buffer.capacity() != chunkSize) {
61 | // we don't recycle the buffer that isn't allocated by BufferPool.
62 | return false;
63 | }
64 | totalCounts++;
65 | totalBytes += buffer.limit();
66 | buffer.clear();
67 | return true;
68 | }
69 |
70 | public void recycle(ByteBuffer buffer) {
71 | if (!checkValidBuffer(buffer)) {
72 | return;
73 | }
74 | items.offer(buffer);
75 | }
76 |
77 | public int getAvgBufSize() {
78 | if (this.totalBytes < 0) {
79 | totalBytes = 0;
80 | this.totalCounts = 0;
81 | return 0;
82 | } else {
83 | return (int) (totalBytes / totalCounts);
84 | }
85 | }
86 |
87 | public ByteBuffer allocate(int size) {
88 | if (size <= this.chunkSize) {
89 | return allocate();
90 | } else {
91 | LOGGER.warn("allocate buffer size large than chunksize:"
92 | + this.chunkSize + ",the size is " + size
93 | + ". we create a temp buffer that could not be recycled."
94 | + size);
95 | return ByteBuffer.allocate(size);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/Reactor.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor;
2 |
3 | import java.io.IOException;
4 | import java.nio.channels.SelectionKey;
5 | import java.nio.channels.Selector;
6 | import java.util.Queue;
7 | import java.util.Set;
8 | import java.util.concurrent.ConcurrentLinkedQueue;
9 |
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import com.seaboat.net.reactor.connection.Connection;
14 | import com.seaboat.net.reactor.handler.Handler;
15 |
16 | /**
17 | *
18 | * @author seaboat
19 | * @date 2016-08-25
20 | * @version 1.0
21 | * email: 849586227@qq.com
22 | * blog: http://blog.csdn.net/wangyangzhizhou
23 | * Reactor reacts all sockets.
24 | */
25 | public final class Reactor extends Thread {
26 | private static final Logger LOGGER = LoggerFactory.getLogger(Reactor.class);
27 | private final Selector selector;
28 | private final ConcurrentLinkedQueue queue;
29 | private long doCount;
30 | private Handler handler;
31 | private ReactorPool reactorPool;
32 |
33 | public Reactor(String name, Handler handler, ReactorPool reactorPool)
34 | throws IOException {
35 | this.selector = Selector.open();
36 | this.queue = new ConcurrentLinkedQueue();
37 | this.handler = handler;
38 | this.reactorPool = reactorPool;
39 | }
40 |
41 | final void postRegister(Connection frontendConnection) {
42 | queue.offer(frontendConnection);
43 | this.selector.wakeup();
44 | }
45 |
46 | @Override
47 | public void run() {
48 | final Selector selector = this.selector;
49 | Set keys = null;
50 | for (;;) {
51 | ++doCount;
52 | try {
53 | selector.select(500L);
54 | register(selector);
55 | keys = selector.selectedKeys();
56 | for (SelectionKey key : keys) {
57 | Connection connection = null;
58 | Object attach = key.attachment();
59 | if (attach != null && key.isValid()) {
60 | connection = (Connection) attach;
61 | if (key.isReadable()) {
62 | try {
63 | // must be ready to read
64 | connection.read();
65 | // judge connection hasn't been closed.
66 | if (connection.isClose())
67 | continue;
68 | handler.handle(connection);
69 | } catch (IOException e) {
70 | connection.close();
71 | LOGGER.warn("IOException happens : ", e);
72 | continue;
73 | } catch (Throwable e) {
74 | LOGGER.warn("Throwable happens : ", e);
75 | continue;
76 | }
77 | }
78 | if (key.isValid() && key.isWritable()) {
79 | connection.write();
80 | }
81 | } else {
82 | key.cancel();
83 | }
84 | }
85 | } catch (Throwable e) {
86 | LOGGER.warn("exception happens selecting : ", e);
87 | } finally {
88 | if (keys != null) {
89 | keys.clear();
90 | }
91 | }
92 | }
93 | }
94 |
95 | private void register(Selector selector) {
96 | Connection c = null;
97 | if (queue.isEmpty()) {
98 | return;
99 | }
100 | while ((c = queue.poll()) != null) {
101 | try {
102 | c.register(selector);
103 | } catch (Throwable e) {
104 | LOGGER.warn("ClosedChannelException happens : ", e);
105 | }
106 | }
107 | }
108 |
109 | final Queue getRegisterQueue() {
110 | return queue;
111 | }
112 |
113 | final long getReactCount() {
114 | return doCount;
115 | }
116 |
117 | public ReactorPool getReactorPool() {
118 | return reactorPool;
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/ReactorPool.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor;
2 |
3 | import java.io.IOException;
4 |
5 | import com.seaboat.net.reactor.handler.Handler;
6 |
7 | /**
8 | *
9 | * @author seaboat
10 | * @date 2016-08-25
11 | * @version 1.0
12 | * email: 849586227@qq.com
13 | * blog: http://blog.csdn.net/wangyangzhizhou
14 | * Reactor pool. Socket connections are polling to the reactor of this pool.
15 | */
16 | public class ReactorPool {
17 | private final Reactor[] reactors;
18 | private volatile int nextReactor;
19 | private String name = "reactor";
20 | private BufferPool bufferPool;
21 |
22 | public ReactorPool(int poolSize, Handler handler) throws IOException {
23 | reactors = new Reactor[poolSize];
24 | for (int i = 0; i < poolSize; i++) {
25 | Reactor reactor = new Reactor(name + "-" + i, handler, this);
26 | reactors[i] = reactor;
27 | reactor.start();
28 | }
29 | this.bufferPool = new BufferPool(1024 * 1024 * 52, 10 * 1024);
30 | }
31 |
32 | public Reactor getNextReactor() {
33 | if (++nextReactor == reactors.length) {
34 | nextReactor = 0;
35 | }
36 | return reactors[nextReactor];
37 | }
38 |
39 | public BufferPool getBufferPool() {
40 | return bufferPool;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/connection/Connection.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.connection;
2 |
3 | import java.io.EOFException;
4 | import java.io.IOException;
5 | import java.nio.ByteBuffer;
6 | import java.nio.channels.SelectionKey;
7 | import java.nio.channels.Selector;
8 | import java.nio.channels.SocketChannel;
9 | import java.util.LinkedList;
10 | import java.util.List;
11 | import java.util.concurrent.ConcurrentLinkedQueue;
12 |
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | import com.seaboat.net.reactor.Reactor;
17 |
18 | /**
19 | *
20 | * @author seaboat
21 | * @date 2016-08-25
22 | * @version 1.0
23 | * email: 849586227@qq.com
24 | * blog: http://blog.csdn.net/wangyangzhizhou
25 | * This is a abstraction of client connection.
26 | */
27 | public class Connection {
28 | private static final Logger LOGGER = LoggerFactory
29 | .getLogger(Connection.class);
30 | private long id;
31 | private SocketChannel channel;
32 | private SelectionKey selectionKey;
33 | private ByteBuffer readBuffer;
34 | private ConcurrentLinkedQueue writeQueue = new ConcurrentLinkedQueue();
35 | private Reactor reactor;
36 | private long lastReadTime;
37 | private long lastWriteTime;
38 | private long createTime;
39 | private long netInBytes;
40 | private long netOutBytes;
41 | protected volatile String charset = "UTF-8";
42 | private volatile boolean isClose = false;
43 | private List eventHandlers = new LinkedList();
44 |
45 | public Connection(SocketChannel channel, long id, Reactor reactor) {
46 | this.id = id;
47 | this.channel = channel;
48 | this.reactor = reactor;
49 | this.createTime = System.currentTimeMillis();
50 | this.lastReadTime = createTime;
51 | this.lastWriteTime = createTime;
52 | this.readBuffer = reactor.getReactorPool().getBufferPool().allocate();
53 | }
54 |
55 | public SocketChannel getChannel() {
56 | return channel;
57 | }
58 |
59 | public long getId() {
60 | return id;
61 | }
62 |
63 | public void read() throws IOException {
64 | this.lastReadTime = System.currentTimeMillis();
65 | int size = channel.read(readBuffer);
66 | if (size == -1) {
67 | // client closes the connection
68 | this.close();
69 | LOGGER.warn(" it return -1 when doing a read from channel,client closes the connection,we close the connection also.");
70 | } else if (size == 0) {
71 | if (!channel.isOpen()) {
72 | this.close();
73 | LOGGER.warn(" it return 0 when doing a read from channel,and channel is not open,we close the connection.");
74 | throw new IOException();
75 | }
76 | }
77 | if (size > 0)
78 | netInBytes += size;
79 | }
80 |
81 | public void close() throws IOException {
82 | this.processEvent(ConnectionEvents.CLOSE);
83 | channel.close();
84 | isClose = true;
85 | if (readBuffer != null) {
86 | reactor.getReactorPool().getBufferPool().recycle(readBuffer);
87 | this.readBuffer = null;
88 | }
89 | }
90 |
91 | public void write() throws IOException {
92 | this.lastWriteTime = System.currentTimeMillis();
93 | ByteBuffer buffer;
94 | while ((buffer = writeQueue.poll()) != null) {
95 | buffer.flip();
96 | while (buffer.hasRemaining()) {
97 | int len = channel.write(buffer);
98 | if (len < 0) {
99 | throw new EOFException();
100 | }
101 | if (len == 0) {
102 | selectionKey.interestOps(selectionKey.interestOps()
103 | | SelectionKey.OP_WRITE);
104 | selectionKey.selector().wakeup();
105 | break;
106 | }
107 | netOutBytes += len;
108 | }
109 | reactor.getReactorPool().getBufferPool().recycle(buffer);
110 | }
111 | selectionKey.interestOps(selectionKey.interestOps()
112 | & ~SelectionKey.OP_WRITE);
113 | }
114 |
115 | public ByteBuffer getReadBuffer() {
116 | return readBuffer;
117 | }
118 |
119 | public void WriteToQueue(ByteBuffer buffer) {
120 | writeQueue.add(buffer);
121 | }
122 |
123 | public void register(Selector selector) throws Throwable {
124 | selectionKey = channel.register(selector, SelectionKey.OP_READ, this);
125 | processEvent(ConnectionEvents.REGISTE);
126 | }
127 |
128 | public void processEvent(int event) {
129 | for (ConnectionEventHandler handler : eventHandlers)
130 | handler.event(this, event);
131 | }
132 |
133 | public void addEventHandler(ConnectionEventHandler handler) {
134 | eventHandlers.add(handler);
135 | }
136 |
137 | public long getLastReadTime() {
138 | return lastReadTime;
139 | }
140 |
141 | public long getLastWriteTime() {
142 | return lastWriteTime;
143 | }
144 |
145 | public long getNetInBytes() {
146 | return netInBytes;
147 | }
148 |
149 | public long getNetOutBytes() {
150 | return netOutBytes;
151 | }
152 |
153 | public Reactor getReactor() {
154 | return reactor;
155 | }
156 |
157 | public boolean isClose() {
158 | return isClose;
159 | }
160 |
161 | public void setClose(boolean isClose) {
162 | this.isClose = isClose;
163 | }
164 |
165 | public String getCharset() {
166 | return charset;
167 | }
168 |
169 | public void setCharset(String charset) {
170 | this.charset = charset;
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/connection/ConnectionEventHandler.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.connection;
2 |
3 | /**
4 | *
5 | * connection event handler interface.
6 | * @author
7 | * seaboat
8 | * email: 849586227@qq.com
9 | * blog: http://blog.csdn.net/wangyangzhizhou
10 | * @version 1.0
11 | */
12 | public interface ConnectionEventHandler {
13 |
14 | public void event(Connection connection, int event);
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/connection/ConnectionEvents.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.connection;
2 |
3 | /**
4 | *
5 | * connection event.
6 | * @author
7 | * seaboat
8 | * email: 849586227@qq.com
9 | * blog: http://blog.csdn.net/wangyangzhizhou
10 | * @version 1.0
11 | */
12 | public class ConnectionEvents {
13 |
14 | public static int ACCEPT = 1;
15 |
16 | public static int REGISTE = 2;
17 |
18 | public static int CLOSE = 4;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/connection/ConnectionFactory.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.connection;
2 |
3 | import java.nio.channels.SocketChannel;
4 |
5 | import com.seaboat.net.reactor.Reactor;
6 |
7 | /**
8 | *
9 | * a connection factory.
10 | * @author
11 | * seaboat
12 | * email: 849586227@qq.com
13 | * blog: http://blog.csdn.net/wangyangzhizhou
14 | * @version 1.0
15 | */
16 | public interface ConnectionFactory {
17 |
18 | public Connection createConnection(SocketChannel channel, long id,
19 | Reactor reactor);
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/connection/DefaultConnectionFactory.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.connection;
2 |
3 | import java.nio.channels.SocketChannel;
4 |
5 | import com.seaboat.net.reactor.Reactor;
6 |
7 | /**
8 | *
9 | * the default connection factory.
10 | * @author
11 | * seaboat
12 | * email: 849586227@qq.com
13 | * blog: http://blog.csdn.net/wangyangzhizhou
14 | * @version 1.0
15 | */
16 | public class DefaultConnectionFactory implements ConnectionFactory {
17 |
18 | public Connection createConnection(SocketChannel channel, long id,
19 | Reactor reactor) {
20 | return new Connection(channel, id, reactor);
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/seaboat/net/reactor/handler/Handler.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.handler;
2 |
3 | import java.io.IOException;
4 |
5 | import com.seaboat.net.reactor.connection.Connection;
6 |
7 | /**
8 | *
9 | * @author seaboat
10 | * @date 2016-08-25
11 | * @version 1.0
12 | * email: 849586227@qq.com
13 | * blog: http://blog.csdn.net/wangyangzhizhou
14 | * This Handler will be call when there is a data having be ready.
15 | */
16 | public interface Handler {
17 |
18 | public void handle(Connection connection) throws IOException;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/java/com/seaboat/net/reactor/test/Bootstrap.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.test;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import com.seaboat.net.reactor.Acceptor;
7 | import com.seaboat.net.reactor.ReactorPool;
8 | import com.seaboat.net.reactor.handler.Handler;
9 |
10 | /**
11 | *
12 | * @author seaboat
13 | * @date 2016-08-25
14 | * @version 1.0
15 | * email: 849586227@qq.com
16 | * blog: http://blog.csdn.net/wangyangzhizhou
17 | * The reactor bootstrap.
18 | */
19 | public class Bootstrap {
20 | private static final Logger LOGGER = LoggerFactory
21 | .getLogger(Bootstrap.class);
22 | private static String acceptorName = "acceptor-thread";
23 | private static String host = "localhost";
24 | private static int port = 6789;
25 |
26 | public static void main(String[] args) {
27 | try {
28 | LOGGER.info("starting up ......");
29 | Handler handler = new MyHandler();
30 | ReactorPool reactorPool = new ReactorPool(Runtime.getRuntime().availableProcessors(), handler);
31 | new Acceptor(reactorPool, acceptorName, host, port).start();
32 | LOGGER.info("started up successfully.");
33 | while (true) {
34 | Thread.sleep(300 * 1000);
35 | }
36 | } catch (Throwable e) {
37 | LOGGER.error(" launch error", e);
38 | System.exit(-1);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/com/seaboat/net/reactor/test/BufferPoolTest.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.test;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.util.ArrayList;
5 |
6 | import com.seaboat.net.reactor.BufferPool;
7 |
8 | public class BufferPoolTest {
9 |
10 | @org.junit.Test
11 | public void Test() {
12 | BufferPool pool = new BufferPool(1024 * 5, 1024);
13 | int i = pool.capacity();
14 | ArrayList all = new ArrayList();
15 | for (int j = 0; j <= i; j++) {
16 | all.add(pool.allocate());
17 | }
18 | for (ByteBuffer buf : all) {
19 | pool.recycle(buf);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/java/com/seaboat/net/reactor/test/MyHandler.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.test;
2 |
3 | import java.io.IOException;
4 | import java.nio.Buffer;
5 | import java.nio.ByteBuffer;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import com.seaboat.net.reactor.connection.Connection;
11 | import com.seaboat.net.reactor.handler.Handler;
12 |
13 | /**
14 | *
15 | * @author seaboat
16 | * @date 2016-08-25
17 | * @version 1.0
18 | * email: 849586227@qq.com
19 | * blog: http://blog.csdn.net/wangyangzhizhou
20 | * Demo.
21 | */
22 | public class MyHandler implements Handler {
23 |
24 | private static final Logger LOGGER = LoggerFactory
25 | .getLogger(MyHandler.class);
26 | private long readSize;
27 |
28 | /**
29 | * The logic to deal with the received data.
30 | *
31 | * It means that reactor will trigger this function once the data is received.
32 | * @throws IOException
33 | */
34 | public void handle(Connection connection) throws IOException {
35 | Buffer buff = connection.getReadBuffer();
36 | readSize = +readSize + buff.position();
37 | LOGGER.info(connection.getId() + " connection has receive " + readSize);
38 | if (readSize % 5 == 0) {
39 | ByteBuffer sendBuffer = ByteBuffer.allocate(10);
40 | ;
41 | ByteBuffer.wrap("hello".getBytes(connection.getCharset()));
42 | connection.WriteToQueue(sendBuffer);
43 | connection.write();
44 | }
45 |
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/com/seaboat/net/reactor/test/SocketClient.java:
--------------------------------------------------------------------------------
1 | package com.seaboat.net.reactor.test;
2 |
3 | import java.net.Socket;
4 |
5 | import org.junit.Test;
6 |
7 | public class SocketClient {
8 |
9 | @Test
10 | public void visit() {
11 | for(int i = 0 ; i <= 100; i++){
12 | new Thread(){
13 | public void run(){
14 | try {
15 | Socket socket = new Socket("132.121.95.184", 6789);
16 | for (;;) {
17 | socket.getOutputStream().write(new byte[] { 1, 2, 3, 4, 5 });
18 | Thread.currentThread().sleep(100);
19 | }
20 | } catch (Exception e) {
21 | e.printStackTrace();
22 | }
23 | };
24 | }.start();;
25 | }
26 | while(true){
27 | try {
28 | Thread.currentThread().sleep(3000);
29 | } catch (InterruptedException e) {
30 | // TODO Auto-generated catch block
31 | e.printStackTrace();
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------