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.
Related
This is the SocketServer code that generates a server thread
public class ProcessorCorresponder {
protected final static Logger logger = LogManager.getLogger( ProcessorCorresponder.class );
private static int port = Integer.parseInt(PropertiesLoader.getProperty("appserver.port") == null ? "666" : PropertiesLoader.getProperty("appserver.port"));
private static int maxConnections = Integer.parseInt(PropertiesLoader.getProperty("appserver.maxconnections") == null ? "666" : PropertiesLoader.getProperty("appserver.maxconnections"));
public static void main(String[] args) {
logger.info("Starting server .. "
+ "[port->" + port
+ ",databaseName->" + databaseName + "]");
try (ServerSocket listener = new ServerSocket();) {
listener.setReuseAddress(true);
listener.bind(new InetSocketAddress(port));
Socket server;
int i = 0;
while((i++ < maxConnections) || (maxConnections == 0)) {
server = listener.accept();
logger.debug(
"New Thread listening on " + server.getLocalAddress().toString() + ":" + server.getLocalPort()
+ ", initiated from IP => " + server.getInetAddress().toString() + ":" + server.getPort()
);
MySocketServer socSrv = new MySocketServer (server);
Thread t = new Thread( socSrv );
t.start();
}
} catch (Exception ex) {
logger.error("Error in ProcessorInterface", ex);
}
}
}
Server code: This is a thread to handle one connection, there is a program that monitors a serversocket and spins off request threads as needed.
public class MySocketServer implements Runnable {
protected final static Logger logger = LogManager.getLogger(MySocketServer.class);
private final Socket server;
// because we are using threads, we must make this volatile, or the class will
// never exit.
private volatile boolean shouldContinue = true;
private StringBuffer buffHeartbeatMessage = new StringBuffer().append((char) 0).append((char) 0).append((char) 0)
.append((char) 0).append((char) 0).append((char) 0);
private Heartbeat heartbeat = new Heartbeat(/* 60 */3000, buffHeartbeatMessage.toString());
public MySocketServer(Socket server) {
this.server = server;
}
#Override
public void run() {
try (BufferedReader in = new BufferedReader(new InputStreamReader(this.server.getInputStream()));
BufferedOutputStream out = new HeartbeatBufferedOutputStream(this.server.getOutputStream(),
heartbeat)) {
final StreamListener listener = new StreamListener(in);
listener.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if (event.getID() == ActionEvent.ACTION_PERFORMED) {
if (event.getActionCommand().equals(StreamListener.ERROR)) {
logger.error("Problem listening to stream.");
listener.setShouldContinue(false);
stopRunning();
} else {
String messageIn = event.getActionCommand();
if (messageIn == null) { // End of Stream;
stopRunning();
} else { // hey, we can do what we were meant for
logger.debug("Request received from client");
// doing stuff here
...
// done doing stuff
logger.debug("Sending Client Response");
try {
sendResponse(opResponse, out);
} catch (Exception ex) {
logger.error("Error sending response to OP.", ex);
}
}
}
}
}
});
listener.start();
while (shouldContinue) {
// loop here until shouldContinue = false;
// this should be set to false in the ActionListener above
}
heartbeat.setShouldStop(true);
return;
} catch (Exception ex) {
logger.error("Error in ESPSocketServer", ex);
return;
}
}
private void stopRunning() {
shouldContinue = false;
}
private void sendResponse(ClientResponse opResponse, BufferedOutputStream out) throws Exception {
logger.debug("Before write");
out.write(opResponse.getResponse().getBytes());
logger.debug("After write. Before flush");
out.flush();
logger.debug("After flush");
// this log message is in my logs, so I know the message was sent
}
}
My StreamListener class.
public class StreamListener extends Thread {
protected final static Logger logger = LogManager.getLogger(StreamListener.class);
public final static String ERROR = "ERROR";
private BufferedReader reader = null;
private List<ActionListener> actionListeners = new ArrayList<>();
private boolean shouldContinue = true;
public StreamListener(BufferedReader reader) {
this.reader = reader;
}
#Override
public void run() {
while (shouldContinue) {
String message;
try {
// client blocks here and never receives message
message = reader.readLine();
ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, message);
fireActionPerformed(event);
} catch (IOException e) {
e.printStackTrace();
ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ERROR);
fireActionPerformed(event);
}
}
}
public void setShouldContinue(boolean shouldContinue) {
this.shouldContinue = shouldContinue;
}
public boolean getShouldContinue() {
return shouldContinue;
}
public boolean addActionListener(ActionListener listener) {
return actionListeners.add(listener);
}
public boolean removeActionListener(ActionListener listener) {
return actionListeners.remove(listener);
}
private void fireActionPerformed(ActionEvent event) {
for (ActionListener listener : actionListeners) {
listener.actionPerformed(event);
}
}
}
My Heartbeat class
public class Heartbeat extends Thread {
private BufferedOutputStream bos = null;
private int beatDelayMS = 0;
private String message = null;
private boolean shouldStop = false;
public Heartbeat(int beatDelayMS, String message) {
this.beatDelayMS = beatDelayMS;
this.message = message;
setDaemon(true);
}
#Override
public void run() {
if (bos == null) { return; }
while(!shouldStop) {
try {
sleep(beatDelayMS);
try {
bos.write(message.getBytes());
bos.flush();
} catch (IOException ex) {
// fall thru
}
} catch (InterruptedException ex) {
if (shouldStop) {
return;
}
}
}
}
public void setBufferedOutputStream(BufferedOutputStream bos) {
this.bos = bos;
}
public BufferedOutputStream getBufferedOutputStream() {
return bos;
}
public void setShouldStop(boolean shouldStop) {
this.shouldStop = shouldStop;
}
public boolean getShouldStop() {
return shouldStop;
}
}
My HeartbeatBufferedOutputStream
public class HeartbeatBufferedOutputStream extends BufferedOutputStream {
private Heartbeat heartbeat = null;
public HeartbeatBufferedOutputStream(OutputStream out, Heartbeat heartbeat) {
super(out);
this.heartbeat = heartbeat;
this.heartbeat.setBufferedOutputStream(this);
heartbeat.start();
}
#Override
public synchronized void flush() throws IOException {
super.flush();
heartbeat.interrupt();
}
}
And finally here is the "Client" class
public class Mockup extends Thread {
protected final static Logger logger = LogManager.getLogger(Mockup.class);
// because we are using threads, we must make this volatile, or the class will
// never exit.
private volatile boolean shouldContinue = true;
public static void main(String[] args) {
new Mockup().start();
}
#Override
public void run() {
try (Socket socket = new Socket("localhost", 16100);
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));) {
final StreamListener listener = new StreamListener(in);
listener.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
if (event.getID() == ActionEvent.ACTION_PERFORMED) {
if (event.getActionCommand().equals(StreamListener.ERROR)) {
logger.error("Problem listening to stream.");
listener.setShouldContinue(false);
stopRunning();
} else {
String messageIn = event.getActionCommand();
if (messageIn == null) { // End of Stream;
stopRunning();
} else { // hey, we can do what we were meant for
// convert the messageIn to an OrderPower request, this parses the information
logger.info("Received message from server. [" + messageIn + "].");
}
}
}
}
});
listener.start();
StringBuffer buff = new StringBuffer("Some message to send to server");
logger.info("Sending message to server [" + buff.toString() + "]");
out.write(buff.toString().getBytes());
out.flush();
boolean started = false;
while (shouldContinue) {
if (!started) {
logger.debug("In loop");
started = true;
}
// loop here until shouldContinue = false;
// this should be set to false in the ActionListener above
}
logger.info("Exiting Mockup");
return;
} catch (Exception ex) {
logger.error("Error running MockupRunner", ex);
}
}
private void stopRunning() {
shouldContinue = false;
}
}
I have confirmed from logging messages that the Server sends a message to the BufferedOutputStream, and is flushed, but the Client logs indicate that it is blocked on the reader.readLine() and never gets the message.
You are reading lines but you are never writing lines. Add a line terminator to what you send.
I have to build a JAVA Nio Server Application in JBoss to read data from a 10-200 Sensor Boxes. They open a stream and send data to me all the time. The comunication is bidirectional. Now, sometimes it can happen, that these Boxes (or the server) have some internal error. To detect this kind of problems, an observer thread checks every 5 seconds, if a data block came in since the last check. If none of my Boxes sent data till then, something bad happened and I want to restart the whole socket comunication.
Now, it is well documentated how to build up a socket connection with NIO, but it is harder to find complexe examples how to clean reset them. And here is my problem: when my watchdog detects that no data came in the last 5s, it calls close() and then startEngine(). But after that, still no data arrive. Something seems blocked, some ressource still associated or like that. If I restart my JBoss, data arrive again. Can somebody give me a hint?
thank you for your time!
Stefan
public class TestServer
{
private NIOServer server;
private HashMap<String, SocketChannel> clientsList = new HashMap<String, SocketChannel>();
class NIOServer extends Thread
{
class MessageBuffer
{
int [] msgAsByte = new int[msgSize];
int pos = 0;
int lastSign = 0;
int bytesRead = 0;
}
private ByteBuffer readBuffer = ByteBuffer.allocate(256);
private Selector selector;
private boolean stop = false;
private int[] ports;
private int msgSize = 48;
private HashMap<String,MessageBuffer> buffer = new HashMap<String, MessageBuffer>();
private List<ServerSocketChannel> channels;
// Maps a SocketChannel to a list of ByteBuffer instances
private Map<SocketChannel, List<ByteBuffer>> pendingDataToWrite = new HashMap<SocketChannel, List<ByteBuffer>>();
public NIOServer(int[] ports) {
this.ports = ports;
}
private void stopAll()
{
stop = true;
try
{
server.interrupt();
server.join(3000);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
closeConnections();
}
public void sendData(SocketChannel socket, byte[] data)
{
// And queue the data we want written
synchronized (this.pendingDataToWrite) {
List<ByteBuffer> queue = (List<ByteBuffer>) this.pendingDataToWrite.get(socket);
if (queue == null) {
queue = new ArrayList<ByteBuffer>();
this.pendingDataToWrite.put(socket, queue);
}
queue.add(ByteBuffer.wrap(data));
}
SelectionKey key = socket.keyFor(this.selector);
if(key != null)
key.interestOps(SelectionKey.OP_WRITE);
// Finally, wake up our selecting thread so it can make the required changes
this.selector.wakeup();
}
public void run()
{
try
{
stop = false;
selector = Selector.open();
channels = new ArrayList<ServerSocketChannel>();
ServerSocketChannel serverchannel;
for (int port : ports)
{
try
{
serverchannel = ServerSocketChannel.open();
serverchannel.configureBlocking(false);
try
{
serverchannel.socket().setReuseAddress(true);
}
catch(SocketException se)
{
//
}
serverchannel.socket().bind(new InetSocketAddress(port));
serverchannel.register(selector, SelectionKey.OP_ACCEPT);
channels.add(serverchannel);
}
catch(Exception e)
{
//
}
}
while (!stop)
{
SelectionKey key = null;
try
{
selector.select();
Iterator<SelectionKey> keysIterator = selector.selectedKeys()
.iterator();
while (keysIterator.hasNext())
{
key = keysIterator.next();
if(key.isValid())
{
if (key.isAcceptable())
{
accept(key);
}
else if (key.isReadable())
{
readData(key);
}
else if (key.isWritable())
{
writeData(key);
}
}
else
{
SocketChannel sc = (SocketChannel) key.channel();
}
keysIterator.remove();
}
}
catch ( Exception e)
{
if(e instanceof IOException || e instanceof ClosedSelectorException)
{
try
{
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
channels.remove(ssc);
ssc.close();
key.cancel();
}
catch(Exception ex)
{
//
}
}
else
{
//
}
}
}
}
catch(Exception e1)
{
//
}
closeConnections();
}
private void closeConnections()
{
//if thread is stopped, close all
try
{
try
{
if(this.selector == null || this.selector.keys() == null)
{
log.debug("No selectors or keys found to close");
}
else
{
Iterator<SelectionKey> keys = this.selector.keys().iterator();
while(keys.hasNext())
{
SelectionKey key = keys.next();
key.cancel();
}
}
}
catch(Exception ex) {
//
}
if(selector != null)
selector.close();
if(channels != null)
{
for(ServerSocketChannel channel:channels)
{
channel.socket().close();
channel.close();
}
}
if(clientsList != null)
{
Iterator<Map.Entry<String, SocketChannel>> hfm = clientsList.entrySet().iterator();
while(hfm.hasNext())
{
Map.Entry<String, SocketChannel> s = hfm.next();
s.getValue().close();
}
}
clientsList=null;
selector = null;
channels = null;
pendingDataToWrite = null;
}
catch(Exception e)
{
//
}
}
private void accept(SelectionKey key) throws IOException
{
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
String ip = sc.socket().getRemoteSocketAddress().toString();
if(!buffer.containsKey(ip))
buffer.put(ip, new MessageBuffer());
}
private void readData(SelectionKey key) throws Exception
{
SocketChannel sc = (SocketChannel) key.channel();
MessageBuffer buf = buffer.get(sc.socket().getRemoteSocketAddress().toString());
try
{
buf.bytesRead = sc.read(readBuffer); //read into buffer.
}
catch(Exception e2)
{
sc.close();
buffer.remove(sc);
}
//close connection
if (buf.bytesRead == -1)
{
sc.close();
key.cancel();
return;
}
readBuffer.flip(); //make buffer ready for read
while(readBuffer.hasRemaining())
{
//Read the data and forward it to another Process...
}
readBuffer.compact(); //make buffer ready for writing
}
private void writeData(SelectionKey key) throws Exception
{
SocketChannel socketChannel = (SocketChannel) key.channel();
synchronized (this.pendingDataToWrite) {
List queue = (List) this.pendingDataToWrite.get(socketChannel);
// Write until there's not more data ...
while (!queue.isEmpty()) {
ByteBuffer buf = (ByteBuffer) queue.get(0);
try
{
socketChannel.write(buf);
}
catch(Exception e)
{
//
}
finally
{
queue.remove(0);
}
if (buf.remaining() > 0) {
// ... or the socket's buffer fills up
break;
}
}
key.interestOps(SelectionKey.OP_READ);
}
}
}
public void close() {
if (server != null && server.isAlive())
{
server.stopAll();
}
if(clientsList != null)
{
clientsList.clear();
}
server = null;
}
public void startEngine(int[] ports) {
if (ports != null) {
for (int port : ports)
log.info("Listening on port " + port);
server= new NIOServer(ports);
server.start();
}
}
}
Use a select() timeout.
If the timeout happens, close all the registered SocketChannels.
If you want to get more fine-grained, keep track of the last I/O time on each channel, and close those that have expired at the bottom of each select() loop.
NB Your technique for OP_WRITE is not correct. There are many answers here showing how to use it properly.
I'm new in NIO and i need to create simple non-blocking client with next api:
void start();
void send(String msg);
void stop();
Start method should create connection for specified host and port.
stop method should stop the client and release connection.
send should send messages to server.
So I have read documentations and I created simple client:
public class NonBlockingNIOClient {
private DatagramChannel channel;
public final static int MAX_PACKET_SIZE = 65507;
private static final Logger LOGGER = LoggerFactory.getLogger(NonBlockingNIOStatsDClient.class);
public NonBlockingNIOStatsDClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() {
try {
channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress(getHost(), getPort()));
while (!channel.isConnected()) {
LOGGER.debug("still connecting");
}
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while(channel.isConnected()) {
}
}
});
thread.start();
} catch(IOException e) {
throw new ClientException("Failed to start client", e);
}
}
public void stop() {
try {
channel.disconnect();
} catch(IOException e) {
throw new StatsDClientException("Failed to stop client", e);
}
}
#Override
public void send(String msg) {
LOGGER.debug("send: {}", msg);
Validate.notBlank(msg, "message to sand cannot be blank");
ByteBuffer buf = ByteBuffer.allocate(MAX_PACKET_SIZE);
buf.clear();
buf.put(msg.getBytes());
buf.flip();
try {
channel.write(buf);
} catch(IOException e) {
getErrorHandler().handle(e);
}
}
}
How I understand from docs that channel.configureBlocking(false); does not guarantee that write method from channel will work in non-blocking mode. I guess I need to use selectors to achieve non-blocking behavior. But when I was trying to do next:
Selector selector = null;
try {
selector = Selector.open();
channel.register(selector, SelectionKey.OP_WRITE);
while(channel.isConnected()){
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext())
{
SelectionKey key = iterator.next();
if(key.isWritable())
{
//do send
}
iterator.remove();
}
}
selector.close();
}
In this case client does not respond to send() method because client was blocked by while(channel.isConnected()). Do you have any suggestions how I can make start method available and at the same time use selectors.
I have a server app. Java NIO
I have Runnable class - EventHandler - that process incoming messages. If message == "Bye" -> EventHandler close related SocketServer and SelectorKey
I have one Runnable object - Acceptor - that is activated on OP_ACCEPT events. It creates new SocketChannel and new EventHandler to process messages from this channel
I have a problem.
First client connect. Send messages. Disconnect. Everything is ok
After first client disconnected Second client connect. Here problem begins - Acceptor object isn't invoked, therefore SocketChannel and EventHandler are not created for new client.
What is wrong in my code? SocketChannel closed improperly?
I changed the code to fix the errors that were noted in the comments. Now it works fine
Reactor. Class with the main loop
public class Reactor implements Runnable {
final Selector selector;
final ServerSocketChannel serverSocketChannel;
Reactor(int port) throws IOException {
//configure server socket channel
this.selector = Selector.open();
this.serverSocketChannel = ServerSocketChannel.open();
this.serverSocketChannel.socket().bind(new InetSocketAddress(port));
this.serverSocketChannel.configureBlocking(false);
//start acceptor
this.serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT, new Acceptor(this.serverSocketChannel, this.selector));
}
public void run() {
System.out.println("Server is listening to port: " + serverSocketChannel.socket().getLocalPort());
try {
while (!Thread.currentThread().isInterrupted()) {
if (this.selector.select() > 0) {
Set<SelectionKey> selected = this.selector.selectedKeys();
for (SelectionKey selectionKey : selected) {
dispatch(selectionKey);
}
selected.clear(); //clear set (thanks to EJP for comment)
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
void dispatch(SelectionKey k) {
Runnable r = (Runnable) (k.attachment());
if (r != null) {
r.run();
}
}
}
Acceptor
public class Acceptor implements Runnable {
final ServerSocketChannel serverSocketChannel;
final Selector selector;
public Acceptor(ServerSocketChannel serverSocketChannel, Selector selector) {
this.serverSocketChannel = serverSocketChannel;
this.selector = selector;
}
public void run() {
try {
SocketChannel socketChannel = this.serverSocketChannel.accept();
if (socketChannel != null) {
new EventHandler(this.selector, socketChannel);
System.out.println("Connection Accepted");
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
EventHandler
public class EventHandler implements Runnable {
EventHandler(Selector selector, SocketChannel socketChannel) throws IOException {
this.socketChannel = socketChannel;
socketChannel.configureBlocking(false);
this.selectionKey = this.socketChannel.register(selector, SelectionKey.OP_READ, this);
//selector.wakeup(); //we don't need to wake up selector (thanks to EJP for comment)
}
#Override
public void run() {
try {
if (this.state == EventHandlerStatus.READING) {
read();
} else if (this.state == EventHandlerStatus.SENDING) {
send();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* Reading client message
*
* #throws IOException
*/
void read() throws IOException {
int readCount = this.socketChannel.read(this.input);
//check whether the result is equal to -1, and close the connection if it is (thanks to EJP for comment)
if(readCount == -1){
this.socketChannel.close();
System.out.println("Stream is closed. Close connection.");
return;
}
if (readCount > 0) {
processMessage(readCount);
}
if(this.clientMessage.equalsIgnoreCase("Bye")){
this.socketChannel.close();
//this.selectionKey.cancel(); //we don't need to cancel selectionKey if socketChannel is just closed (thanks to EJP for comment)
System.out.println("Client said Bye. Close connection.");
return;
}
this.state = EventHandler.Status.SENDING;
this.selectionKey.interestOps(SelectionKey.OP_WRITE); //mark that we interested in writing
}
/**
* Processing of the read message.
*
* #param readCount Number of bytes to read
*/
synchronized void processMessage(int readCount) {
this.input.flip();
StringBuilder sb = new StringBuilder();
sb.append(new String(Arrays.copyOfRange(input.array(), 0, readCount))); // Assuming ASCII (bad assumption but simplifies the example)
this.clientMessage = sb.toString().trim();
this.input.clear();
System.out.println("Client said: " + this.clientMessage);
}
/**
* Sending response to client
*
* #throws IOException
*/
void send() throws IOException {
System.out.println("Answer to client: " + this.clientMessage);
this.socketChannel.write(ByteBuffer.wrap((this.clientMessage + "\n").getBytes()));
this.state = EventHandler.Status.READING;
this.selectionKey.interestOps(SelectionKey.OP_READ); //mark that we interested in reading
}
//----------------------------------------------------------------------------------------------------------------------
// Fields
//----------------------------------------------------------------------------------------------------------------------
final SocketChannel socketChannel;
final SelectionKey selectionKey;
ByteBuffer input = ByteBuffer.allocate(1024);
EventHandlerStatus state = EventHandler.Status.READING;
String clientMessage = "";
//----------------------------------------------------------------------------------------------------------------------
// Enum to mark current status of EventHandler
//----------------------------------------------------------------------------------------------------------------------
enum Status {
READING, SENDING
}
}
I am currently developing chat application working over Internet.currently my application working fine over LAN but not working over Internet.I have also used port forwarding.I have done setting in modem and forward the port to private IP address but still it's not working.I got the error that "server isn't found".Please suggest me what I have to do and tell,Am I done the correct setting in modem or not??
Below is my server code...
Server.java
import java.util.*;
import java.net.*;
import java.io.*;
class Server implements ChatConstants
{
private static Vector list;
private ServerSocket ssocket ;
private Service service;
private static Socket socket;
private boolean done=false;
private static Hashtable userTable = new Hashtable();
private static Hashtable _userList = new Hashtable();
private static Hashtable _conflist = new Hashtable();
public Server() throws UnknownHostException
{
System.out.println("Initializing...");
list=new Vector(BACKLOG);
try {
ssocket= new ServerSocket(SERVER_PORT,BACKLOG);
}
catch(Exception e) {
e.printStackTrace();
System.out.println("Inside constructor"+e);
}
start();
}
public void start() throws UnknownHostException
{
byte[] data;
int header;
Socket _socket = null;
String hostname = null;
System.out.println("Server successfully started at "
+InetAddress.getLocalHost().toString()
+" port "+SERVER_PORT);
while(!done) {
try
{
_socket=ssocket.accept();
if(_socket != null) {
synchronized(list) {
list.addElement(_socket);
}
DataInputStream dis=new DataInputStream(_socket.getInputStream());
data = new byte[MAX_MESSAGE_SIZE];
dis.read(data);
Message message = ((Message)ChatUtils.bytesToObject(data));
System.out.println("Joined client "
+message._username+" at "+message._host+"...");
synchronized(userTable) {
userTable.put(message._username,_socket);
}
addUser(message);
sendUserList(message);
writeToClients(message);
service = new Service(_socket,hostname,message._user);
}
}
catch(Exception e) {
e.printStackTrace();
System.out.println("Thread exception"+e);
try {
_socket.close();
}
catch(Exception ex) {
ex.printStackTrace();
System.out.println("ERROR CLOSING SOCKET");
}
}
}//END WHILE
}
private void addUser(Message message)
{
synchronized(_userList) {
_userList.put(message._user.toString(),message._user);
}
}
public static void updateUser(User user)
{
User myuser;
synchronized(_userList) {
_userList.put(user.toString(),user);
}
}
public static synchronized void writeToClients(Message message)
{
byte[] data;
DataOutputStream dos;
for(int count=0;count<list.size();count++) {
try {
dos=new
DataOutputStream(((Socket)list.elementAt(count)).getOutputStream());
data=ChatUtils.objectToBytes(message);
dos.write(data,0,data.length);
}
catch(Exception e) {
e.printStackTrace();
System.out.println("Output exception");
}
}//END FOR
}
public static void writeToClient(Message message)
{
Socket socket;
byte[] data;
DataOutputStream dos;
synchronized(userTable) {
try {
socket = (Socket)userTable.get(message._destination);
dos=new DataOutputStream(socket.getOutputStream());
data=ChatUtils.objectToBytes(message);
dos.write(data,0,data.length);
}
catch(Exception e) {
e.printStackTrace();
System.out.println("SEND EXCEPTION"+e);
}
}
}
public static void sendConferenceListToClient(Message message)
{
Socket socket;
byte[] data;
DataOutputStream dos;
synchronized(userTable) {
try {
Message mymessage= new Message(CONFERENCE_LIST);
Vector vector = (Vector)
_conflist.get(message._destination);
mymessage._username = message._username;
mymessage._destination = message._destination;
mymessage.userlist = vector;
socket = (Socket)userTable.get(message._username);
if(socket!=null) {
dos=new DataOutputStream(socket.getOutputStream());
data=ChatUtils.objectToBytes(mymessage);
dos.write(data,0,data.length);
}
}
catch(Exception e) {
e.printStackTrace();
System.out.println("CONFERENCE LIST EXCEPTION"+e);
}
}
}
public static void writeToPublicChat(Message message)
{
Socket socket;
byte[] data;
DataOutputStream dos;
synchronized(_conflist) {
try {
Vector svector = (Vector)_conflist.get(message._destination);
for(int cnt=0;cnt<svector.size();cnt++) {
synchronized(userTable) {
try {
socket = (Socket)userTable.get((svector.get(cnt).toString()));
if(socket!=null) {
dos=new DataOutputStream(socket.getOutputStream());
data=ChatUtils.objectToBytes(message);
dos.write(data,0,data.length);
}
}
catch(Exception e) {
e.printStackTrace();
System.out.println("PUBLIC CHAT EXCEPTION"+e);
}
}
}
} catch(Exception e){
e.printStackTrace();
System.out.println("PUBLIC EXCEPTION"+e);
}
}
}
public static void inviteToPublicChat(Vector svector,Message message)
{
Socket socket;
byte[] data;
DataOutputStream dos;
synchronized(_conflist) {
for(int cnt=0;cnt<svector.size();cnt++) {
synchronized(userTable) {
try {
socket = (Socket)userTable.get((svector.get(cnt).toString()));
if(socket != null) {
dos=new DataOutputStream(socket.getOutputStream());
data=ChatUtils.objectToBytes(message);
dos.write(data,0,data.length);
}
}
catch(Exception e) {
e.printStackTrace();
System.out.println("PUBLIC INVITE EXCEPTION"+e);
}
}
}
}
}
private void sendUserList(Message message)
{
int header=0;
String destination;
header=message._header;
destination = message._destination;
message._header = USERS_LIST;
message._destination = message._username;
message.userlist = new Vector(_userList.values());
writeToClient(message);
//Restore the headers
message._destination = destination;
message._header = header;
}
public static synchronized void removeUser(User user)
{
try {
Socket socket = (Socket)userTable.get(user.toString());
list.removeElement(socket);
_userList.remove(user.toString());
userTable.remove(user.toString());
}
catch(Exception e) {
e.printStackTrace();
System.out.println("ERROR REMOVING SOCKET "+e);
}
}
public static synchronized void processClientMessage(Message message)
{
switch(message._header) {
case CHANGE_STATUS:
updateUser(message._user);
writeToClients(message);
break;
case CLIENT_LOGOUT:
removeUser(message._user);
writeToClients(message);
break;
case CONFERENCE_CREATE:
Vector myvector = new Vector();
myvector.add(message._username);
_conflist.put(message._user.toString(),myvector);
case CONFERENCE_INVITE:
inviteToPublicChat(message.userlist,message);
break;
case CONFERENCE_JOIN:
Vector vector=null;
vector = (Vector)
_conflist.get(message._destination.toString());
vector.add(message._username);
_conflist.put(message._destination.toString(),vector);
writeToPublicChat(message);
break;
case CONFERENCE_DENY:
//_conflist.remove(message._user.toString(),message.userlist);
writeToPublicChat(message);
break;
case CONFERENCE_LEAVE:
Vector vectors =(Vector)
_conflist.get(message._destination.toString());
for(int count=0;count<vectors.size();count++) {
if(message._username.equals((vectors.elementAt(count).toString())))
vectors.remove(count);
}
if(vectors.size() != 0)
_conflist.put(message._user.toString(),vectors);
else//IF THERE ARE NO MORE USERS
_conflist.remove(message._user.toString());//DONE CONFERENCE
writeToPublicChat(message);
break;
case PUBLIC_CHAT:
writeToPublicChat(message);
break;
case CONFERENCE_LIST:
sendConferenceListToClient(message);
break;
default:
writeToClient(message);
}
}
public static void main(String args[]) throws Exception
{
Server chatserver=new Server();
}
}
//
// Service: Service class for each clients connected to server.
//
class Service implements Runnable, ChatConstants
{
private DataInputStream dis;
private Socket socket;
private boolean done=false;
private Thread thread;
private String hostname;
private User user;
public Service(Socket _socket,String _hostname,User user)
{
try {
this.socket = _socket;
this.hostname=_hostname;
this.user = user;
dis=new DataInputStream(socket.getInputStream());
thread=new Thread(this,"SERVICE");
thread.start();
}
catch(Exception e){
e.printStackTrace();
System.out.println("service constructor"+e);
}
}
public void run()
{
byte[] data;
while(!done)
{
try {
data = new byte[MAX_MESSAGE_SIZE];
dis.read(data);
Message message = ((Message)ChatUtils.bytesToObject(data));
Server.processClientMessage(message);
}
catch(Exception e) {
e.printStackTrace();
done = true;
Server.removeUser(user);
Message message = new Message(CLIENT_LOGOUT);
user.isOnline = OFFLINE;
message._user = user;
Server.writeToClients(message);
try {
socket.close();
} catch(Exception se) {
se.printStackTrace();
System.out.println("ERROR CLOSING SOCKET "+se);
}
//System.out.println("SERVICE THREAD EXCEPTION"+e);
}
}//END WHILE
}
}
Thanks in advance.
I think the
ssocket= new ServerSocket(SERVER_PORT,BACKLOG);
is making the issue. Use the version
ssocket= new ServerSocket(SERVER_PORT,BACKLOG,LOCAL_INET_ADDRESS);
and bind server to some constant local IP. Now use the port forwarding in the modem, to forward all requests to that local ip. Make sure that the firewall is not preventing you to use the port. Since firewall may allow a local networking but not to web.