SocketSwitch was not used.
IMessageWriter + IMessageWriterFactory are not necessary, as messages are just buffers that need to be drained to the socket. Instead we have a standard MessageWriter that does that.
This commit is contained in:
parent
4bb99c6c38
commit
976ecb6af2
@ -1,23 +0,0 @@
|
|||||||
package com.jenkov.nioserver;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.Selector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by jjenkov on 16-10-2015.
|
|
||||||
*/
|
|
||||||
public interface IMessageWriter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by IMessageProcessor - so no access to write Selector from here.
|
|
||||||
*
|
|
||||||
* @param message
|
|
||||||
*/
|
|
||||||
public void enqueue(Message message);
|
|
||||||
|
|
||||||
public void write(Socket socket, ByteBuffer byteBuffer) throws IOException;
|
|
||||||
|
|
||||||
public boolean isEmpty();
|
|
||||||
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package com.jenkov.nioserver;
|
|
||||||
|
|
||||||
import java.nio.channels.Selector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by jjenkov on 16-10-2015.
|
|
||||||
*/
|
|
||||||
public interface IMessageWriterFactory {
|
|
||||||
|
|
||||||
public IMessageWriter createMessageWriter();
|
|
||||||
|
|
||||||
}
|
|
@ -8,7 +8,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* Created by jjenkov on 21-10-2015.
|
* Created by jjenkov on 21-10-2015.
|
||||||
*/
|
*/
|
||||||
public class MessageWriter implements IMessageWriter {
|
public class MessageWriter {
|
||||||
|
|
||||||
private List<Message> writeQueue = new ArrayList<>();
|
private List<Message> writeQueue = new ArrayList<>();
|
||||||
private Message messageInProgress = null;
|
private Message messageInProgress = null;
|
||||||
@ -17,25 +17,15 @@ public class MessageWriter implements IMessageWriter {
|
|||||||
public MessageWriter() {
|
public MessageWriter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void enqueue(Message message) {
|
public void enqueue(Message message) {
|
||||||
if(this.messageInProgress == null){
|
if(this.messageInProgress == null){
|
||||||
this.messageInProgress = message;
|
this.messageInProgress = message;
|
||||||
System.out.println("Message set as message in progress.");
|
|
||||||
} else {
|
} else {
|
||||||
this.writeQueue.add(message);
|
this.writeQueue.add(message);
|
||||||
System.out.println("Message enqueued.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo register socket for write interest
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(Socket socket, ByteBuffer byteBuffer) throws IOException {
|
public void write(Socket socket, ByteBuffer byteBuffer) throws IOException {
|
||||||
System.out.println("Writing message to socket");
|
|
||||||
|
|
||||||
byteBuffer.put(this.messageInProgress.sharedArray, this.messageInProgress.offset + this.bytesWritten, this.messageInProgress.length - this.bytesWritten);
|
byteBuffer.put(this.messageInProgress.sharedArray, this.messageInProgress.offset + this.bytesWritten, this.messageInProgress.length - this.bytesWritten);
|
||||||
byteBuffer.flip();
|
byteBuffer.flip();
|
||||||
|
|
||||||
@ -52,8 +42,8 @@ public class MessageWriter implements IMessageWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return this.writeQueue.isEmpty() && this.messageInProgress == null;
|
return this.writeQueue.isEmpty() && this.messageInProgress == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,15 +12,16 @@ import java.util.*;
|
|||||||
*/
|
*/
|
||||||
public class ServerCore implements Runnable {
|
public class ServerCore implements Runnable {
|
||||||
|
|
||||||
private MessageBuffer readMessageBuffer = null;
|
private Queue<Socket> inboundSocketQueue = null;
|
||||||
private MessageBuffer writeMessageBuffer = null;
|
|
||||||
private Queue<Socket> inboundSocketQueue = null;
|
|
||||||
private Queue<Message> outboundMessageQueue = new LinkedList<>(); //todo use a better / faster queue.
|
|
||||||
|
|
||||||
private Map<Long, Socket> socketMap = new HashMap<>();
|
private MessageBuffer readMessageBuffer = null; //todo Not used now - but perhaps will be later - to check for space in the buffer before reading from sockets
|
||||||
|
private MessageBuffer writeMessageBuffer = null; //todo Not used now - but perhaps will be later - to check for space in the buffer before reading from sockets (space for more to write?)
|
||||||
|
|
||||||
private IMessageReaderFactory messageReaderFactory = null;
|
private IMessageReaderFactory messageReaderFactory = null;
|
||||||
private IMessageWriterFactory messageWriterFactory = null;
|
|
||||||
|
private Queue<Message> outboundMessageQueue = new LinkedList<>(); //todo use a better / faster queue.
|
||||||
|
|
||||||
|
private Map<Long, Socket> socketMap = new HashMap<>();
|
||||||
|
|
||||||
private ByteBuffer readByteBuffer = ByteBuffer.allocate(1024 * 1024);
|
private ByteBuffer readByteBuffer = ByteBuffer.allocate(1024 * 1024);
|
||||||
private ByteBuffer writeByteBuffer = ByteBuffer.allocate(1024 * 1024);
|
private ByteBuffer writeByteBuffer = ByteBuffer.allocate(1024 * 1024);
|
||||||
@ -36,7 +37,7 @@ public class ServerCore implements Runnable {
|
|||||||
private Set<Socket> nonEmptyToEmptySockets = new HashSet<>();
|
private Set<Socket> nonEmptyToEmptySockets = new HashSet<>();
|
||||||
|
|
||||||
|
|
||||||
public ServerCore(Queue<Socket> inboundSocketQueue, MessageBuffer readMessageBuffer, MessageBuffer writeMessageBuffer, IMessageReaderFactory messageReaderFactory, IMessageWriterFactory messageWriterFactory, IMessageProcessor messageProcessor) throws IOException {
|
public ServerCore(Queue<Socket> inboundSocketQueue, MessageBuffer readMessageBuffer, MessageBuffer writeMessageBuffer, IMessageReaderFactory messageReaderFactory, IMessageProcessor messageProcessor) throws IOException {
|
||||||
this.inboundSocketQueue = inboundSocketQueue;
|
this.inboundSocketQueue = inboundSocketQueue;
|
||||||
|
|
||||||
this.readMessageBuffer = readMessageBuffer;
|
this.readMessageBuffer = readMessageBuffer;
|
||||||
@ -44,7 +45,6 @@ public class ServerCore implements Runnable {
|
|||||||
this.writeProxy = new WriteProxy(writeMessageBuffer, this.outboundMessageQueue);
|
this.writeProxy = new WriteProxy(writeMessageBuffer, this.outboundMessageQueue);
|
||||||
|
|
||||||
this.messageReaderFactory = messageReaderFactory;
|
this.messageReaderFactory = messageReaderFactory;
|
||||||
this.messageWriterFactory = messageWriterFactory;
|
|
||||||
|
|
||||||
this.messageProcessor = messageProcessor;
|
this.messageProcessor = messageProcessor;
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ public class ServerCore implements Runnable {
|
|||||||
newSocket.socketChannel.configureBlocking(false);
|
newSocket.socketChannel.configureBlocking(false);
|
||||||
|
|
||||||
newSocket.messageReader = this.messageReaderFactory.createMessageReader();
|
newSocket.messageReader = this.messageReaderFactory.createMessageReader();
|
||||||
newSocket.messageWriter = this.messageWriterFactory.createMessageWriter();
|
newSocket.messageWriter = new MessageWriter();
|
||||||
|
|
||||||
this.socketMap.put(newSocket.socketId, newSocket);
|
this.socketMap.put(newSocket.socketId, newSocket);
|
||||||
|
|
||||||
@ -128,6 +128,7 @@ public class ServerCore implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(socket.endOfStreamReached){
|
if(socket.endOfStreamReached){
|
||||||
|
System.out.println("Socket closed: " + socket.socketId);
|
||||||
this.socketMap.remove(socket.socketId);
|
this.socketMap.remove(socket.socketId);
|
||||||
key.attach(null);
|
key.attach(null);
|
||||||
key.cancel();
|
key.cancel();
|
||||||
@ -147,9 +148,7 @@ public class ServerCore implements Runnable {
|
|||||||
// Register all sockets that *have* data and which are not yet registered.
|
// Register all sockets that *have* data and which are not yet registered.
|
||||||
registerNonEmptySockets();
|
registerNonEmptySockets();
|
||||||
|
|
||||||
|
|
||||||
// Select from the Selector.
|
// Select from the Selector.
|
||||||
|
|
||||||
int writeReady = this.writeSelector.selectNow();
|
int writeReady = this.writeSelector.selectNow();
|
||||||
|
|
||||||
if(writeReady > 0){
|
if(writeReady > 0){
|
||||||
@ -185,6 +184,7 @@ public class ServerCore implements Runnable {
|
|||||||
private void cancelEmptySockets() {
|
private void cancelEmptySockets() {
|
||||||
for(Socket socket : nonEmptyToEmptySockets){
|
for(Socket socket : nonEmptyToEmptySockets){
|
||||||
SelectionKey key = socket.socketChannel.keyFor(this.writeSelector);
|
SelectionKey key = socket.socketChannel.keyFor(this.writeSelector);
|
||||||
|
|
||||||
key.cancel();
|
key.cancel();
|
||||||
}
|
}
|
||||||
nonEmptyToEmptySockets.clear();
|
nonEmptyToEmptySockets.clear();
|
||||||
@ -196,7 +196,7 @@ public class ServerCore implements Runnable {
|
|||||||
Socket socket = this.socketMap.get(outMessage.socketId);
|
Socket socket = this.socketMap.get(outMessage.socketId);
|
||||||
|
|
||||||
if(socket != null){
|
if(socket != null){
|
||||||
IMessageWriter messageWriter = socket.messageWriter;
|
MessageWriter messageWriter = socket.messageWriter;
|
||||||
if(messageWriter.isEmpty()){
|
if(messageWriter.isEmpty()){
|
||||||
messageWriter.enqueue(outMessage);
|
messageWriter.enqueue(outMessage);
|
||||||
nonEmptyToEmptySockets.remove(socket);
|
nonEmptyToEmptySockets.remove(socket);
|
||||||
|
@ -11,9 +11,9 @@ public class Socket {
|
|||||||
|
|
||||||
public long socketId;
|
public long socketId;
|
||||||
|
|
||||||
public SocketChannel socketChannel = null;
|
public SocketChannel socketChannel = null;
|
||||||
public IMessageReader messageReader = null;
|
public IMessageReader messageReader = null;
|
||||||
public IMessageWriter messageWriter = null;
|
public MessageWriter messageWriter = null;
|
||||||
|
|
||||||
public boolean endOfStreamReached = false;
|
public boolean endOfStreamReached = false;
|
||||||
|
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
package com.jenkov.nioserver;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Todo - rename class to SocketWriter ?
|
|
||||||
*
|
|
||||||
* Created by jjenkov on 20-10-2015.
|
|
||||||
*/
|
|
||||||
public class SocketSwitch {
|
|
||||||
|
|
||||||
private Map<Long, Socket> socketMap = new HashMap<>();
|
|
||||||
private Map<Long, IMessageWriter> messageWriterMap = new HashMap<>();
|
|
||||||
|
|
||||||
private Set<Socket> socketsToCancel = new HashSet<>();
|
|
||||||
private Set<Socket> socketsToRegister = new HashSet<>();
|
|
||||||
|
|
||||||
|
|
||||||
public Set<Socket> getSocketsToCancel() {
|
|
||||||
return this.socketsToCancel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<Socket> getSocketsToRegister() {
|
|
||||||
return this.socketsToRegister;
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo change putMessageWriter to registerSocket();
|
|
||||||
public void registerSocket(Socket socket){
|
|
||||||
System.out.println("Registering socket: " + socket.socketId );
|
|
||||||
this.socketMap.put(socket.socketId, socket);
|
|
||||||
this.messageWriterMap.put(socket.socketId, socket.messageWriter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void enqueueMessage(long socketId, Message message){
|
|
||||||
IMessageWriter messageWriter = this.messageWriterMap.get(socketId);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(messageWriter != null){
|
|
||||||
boolean wasEmpty = messageWriter.isEmpty();
|
|
||||||
messageWriter.enqueue(message);
|
|
||||||
|
|
||||||
if(wasEmpty){
|
|
||||||
Socket socket = this.socketMap.get(socketId);
|
|
||||||
|
|
||||||
if(this.socketsToCancel.contains(socket)){
|
|
||||||
//Socket already registered with Selector. Remove cancellation request but
|
|
||||||
// do not re-register socket with Selector
|
|
||||||
this.socketsToCancel.remove(socket);
|
|
||||||
} else {
|
|
||||||
//Socket not currently registered with Selector. Request registration.
|
|
||||||
this.socketsToRegister.add(socket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeToSocket(Socket socket, ByteBuffer writeByteBuffer) throws IOException {
|
|
||||||
IMessageWriter messageWriter = messageWriterMap.get(socket.socketId);
|
|
||||||
if(messageWriter != null){
|
|
||||||
messageWriter.write(socket, writeByteBuffer);
|
|
||||||
|
|
||||||
if(messageWriter.isEmpty()) {
|
|
||||||
this.socketsToCancel.add(socket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unregisterSocket(long socketId){
|
|
||||||
System.out.println("Unregistering socket: " + socketId);
|
|
||||||
this.socketMap.remove(socketId);
|
|
||||||
|
|
||||||
//todo in case the message writer should survive a "missing connection" - don't remove it.
|
|
||||||
this.messageWriterMap.remove(socketId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeMessageWriter(long id){
|
|
||||||
this.socketMap.remove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ package com.jenkov.nioserver.example;
|
|||||||
|
|
||||||
import com.jenkov.nioserver.*;
|
import com.jenkov.nioserver.*;
|
||||||
import com.jenkov.nioserver.http.HttpMessageReaderFactory;
|
import com.jenkov.nioserver.http.HttpMessageReaderFactory;
|
||||||
import com.jenkov.nioserver.http.HttpMessageWriterFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
@ -14,7 +13,6 @@ import java.util.concurrent.BlockingQueue;
|
|||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
|
|
||||||
BlockingQueue inboundSocketQueue = new ArrayBlockingQueue(1024);
|
BlockingQueue inboundSocketQueue = new ArrayBlockingQueue(1024);
|
||||||
|
|
||||||
Server server = new Server(9999, inboundSocketQueue);
|
Server server = new Server(9999, inboundSocketQueue);
|
||||||
@ -22,11 +20,9 @@ public class Main {
|
|||||||
Thread serverThread = new Thread(server);
|
Thread serverThread = new Thread(server);
|
||||||
serverThread.start();
|
serverThread.start();
|
||||||
|
|
||||||
|
|
||||||
MessageBuffer readMessageBuffer = new MessageBuffer();
|
MessageBuffer readMessageBuffer = new MessageBuffer();
|
||||||
MessageBuffer writeMessageBuffer = new MessageBuffer();
|
MessageBuffer writeMessageBuffer = new MessageBuffer();
|
||||||
IMessageReaderFactory messageReaderFactory = new HttpMessageReaderFactory(readMessageBuffer);
|
IMessageReaderFactory messageReaderFactory = new HttpMessageReaderFactory(readMessageBuffer);
|
||||||
IMessageWriterFactory messageWriterFactory = new HttpMessageWriterFactory();
|
|
||||||
|
|
||||||
String httpResponse = "HTTP/1.1 200 OK\r\n" +
|
String httpResponse = "HTTP/1.1 200 OK\r\n" +
|
||||||
"Content-Length: 38\r\n" +
|
"Content-Length: 38\r\n" +
|
||||||
@ -46,7 +42,7 @@ public class Main {
|
|||||||
writeProxy.enqueue(response);
|
writeProxy.enqueue(response);
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerCore serverCore = new ServerCore(inboundSocketQueue, readMessageBuffer, writeMessageBuffer, messageReaderFactory, messageWriterFactory, messageProcessor);
|
ServerCore serverCore = new ServerCore(inboundSocketQueue, readMessageBuffer, writeMessageBuffer, messageReaderFactory, messageProcessor);
|
||||||
Thread serverCoreThread = new Thread(serverCore);
|
Thread serverCoreThread = new Thread(serverCore);
|
||||||
serverCoreThread.start();
|
serverCoreThread.start();
|
||||||
}
|
}
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
package com.jenkov.nioserver.http;
|
|
||||||
|
|
||||||
import com.jenkov.nioserver.IMessageWriter;
|
|
||||||
import com.jenkov.nioserver.Message;
|
|
||||||
import com.jenkov.nioserver.SocketSwitch;
|
|
||||||
import com.jenkov.nioserver.Socket;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by jjenkov on 21-10-2015.
|
|
||||||
*/
|
|
||||||
public class HttpMessageWriter implements IMessageWriter {
|
|
||||||
|
|
||||||
private List<Message> writeQueue = new ArrayList<>();
|
|
||||||
private Message messageInProgress = null;
|
|
||||||
private int bytesWritten = 0;
|
|
||||||
|
|
||||||
public HttpMessageWriter() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void enqueue(Message message) {
|
|
||||||
if(this.messageInProgress == null){
|
|
||||||
this.messageInProgress = message;
|
|
||||||
System.out.println("Message set as message in progress.");
|
|
||||||
} else {
|
|
||||||
this.writeQueue.add(message);
|
|
||||||
System.out.println("Message enqueued.");
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo register socket for write interest
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(Socket socket, ByteBuffer byteBuffer) throws IOException {
|
|
||||||
System.out.println("Writing message to socket");
|
|
||||||
|
|
||||||
byteBuffer.put(this.messageInProgress.sharedArray, this.messageInProgress.offset + this.bytesWritten, this.messageInProgress.length - this.bytesWritten);
|
|
||||||
byteBuffer.flip();
|
|
||||||
|
|
||||||
this.bytesWritten += socket.write(byteBuffer);
|
|
||||||
byteBuffer.clear();
|
|
||||||
|
|
||||||
if(bytesWritten >= this.messageInProgress.length){
|
|
||||||
if(this.writeQueue.size() > 0){
|
|
||||||
this.messageInProgress = this.writeQueue.remove(0);
|
|
||||||
} else {
|
|
||||||
this.messageInProgress = null;
|
|
||||||
//todo unregister from selector
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return this.writeQueue.isEmpty() && this.messageInProgress == null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package com.jenkov.nioserver.http;
|
|
||||||
|
|
||||||
import com.jenkov.nioserver.IMessageWriter;
|
|
||||||
import com.jenkov.nioserver.IMessageWriterFactory;
|
|
||||||
|
|
||||||
import java.nio.channels.Selector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by jjenkov on 19-10-2015.
|
|
||||||
*/
|
|
||||||
public class HttpMessageWriterFactory implements IMessageWriterFactory {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IMessageWriter createMessageWriter() {
|
|
||||||
return new HttpMessageWriter();
|
|
||||||
//return null;
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user