I currently have a desktop server written using the NIO java library in non-blocking mode. You can find the full server project here. I have also created a non-blocking NIO client for testing purposes. You can find that project here. In the end, the server is supposed to be used for an android instant messaging application. I have based both the client and server on the idea of sending 'Packets' to communicate. What i mean by this is, references of the Packet class are packed into byte buffers and sent through socket channels. They are then de-serialized on the other side and executed.
My current issue:
It seems that my test client drops it's connection when it connects to the server. I know the problem is client sided because when I connect to my server through command line using telnet, the connection does not drop. The weird part is that the server is printing out that a connection to it has been established but never says a connection has been lost/terminated when the client drops the connection.
This is the Client class that handles all the nio networking...
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.baiocchi.enigma.client.test.Engine;
import org.baiocchi.enigma.client.test.databundle.DataBundle;
import org.baiocchi.enigma.client.test.packet.Packet;
import org.baiocchi.enigma.client.test.ui.LogType;
import org.baiocchi.enigma.client.test.ui.Logger;
public class Client extends Thread {
private boolean running;
private final int port;
private SocketChannel connection;
private final ByteBuffer buffer;
private Selector selector;
private List<DataBundle> pendingDataBundleQue;
public Client(int port) {
this.port = port;
pendingDataBundleQue = new LinkedList<DataBundle>();
buffer = ByteBuffer.allocate(8192);
try {
selector = initiateSelector();
connection = initiateConnection();
} catch (IOException e) {
e.printStackTrace();
}
}
private Selector initiateSelector() throws IOException {
return SelectorProvider.provider().openSelector();
}
private SocketChannel initiateConnection() throws IOException {
SocketChannel connection = SocketChannel.open();
connection.configureBlocking(false);
connection.connect(new InetSocketAddress("localhost", port));
connection.register(selector, SelectionKey.OP_CONNECT);
return connection;
}
public SocketChannel getConnection() {
return connection;
}
#Override
public void start() {
running = true;
super.start();
}
public void addToPendingDataBundleQue(DataBundle bundle) {
synchronized (pendingDataBundleQue) {
pendingDataBundleQue.add(bundle);
}
}
#Override
public void run() {
while (running) {
System.out.println("loop");
try {
synchronized (pendingDataBundleQue) {
System.out.println("Checking for que changes.");
if (!pendingDataBundleQue.isEmpty()) {
System.out.println("Found que change.");
SelectionKey key = connection.keyFor(selector);
key.interestOps(SelectionKey.OP_WRITE);
}
}
System.out.println("Selecting keys");
selector.select();
System.out.println("Creating selected keys list.");
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
System.out.println("scrolling through list");
SelectionKey key = (SelectionKey) selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
System.out.println("invalid");
continue;
} else if (key.isConnectable()) {
System.out.println("connect");
establishConnection(key);
} else if (key.isReadable()) {
System.out.println("read");
readData(key);
} else if (key.isWritable()) {
System.out.println("write");
writeData(key);
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
System.out.println("Broke loop");
}
private void writeData(SelectionKey key) throws IOException {
synchronized (pendingDataBundleQue) {
SocketChannel connection = (SocketChannel) key.channel();
for (DataBundle bundle : pendingDataBundleQue) {
System.out.println("sent packet");
connection.write(bundle.getBuffer());
}
pendingDataBundleQue.clear();
if (pendingDataBundleQue.isEmpty()) {
Logger.write("All packets sent.", LogType.CLIENT);
connection.keyFor(selector).interestOps(SelectionKey.OP_READ);
}
}
}
private void readData(SelectionKey key) throws IOException, ClassNotFoundException {
buffer.clear();
int byteCount;
try {
byteCount = connection.read(buffer);
} catch (IOException e) {
Logger.writeException("Connenction closed.", LogType.CLIENT);
connection.close();
key.cancel();
return;
}
if (byteCount == -1) {
Logger.writeException("Connection error. Attempting to terminate connection.", LogType.CLIENT);
key.channel().close();
key.cancel();
}
Engine.getInstance().getPacketProcessor().processData(buffer);
}
private void establishConnection(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
try {
if (channel.finishConnect()) {
Logger.write("Connection established.", LogType.CLIENT);
key.interestOps(SelectionKey.OP_READ);
}
} catch (IOException e) {
Logger.write("Failed to establish connection.", LogType.CLIENT);
key.channel().close();
key.cancel();
return;
}
}
}
The server class that handles all the server networking is here.
package org.baiocchi.enigma.server.network;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayList;
import java.util.Iterator;
import org.baiocchi.enigma.server.Engine;
import org.baiocchi.enigma.server.databundle.DataBundle;
import org.baiocchi.enigma.server.ui.components.logger.LogType;
import org.baiocchi.enigma.server.ui.components.logger.Logger;
public class Server extends Thread {
private boolean running;
private final int port;
private ServerSocketChannel server;
private final ByteBuffer buffer;
private Selector selector;
private ArrayList<DataBundle> pendingDataBundleQue;
public Server(int port) {
this.port = port;
buffer = ByteBuffer.allocate(8192);
pendingDataBundleQue = new ArrayList<DataBundle>();
try {
server = ServerSocketChannel.open().bind(new InetSocketAddress("localhost", port));
server.configureBlocking(false);
selector = SelectorProvider.provider().openSelector();
server.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void start() {
running = true;
super.start();
}
public void terminateConnection(SocketChannel channel) {
SelectionKey key = channel.keyFor(selector);
try {
key.channel().close();
key.cancel();
} catch (IOException e) {
e.printStackTrace();
}
}
public void addToPendingPacketQue(DataBundle bundle) {
synchronized (pendingDataBundleQue) {
pendingDataBundleQue.add(bundle);
}
}
#Override
public void run() {
while (running) {
try {
synchronized (pendingDataBundleQue) {
if (!pendingDataBundleQue.isEmpty()) {
for (DataBundle bundle : pendingDataBundleQue) {
SelectionKey key = bundle.getChannel().keyFor(selector);
key.interestOps(SelectionKey.OP_WRITE);
}
}
}
selector.select();
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
SelectionKey key = (SelectionKey) selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
continue;
} else if (key.isAcceptable()) {
acceptConnection(key);
} else if (key.isReadable()) {
readData(key);
} else if (key.isWritable()) {
writeData(key);
}
}
} catch (IOException | ClassNotFoundException e) {
Logger.writeException("Internal server error.", LogType.SERVER);
Logger.writeException(e.getMessage(), LogType.SERVER);
}
}
}
private void writeData(SelectionKey key) throws IOException {
DataBundle bundle = null;
for (DataBundle b : pendingDataBundleQue) {
if (b.getChannel().equals((SocketChannel) key.channel())) {
bundle = b;
break;
}
}
if (bundle == null) {
Logger.writeException("Couldn't find out bound packet in list.", LogType.SERVER);
return;
}
SocketChannel connection = bundle.getChannel();
connection.write(bundle.getBuffer());
connection.keyFor(selector).interestOps(SelectionKey.OP_READ);
pendingDataBundleQue.remove(bundle);
}
private void readData(SelectionKey key) throws IOException, ClassNotFoundException {
SocketChannel channel = (SocketChannel) key.channel();
buffer.clear();
int byteCount;
try {
byteCount = channel.read(buffer);
} catch (IOException e) {
Logger.writeException("Connenction terminated.", LogType.SERVER);
channel.close();
key.cancel();
return;
}
if (byteCount == -1) {
Logger.writeException("Connection error. Terminating connection.", LogType.SERVER);
key.channel().close();
key.cancel();
return;
}
Engine.getInstance().getPacketProcessor().processData(buffer, channel);
}
private void acceptConnection(SelectionKey key) throws IOException {
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel connection = channel.accept();
connection.configureBlocking(false);
connection.register(selector, SelectionKey.OP_READ);
Logger.write("Connection established.", LogType.SERVER);
}
}
Thank you in advance!
There is no evidence here that the client has dropped the connection. If it had, one of these blocks would have executed at the server:
try {
byteCount = channel.read(buffer);
} catch (IOException e) {
Logger.writeException("Connenction terminated.", LogType.SERVER);
channel.close();
key.cancel();
return;
}
if (byteCount == -1) {
Logger.writeException("Connection error. Terminating connection.", LogType.SERVER);
key.channel().close();
key.cancel();
return;
}
I conclude that the client isn't dropping the connection at all.
I note that you have a method called Logger.writeException() which you are never using to log an exception, and that your log messages are back to front: catch (IOException ) indicates a connection error, on which you should log the actual exception, and readBytes == -1 indicates 'connection terminated', which is not an error.
I also note a missing return in the corresponding code in the client.
NB Closing the channel cancels the key. You don't need to cancel it yourself.
Related
Please help .
When you first start the server, create a thread pool in advance (5 threads) and create 5 selectors to read, pass one selector to each Runnable, and immediately send the submit function to the thread pool.
Then, create a selector that accepts the user and run it on the main thread.
The problem here is that the Selector in the main thread detects the user's request and registers the returned SocketChannel in one of the 5 Read Selectors.
And when the connection is successful, the user sends text data every second, but there is no response from the Read Selector.
I don't know why this problem occurs.. ㅜㅜ
Main.java
public class Main {
public static void main(String[] args) {
Server server = new Quarter();
}
}
Server.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public abstract class Server {
protected volatile boolean isStart = true;
// 192.168.0.11 company
// 192.168.75.35 house
// 192.168.75.97 my notebook
private static final String ADDRESS = "192.168.75.35";
private static final int PORT = 1111;
private static final int BACKLOG = 1024;
protected Selector acceptSelector = null;
/** 하나의 스레드에서만 접근 할것이기 때문에 동기화 처리는 필요 없음. */
protected List<Selector> readSelectors = new ArrayList<Selector>();
protected ServerSocketChannel serverSocketChannel = null;
protected InetSocketAddress inetSocketAddress = null;
private final int nThreads = 5;
protected ExecutorService threadpool = null;
/** 현재 연결된 클라이언트 수 */
protected static int connectionCount = 0;
// volatile 캐시 메모리 문제는 아닌가?
// 뭐가 문제지?
protected void init() {
try {
this.threadpool = Executors.newFixedThreadPool(this.nThreads);
for (int i = 1; i <= this.nThreads; i++) {
Selector sel = Selector.open();
this.readSelectors.add(sel);
threadpool.submit(new SubSelector(sel));
}
this.acceptSelector = Selector.open();
this.inetSocketAddress = new InetSocketAddress(ADDRESS, PORT);
this.serverSocketChannel = ServerSocketChannel.open();
// non - blocking 으로 설정.
this.serverSocketChannel.configureBlocking(false);
System.out.println("serverSocketChannel is non-blocking.");
this.serverSocketChannel.bind(this.inetSocketAddress, BACKLOG);
System.out.println("binding is success.");
this.serverSocketChannel.register(this.acceptSelector, SelectionKey.OP_ACCEPT);
System.out.println("initing is success.");
} catch (AlreadyBoundException abe) {
// 이미 사용중인 포트에 바인딩하려면 에러
System.err.println(abe.getMessage());
this.exit();
} catch (IOException e) {
System.err.println(e.getMessage());
this.exit();
}
}
protected synchronized void exit() {
try {
this.isStart = false;
this.serverSocketChannel.close();
this.acceptSelector.close();
this.threadpool.shutdown();
for (int i = 0; i < this.readSelectors.size(); i++) {
this.readSelectors.get(i).close();
}
} catch (IOException e) {
System.err.print(e.getMessage());
}
}
protected synchronized void connectCountAdd() {
connectionCount++;
}
protected synchronized void connectCountMinus() {
connectionCount--;
}
}
Quarter.java
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Quarter extends Server {
public Quarter() {
init();
this.initQuarter();
}
private void initQuarter() {
while (isStart) {
try {
int ready = acceptSelector.select();
if (ready <= 0) continue;
Set<SelectionKey> set = acceptSelector.selectedKeys();
Iterator<SelectionKey> selected = set.iterator();
while (selected.hasNext()) {
SelectionKey key = selected.next();
selected.remove();
if (!key.isValid()) continue;
if (key.isAcceptable()) this.accept(key);
}
}
catch (ClosedSelectorException cse) {
System.err.println(cse.getMessage());
}
catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
private void accept(SelectionKey key) {
try {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
if (channel == null) return;
channel.configureBlocking(false);
int targetIndex = connectionCount % readSelectors.size();
Selector tarSel = readSelectors.get(targetIndex);
channel.register(tarSel, SelectionKey.OP_READ);
connectCountAdd();
System.out.println("Connected to: " + channel.getRemoteAddress());
System.out.println("Current Connection Count: " + connectionCount);
}
catch (ClosedChannelException cce) {
// 채널이 닫힌 경우
System.err.println(cce.getMessage());
key.cancel();
}
catch (ClosedSelectorException cse) {
// 셀렉터가 닫힌 경우
System.err.println(cse.getMessage());
key.cancel();
exit();
}
catch (IOException e) {
System.err.println(e.getMessage());
key.cancel();
exit();
}
}
}
SubSelector.java
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
public class SubSelector extends Server implements Runnable {
private Selector selector = null;
Charset charset = Charset.forName("UTF-8");
public SubSelector(Selector selector) {
this.selector = selector;
}
#Override
public void run() {
while (isStart) {
try {
int ready = selector.select();
if (ready <= 0) return;
Set<SelectionKey> set = this.selector.selectedKeys();
Iterator<SelectionKey> selected = set.iterator();
while (selected.hasNext()) {
SelectionKey key = selected.next();
selected.remove();
if (!key.isValid()) continue;
if (key.isReadable()) this.read(key);
}
}
catch (ClosedSelectorException cse) {
System.err.println(cse.getMessage());
}
catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
private void read(SelectionKey key) {
try {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(100);
int readCount = channel.read(buffer);
if (readCount <= 0) return;
buffer.flip();
String result = charset.decode(buffer).toString();
System.out.println("client ip address: " + channel.getRemoteAddress());
System.out.println("client send message: " + result);
}
catch (NotYetConnectedException nyce) {
// 채널이 아직 연결되지 않은 경우
System.err.println(nyce.getMessage());
}
catch (SocketException se) {
System.err.println(se.getMessage());
this.disConnect(key);
}
catch (IOException e) {
// 기타 에러
System.err.println(e.getMessage());
}
}
private void disConnect(SelectionKey key) {
key.cancel();
selector.wakeup();
connectCountMinus();
System.out.println("Current Connection Count: " + connectionCount);
}
}
Client.java
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("192.168.75.35", 1111));
System.out.println("is Connected: " + socket.isConnected());
while (true) {
OutputStream os = socket.getOutputStream();
os.write(Thread.currentThread().getName().getBytes());
os.flush();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
I'm reading Doug Lea's Scalable I/O in Java, and I followed the Basic Reactor Design example code. But after I started server, the client can't connect to server.
Here is the Reactor class:
class Reactor implements Runnable {
private static final Logger logger = LogManager.getLogger();
final Selector selector;
final ServerSocketChannel serverSocket;
public Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false);
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
sk.attach(new Acceptor());
logger.info("server started.");
}
#Override
public void run() {
while (!Thread.interrupted()) {
for (final Iterator<SelectionKey> it = selector.selectedKeys().iterator(); it.hasNext(); it.remove()) {
dispatch(it.next());
}
}
}
private void dispatch(SelectionKey key) {
Runnable r = (Runnable) key.attachment();
if (r != null) {
r.run();
}
}
private final class Acceptor implements Runnable {
#Override
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null) {
new Handler(selector, c);
}
} catch (IOException ex) {
ex.getMessage();
}
}
}
public static void main(String[] args) throws IOException {
new Reactor(9000).run();
}
}
Handler class
final class Handler implements Runnable {
private static final Logger logger = LogManager.getLogger();
final SocketChannel c;
final SelectionKey key;
ByteBuffer buffer = ByteBuffer.allocate(1024);
public Handler(Selector sel, SocketChannel c) throws IOException {
this.c = c;
c.configureBlocking(false);
key = c.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
logger.info("client connected: " + c);
}
void read() throws IOException {
if (!buffer.hasRemaining()) {
return;
}
c.read(buffer);
}
void process() {/* */}
void write() throws IOException {
buffer.flip();
c.write(buffer);
c.close();
}
#Override
public void run() {
try {
read();
process();
write();
} catch (IOException ex) {
ex.getMessage();
}
}
}
I start server in idea and then server started is printed in console
But after I enter telnet localhost 9000 in terminal, client connected: doesn't appear.
I had to change the Reactor run method a bit. you have to call selector.select() or selector.selectNow():
#Override
public void run() {
while (!Thread.interrupted()) {
try {
int ready = selector.selectNow();
if (ready == 0){
continue;
}
Set<SelectionKey> selected = selector.selectedKeys();
Iterator<SelectionKey> it = selected.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if(key.isAcceptable() || key.isReadable()) {
dispatch(key);
}
}
selected.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
}
that allowed the client to connect.
in order to enable an echo service from Handler I implemented this:
final class Handler implements Runnable {
private static final Logger logger = LogManager.getLogger();
final SocketChannel c;
final SelectionKey key;
ByteBuffer buffer = ByteBuffer.allocate(1024);
public Handler(Selector selector, SocketChannel c) throws IOException {
this.c = c;
c.configureBlocking(false);
logger.info("client connected: " + c);
key = c.register(selector, 0);
key.attach(this);
key.interestOps(SelectionKey.OP_READ);
selector.wakeup();
}
#Override
public void run() {
try {
SocketChannel client = (SocketChannel) key.channel();
client.read(buffer);
if (new String(buffer.array()).trim().equals("close")) {
client.close();
System.out.println("close connection");
}
buffer.flip();
client.write(buffer);
buffer.clear();
} catch (IOException ex) {
ex.getMessage();
}
}
}
register the Handler instance for reading and then upon a readable selection key the run method of this instance is called to handle the reading.
I'm trying to play around with a simple client-server program, eventually aiming to make it a two-way communication. For some reason when I just instantiate the client class to make it connect to the server (without sending any data), it doesn't work, and throws Unable to establish loopback connection exception. When I put a while loop of reading through stdin, it works then. I need to establish a connection first, and then once in a while send a message to the server. How can I fix it?
Client code:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
public class ClientCore {
SelectionKey selkey;
Selector sckt_manager ;
SocketChannel sc;
public ClientCore() {
try {
sc = SocketChannel.open();
sc.configureBlocking(false);
sc.connect(new InetSocketAddress(8888));
// should not proceed until connect is finished
while (!sc.finishConnect()) {
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (sc != null) {
try {
sc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
void send(String message) {
try {
if (!message.equalsIgnoreCase("end")) {
System.out.println("Sending a request to the server ...");
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
sc.write(buffer);
}
} catch (IOException e) {
}
}
public static void main(String[] args) throws Exception {
ClientCore cl = new ClientCore();
cl.send("hello");
}
}
Server code:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.channels.spi.SelectorProvider;
public class ServerCore extends Thread {
SelectionKey selkey = null;
Selector sckt_manager = null;
public void run() {
try {
coreServer();
} catch (Exception ej) {
ej.printStackTrace();
}
}
private void coreServer() {
try {
ServerSocketChannel ssc = ServerSocketChannel.open();
try {
ssc.socket().bind(new InetSocketAddress(8888));
while (true) {
sckt_manager = SelectorProvider.provider().openSelector();
ssc.configureBlocking(false);
SocketChannel sc = ssc.accept();
register_server(ssc, SelectionKey.OP_ACCEPT);
if (sc == null) {
} else {
System.out
.println("Received an incoming connection from "
+ sc.socket().getRemoteSocketAddress());
printRequest(sc);
System.err.println("testing 1");
String HELLO_REPLY = "Sample Display";
ByteBuffer buffer = ByteBuffer.wrap(HELLO_REPLY .getBytes());
System.err.println("testing 2");
sc.write(buffer);
System.err.println("testing 3");
sc.close();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ssc != null) {
try {
ssc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (Exception E) {
System.out.println("Ex in servCORE " + E);
}
}
private static void printRequest(SocketChannel sc) throws IOException {
ReadableByteChannel rbc = Channels.newChannel(sc.socket()
.getInputStream());
WritableByteChannel wbc = Channels.newChannel(System.out);
ByteBuffer b = ByteBuffer.allocate(1024); // read 1024 bytes
while (rbc.read(b) != -1) {
b.flip();
while (b.hasRemaining()) {
wbc.write(b);
System.out.println();
}
b.clear();
}
}
public void register_server(ServerSocketChannel ssc, int selectionkey_ops)
throws Exception {
ssc.register(sckt_manager, selectionkey_ops);
}
public static void main(String[] args) {
ServerCore st = new ServerCore();
st.coreServer();
}
}
I'm doing an exercise requires making a server - client chat program using Java Non-Blocking IO. At the moment, the way the program works is simple: when a client send a message to the server, the server (which already keep track of all the clients) echo the message back to all the clients.
This is my some parts of my server-side code:
public static ByteBuffer str_to_bb(String msg) {
try {
return encoder.encode(CharBuffer.wrap(msg));
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
private static void broadcastMessage(String nickname, String message) {
System.out.println(">clientSocketChannels size " + clientSocketChannels.size());
Iterator clientSocketChannelsIterator = clientSocketChannels.iterator();
while (clientSocketChannelsIterator.hasNext()) {
SocketChannel sc = (SocketChannel) clientSocketChannelsIterator.next();
try {
ByteBuffer bb = str_to_bb(message);
System.out.println("bufferRemaining: " + bb.remaining()); // returns 2048
int writeResult = sc.write(bb);
System.out.println("writeResult: " + writeResult); // returns 2048
} catch (IOException e) {
e.printStackTrace();
}
}
}
The following is my client-side code:
import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/**
* Created by ThaiSon on 7/6/2015.
*/
public class ChatRoomClientGUI {
private JTextArea textAreaMessages;
private JTextField textFieldMessage;
private JButton buttonSendMsg;
private JPanel jPanel1;
private JLabel txtFieldInfo;
private static InetAddress inetAddress;
private static final int PORT = 1234;
private static Socket socket = null;
private static Scanner input = null;
private static PrintWriter output = null;
private static ChatRoomClientGUI singleton;
public ChatRoomClientGUI() {
singleton = this;
buttonSendMsg.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if (e.getButton() == MouseEvent.BUTTON1) {
String message = textFieldMessage.getText();
output.println(message);
textFieldMessage.setText("");
}
}
});
}
public static void main(String[] args) {
JFrame promptFrame = new JFrame();
Object nickname = JOptionPane.showInputDialog(promptFrame, "Enter your nickname:");
promptFrame.dispose();
JFrame frame = new JFrame("ChatRoomClientGUI");
frame.setContentPane(new ChatRoomClientGUI().jPanel1);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
System.out.println("> Client with nickname " + nickname);
try {
inetAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
accessServer(nickname.toString());
}
private static void accessServer(String nickname) {
try {
socket = new Socket(inetAddress, PORT);
input = new Scanner(socket.getInputStream());
output = new PrintWriter(socket.getOutputStream(), true);
output.println(nickname); // Register nickname with the server
//TODO update the txtFieldInfo content
// Create a new thread to listen to InputStream event
InputStreamEvent inputStreamEvent = new InputStreamEvent(socket);
inputStreamEvent.start();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleInputStream(){
String response = input.nextLine();
System.out.println("TODO " + response);
singleton.textAreaMessages.append(response + "\n");
}
static class InputStreamEvent extends Thread{
Socket socket;
public InputStreamEvent(Socket socket){
this.socket = socket;
}
public void run(){
try {
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[2048];
int read;
while (true){
if(inputStream.available() > 0){
handleInputStream();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The problem I'm facing with now is that when I use a client (which works well with the old multithreaded server) to send message to the server, the client only get return the first message it sends. All the next responses from the server is empty (the server does send back, but only an empty message).
So my attempts to debug includes:
check if the messages from the client has reach the server or not. They does.
log the buffer.remaining() and socketChannel.write(buffer) result as shown above, all the log result seems to be normal to me.
Hope you guys can help me with this.
This:
if(inputStream.available() > 0){
Get rid of this test. With it, your client is smoking the CPU. Without it, it will block in readLine() as God intended.
Are you sure your server is still sending lines? with line terminators? If it isn't, readLine() will block forever looking for one, until end of stream or an exception occurs.
I referred the code explain by EJP on this link Java NIO Server/Client Chat App - sending data only by closing the socket
it solves my problem. use this code
import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.Scanner;
import java.nio.channels.SelectionKey;
import java.net.InetSocketAddress;
public class Client {
public static void main(String args[]) {
try {
ByteBuffer buf = ByteBuffer.allocate(200);
Scanner scanner = new Scanner(System.in);
Selector selector = Selector.open();
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_CONNECT|SelectionKey.OP_READ|SelectionKey.OP_WRITE);
boolean isConnected = socketChannel.connect(new InetSocketAddress("localhost", 5000));
if(isConnected) {
System.out.println("Connected, de-registering OP_CONNECT");
}
new Thread(new Runnable(){
private SocketChannel socketChannel;
private Selector selector;
public Runnable init(SocketChannel socketChannel, Selector selector) {
this.socketChannel = socketChannel;
this.selector = selector;
return this;
}
public void run() {
try {
ByteBuffer buf = ByteBuffer.allocate(200);
while(!Thread.interrupted()) {
int keys = selector.select();
if(keys > 0) {
for(SelectionKey key : selector.selectedKeys()) {
if(key.isConnectable()) {
boolean finishConnectResult = socketChannel.finishConnect();
socketChannel.register(this.selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
System.out.println("Finished Connect : " + finishConnectResult);
}
if(key.isReadable()) {
int bytesRead = 0;
while((bytesRead = socketChannel.read(buf)) > 0) {
buf.flip();
while(buf.hasRemaining()) {
System.out.print((char)buf.get());
}
buf.clear();
}
if(bytesRead == -1) {
key.channel().close();
}
}
}
}
Thread.sleep(10);
}
} catch(IOException e) {
e.printStackTrace();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}.init(socketChannel, selector)).start();
while(true) {
while(scanner.hasNextLine()) {
buf.clear();
buf.put(scanner.nextLine().getBytes());
buf.flip();
socketChannel.write(buf);
buf.flip();
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
}
I have done the mistake setting this flag
key.interestOps(SelectionKey.OP_READ);
)
instead of below.
use this
socketChannel.register(this.selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
Before people suspect that I have no idea what I'm doing at all (and end up voting this down for no reason at all), please read this:
It connects to my server just fine! I'm getting no errors (from the client OR server), and my server is recognizing the connection. It works with my friend's client that he made, but I wanted to make my own client, and apparently I'm doing something wrong. PLEASE STAY ON TOPIC! Thanks :)
Title basically says it all. I've tested with println messages above and below the setupStream() in my Client.java run(), but only the message above the setupStream() prints. I'm not sure how I'm supposed to initialize my stream without making my program come to a halt.
Client.java
import java.io.IOException;
public class Client extends Stream implements Runnable {
public boolean running = false;
private Thread clientThread;
Frame frame;
public Client() {
super("localhost", 43594);
frame = new ClientFrame(500, 500);
start();
}
public synchronized void start() {
if(running) return;
running = true;
clientThread = new Thread(this);
clientThread.start();
}
public synchronized void stop() {
if(!running) return;
running = false;
clientThread.interrupt();
try {
clientThread.join();
} catch (InterruptedException e) {e.printStackTrace();}
}
public void run() {
try{
setupStream();
while(running) {
System.out.println("running");
}
}catch(IOException e) {
e.printStackTrace();
}finally {
try{
out.close();
in.close();
socket.close();
clientThread.join();
}catch(IOException | InterruptedException e) { e.printStackTrace(); }
}
}
public static void main(String[] args) {
new Client();
}
}
Stream.java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class Stream {
Socket socket;
ObjectOutputStream out;
ObjectInputStream in;
String data;
public Stream(String host, int port) {
try {
socket = new Socket(host, port);
} catch (IOException e) {
e.printStackTrace();
}
}
protected void setupStream() throws IOException {
out = new ObjectOutputStream(socket.getOutputStream());
out.flush();
in = new ObjectInputStream(socket.getInputStream());
}
}
My Server Thread:
package Server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
public class User extends Thread {
public static int users = 0;
public int ID;
public String username;
boolean online = false;
public static ArrayList<String> usernames = new ArrayList<String>();
Socket socket;
DataOutputStream out;
DataInputStream in;
String input;
public User(Socket socket) {
this.socket = socket;
}
public String decode(String input) {
String[] split = input.split(" ");
if(input.startsWith("::")) {
try {
switch(split[0].substring(2, split[0].length()).toLowerCase()) {
case "setname":
case "changename":
case "newname":
if(usernames.contains(split[1].toLowerCase())) {
out.writeUTF("This name is already taken! Please choose a different one.");
out.flush();
return null;
}
if(username == null) {
username = split[1].substring(0, 1).toUpperCase() + split[1].substring(1, split[1].length());
Server.users.put(split[1].toLowerCase(), Server.user[ID]);
usernames.add(split[1].toLowerCase());
} else {
usernames.remove(username.toLowerCase());
username = split[1].substring(0, 1).toUpperCase() + split[1].substring(1, split[1].length());
usernames.add(split[1].toLowerCase());
}
return null;
case "rank+":
return null;
case "[sm]=":
return null;
}
}catch(IOException e) { }
}
return input;
}
String timeStamp;
public void run() {
try {
out = new DataOutputStream(socket.getOutputStream());
out.flush();
in = new DataInputStream(socket.getInputStream());
while((input = in.readUTF()) != null) {
input = decode(input);
if(input != null) {
if(username != null) {
timeStamp = new SimpleDateFormat("[h:mm:ss] ").format(Calendar.getInstance().getTime());
Server.sendGlobalMessage(timeStamp + username +": "+input);
} else {
timeStamp = new SimpleDateFormat("[h:mm:ss] ").format(Calendar.getInstance().getTime());
Server.sendGlobalMessage(timeStamp + "Guest "+ID+": "+input);
}
}
}
}catch(IOException e) { e.printStackTrace(); } finally {
try{
out.close();
in.close();
socket.close();
} catch(IOException e) { e.printStackTrace(); }
}
}
}
I haven't touched the code of my Server Thread for a while, since it has always worked up until I made my new client.
I suspect that your server does not create an ObjectOutputStream, so when the client constructs its ObjectInputStream, it blocks waiting for the object stream header, which never arrives.