I have created a simple server client application using java NIO.
I used a single selector there for accepting connection, reading data and writing.
But I want an application where 1 selector will be busy in accepting the connection while the 2nd selector will read the data and the 3rd selector will write the data.
Means I donot want to put all the load into single selector.
How to achieve this?
Is there any online help
Thanks
Deepak.
// Create the selector
Selector selector = Selector.open();
// Create two non-blocking server sockets on 80 and 81
ServerSocketChannel ssChannel1 = ServerSocketChannel.open();
ssChannel1.configureBlocking(false);
ssChannel1.socket().bind(new InetSocketAddress(80));
// Register both channels with selector
ssChannel1.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// Wait for an event
selector.select();
// Get list of selection keys with pending events
Iterator it = selector.selectedKeys().iterator();
// Process each key
while (it.hasNext()) {
// Get the selection key
SelectionKey selKey = (SelectionKey)it.next();
// Remove it from the list to indicate that it is being processed
it.remove();
// Check if it's a connection request
if (selKey.isAcceptable()) {
// Get channel with connection request
ServerSocketChannel ssChannel = (ServerSocketChannel)selKey.channel();
// Accepting a Connection on a ServerSocketChannel
SocketChannel sChannel = serverSocketChannel.accept();
// If serverSocketChannel is non-blocking, sChannel may be null
if (sChannel == null) {
// There were no pending connection requests; try again later.
// To be notified of connection requests,
} else {
// Use the socket channel to communicate with the client
}
}
}
}
Usually, on a non-blocking tcp server, first accept, then read, then write,
you need to register the selector in this order to make sense.
Example code
Here is a full example of non-blocking io:
TcpChannelTest.java: (a TestNG testing class)
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.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;
import org.testng.annotations.Test;
/**
* tcp channel test
*
* #author eric
* #date Sep 2, 2012 9:17:40 PM
*/
public class TcpChannelTest {
public String serverHost = "localhost";
public int serverPort = 12345;
private ServerSocketChannel server;
private int clientSerial = 0;
private int clientCount = 5;
// test tcp non-blocking channel,
#Test
public void testTcpNonBlockingChanne() throws IOException, InterruptedException {
// start server
startServerNonBlocking();
Thread.sleep(500); // wait server to be ready, before start client,
// start clients
for (int i = 0; i < clientCount; i++) {
startClientOnce();
}
// shutdown server,
Thread.sleep(500); // wait client to be handled,
shutdownServer();
}
// start non-blocking server,
private void startServerNonBlocking() throws IOException {
new Thread(new Runnable() {
#Override
public void run() {
try {
server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(serverHost, serverPort)); // bind,
server.configureBlocking(false); // non-blocking mode,
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set<SelectionKey> readyKeys = selector.selectedKeys();
// process each ready key...
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel client = server.accept();
System.out.printf("[%s]:\t%s\n", Thread.currentThread().getName(), "accept connection");
client.configureBlocking(false);
// prepare for read,
client.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
// read
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer inBuf = ByteBuffer.allocate(1024);
while (client.read(inBuf) > 0) {
System.out.printf("[%s]:\t%s\n", Thread.currentThread().getName(), new String(inBuf.array(), StandardCharsets.UTF_8));
}
// prepare for write,
client.register(selector, SelectionKey.OP_WRITE);
} else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
String response = "hi - from non-blocking server";
byte[] bs = response.getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = ByteBuffer.wrap(bs);
client.write(buffer);
// switch to read, and disable write,
client.register(selector, SelectionKey.OP_READ);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}, "t-server-threads").start();
}
// close server,
private void shutdownServer() {
try {
if (server != null) {
server.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* <p>
* tcp client - via channel,
* </p>
* <p>
* It send once request.
* </p>
*
* #throws IOException
*/
private void startClientOnce() throws IOException {
// start client in a new thread
new Thread(new Runnable() {
#Override
public void run() {
try {
SocketChannel client = SocketChannel.open(new InetSocketAddress(serverHost, serverPort));
// write
String request = "hello - from client [" + Thread.currentThread().getName() + "}";
byte[] bs = request.getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = ByteBuffer.wrap(bs);
while (buffer.hasRemaining()) {
client.write(buffer);
}
// read
ByteBuffer inBuf = ByteBuffer.allocate(1024);
while (client.read(inBuf) > 0) {
System.out.printf("[%s]:\t%s\n", Thread.currentThread().getName(), new String(inBuf.array(), StandardCharsets.UTF_8));
}
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}, "t-channelClient-" + clientSerial++).start();
}
}
It is possible to register a channel with multiple Selectors using register(Selector sel, int ops). You then register different interest ops on each of the selectors:
// After the accepting a connection:
SelectionKey readKey = sChannel.register(readSelector, SelectionKey.OP_READ);
// When you have something to write:
SelectionKey writeKey = sChannel.register(writeSelector, SelectionKey.OP_WRITE);
Related
I'm trying java nio's socket server channel and selector as below:
try { // server side main function
ServerSocketChannel listenChannel = ServerSocketChannel.open();
listenChannel.socket().bind(new InetSocketAddress(12112));
Selector selector = Selector.open();
listenChannel.configureBlocking(false);
listenChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
if (selector.select(TIMEOUT) == 0) {
System.out.print(".");
continue;
}
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) {
SocketChannel channel = listenChannel.accept();
channel.configureBlocking(false);
SelectionKey connKey = channel.register(selector, SelectionKey.OP_ACCEPT);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
Then a simple client like:
try {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 12112));
ByteBuffer writeBuffer = ByteBuffer.allocate(32);
ByteBuffer readBuffer = ByteBuffer.allocate(32);
writeBuffer.put("hello".getBytes());
writeBuffer.flip();
while (true) {
writeBuffer.rewind();
socketChannel.write(writeBuffer);
readBuffer.clear();
socketChannel.read(readBuffer);
}
} catch (IOException e) {
}
First I start the server and then client, the server will be connect and throw exception like below:
..................java.lang.IllegalArgumentException
at java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:199)
at java.nio.channels.SelectableChannel.register(SelectableChannel.java:280)
at NIOServer.main(myServer.java:32)
So what happened when client connects? Why register function throws out exception and how to fix it?
The SocketChannel has the valid option only for OP_READ,OP_WRITE,OP_CONNECT.
Please check SocketChannel.validOps()
If it fails in channel.register(selector, SelectionKey.OP_ACCEPT) then you have to change it to
channel.register(selector, SelectionKey.OP_READ);
Only ServerSocketChannel supports SelectionKey.OP_ACCEPT.
listenChannel.accept() create new channel which supports read and write operations.
I'm trying to do a simple chat in java using a multiplex server but I'm having a little problem: I can't seem to be able to iterate through the socket channel and relay a message to all clients connected to the server.
Here's the code: https://pastebin.com/ZaXzsRpA
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
public class Server
{
static private Selector selector;
// A pre-allocated buffer for the received data
static private final ByteBuffer buffer = ByteBuffer.allocate( 16384 );
// Decoder for incoming text -- assume UTF-8
static private final Charset charset = Charset.forName("UTF8");
static private final CharsetDecoder decoder = charset.newDecoder();
static public void main( String args[] ) throws Exception {
// Parse port from command line
int port = Integer.parseInt( args[0] );
try {
// Instead of creating a ServerSocket, create a ServerSocketChannel
ServerSocketChannel ssc = ServerSocketChannel.open();
// Set it to non-blocking, so we can use select
ssc.configureBlocking( false );
// Get the Socket connected to this channel, and bind it to the
// listening port
ServerSocket ss = ssc.socket();
InetSocketAddress isa = new InetSocketAddress( port );
ss.bind( isa );
// Create a new Selector for selecting
selector = Selector.open();
// Register the ServerSocketChannel, so we can listen for incoming
// connections
ssc.register( selector, SelectionKey.OP_ACCEPT );
System.out.println( "Listening on port "+port );
while (true) {
// See if we've had any activity -- either an incoming connection,
// or incoming data on an existing connection
int num = selector.select();
// If we don't have any activity, loop around and wait again
if (num == 0) {
continue;
}
// Get the keys corresponding to the activity that has been
// detected, and process them one by one
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
// Get a key representing one of bits of I/O activity
SelectionKey key = it.next();
// What kind of activity is it?
if ((key.readyOps() & SelectionKey.OP_ACCEPT) ==
SelectionKey.OP_ACCEPT) {
// It's an incoming connection. Register this socket with
// the Selector so we can listen for input on it
Socket s = ss.accept();
System.out.println( "Got connection from "+s );
// Make sure to make it non-blocking, so we can use a selector
// on it.
SocketChannel sc = s.getChannel();
sc.configureBlocking( false );
// Register it with the selector, for reading
sc.register( selector, SelectionKey.OP_READ );
} else if ((key.readyOps() & SelectionKey.OP_READ) ==
SelectionKey.OP_READ) {
SocketChannel sc = null;
try {
// It's incoming data on a connection -- process it
sc = (SocketChannel)key.channel();
boolean ok = processInput( sc );
// If the connection is dead, remove it from the selector
// and close it
if (!ok) {
key.cancel();
Socket s = null;
try {
s = sc.socket();
System.out.println( "Closing connection to "+s );
s.close();
} catch( IOException ie ) {
System.err.println( "Error closing socket "+s+": "+ie );
}
}
} catch( IOException ie ) {
// On exception, remove this channel from the selector
key.cancel();
try {
sc.close();
} catch( IOException ie2 ) { System.out.println( ie2 ); }
System.out.println( "Closed "+sc );
}
}
}
// We remove the selected keys, because we've dealt with them.
keys.clear();
}
} catch( IOException ie ) {
System.err.println( ie );
}
}
// Just read the message from the socket and send it to stdout
static private boolean processInput( SocketChannel sc ) throws IOException {
// Read the message to the buffer
buffer.clear();
sc.read( buffer );
buffer.flip();
// If no data, close the connection
if (buffer.limit()==0) {
return false;
}
// Decode and print the message to stdout
String message = decoder.decode(buffer).toString();
System.out.println("RECEIVED: "+ message);
buffer.flip();
//InetSocketAddress isa = new InetSocketAddress( port );
//ss.bind( isa );
// Create a new Selector for selecting
//selector = Selector.open();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
System.out.println("One for each connect");
// Get a key representing one of bits of I/O activity
SelectionKey key = it.next();
if(key.isAcceptable())
continue;
SocketChannel scAux = (SocketChannel)key.channel();
scAux.write(buffer);
buffer.rewind();
}
buffer.clear();
return true;
}
}
Anyone able to give me some feedback?
friends! I'm new to Java NIO and I'm currently trying to make a non-blocking chat app. The client connects to the server without problem. The client writes a message or few messages to the server but the server starts reading the messages only when the Socket connection is closed from the client code, so a SocketChannel (or only Socket) must be created and closed in the client code for every message - this doesn't seems to me right. I've tried the client side with simple Java I/O and also with NIO Selector. Same problem - the server starts to read only when the SocketChannel or the Socket is closed from client. Can somebody please tell me the proper way of doing such non blocking connections or show me the error in my logic... Thank You very much!
This is the server code:
public class NIOServer implements Runnable {
#Override
public void run() {
try {
runServer();
} catch (IOException e) {
e.printStackTrace();
}
}
private void runServer() throws IOException {
ServerSocketChannel server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(8080));
server.configureBlocking(false);
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
while(true) {
int readyChannels = selector.selectNow();
if(readyChannels==0){
continue;
}
System.out.println("Ready channels: "+readyChannels);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
if(key.isAcceptable()){
ServerSocketChannel acceptableServer = (ServerSocketChannel)key.channel();
SocketChannel client = server.accept();
if(client!=null){
System.out.println("Client accepted!");
client.configureBlocking(false);
SelectionKey selectionKey = client.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
}
}
if (key.isReadable()) {
read(key);
}
/*if(key.isConnectable()){
System.out.println("connectable");
}
if(key.isWritable()){
//System.out.println("writable");
}*/
}
}
}
public void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel)key.channel();
channel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(100);
buffer.clear();
int bytesRead = channel.read(buffer);
while(bytesRead>0){
System.out.println("Read bytes: "+ bytesRead);
bytesRead=channel.read(buffer);
if(bytesRead==-1){
channel.close();
key.cancel();
}
buffer.flip();
while(buffer.hasRemaining()){
System.out.print((char)buffer.get());
}
}
//key.cancel();
//channel.close();
}
}
Client with NIO Selector:
public class NIOSelectorClient implements Runnable{
private Selector selector;
#Override
public void run() {
try {
startClient();
} catch (IOException e) {
e.printStackTrace();
}
}
public void startClient() throws IOException {
SocketChannel socketChannel= openConnection();
selector = Selector.open();
socketChannel.register(selector,SelectionKey.OP_CONNECT|SelectionKey.OP_READ|SelectionKey.OP_WRITE);
while(!Thread.interrupted()) {
int readyChannels = selector.selectNow();
if(readyChannels==0) {
continue;
}
Set<SelectionKey> keySet = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = keySet.iterator();
while(keyIterator.hasNext()) {
SelectionKey currentKey = keyIterator.next();
keyIterator.remove();
if(!currentKey.isValid()) {
continue;
}
if(currentKey.isConnectable()) {
System.out.println("I'm connected to the server!");
handleConnectable(currentKey);
}
if(currentKey.isWritable()){
handleWritable(currentKey);
}
}
}
}
private void handleWritable(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(100);
Scanner scanner = new Scanner(System.in);
System.out.println("Enter message to server: ");
String output = scanner.nextLine();
buffer.put(output.getBytes());
buffer.flip();
//while(buffer.hasRemaining()) {
channel.write(buffer);
//}
System.out.println("Message send");
buffer.clear();
channel.close();
key.cancel();
}
private void handleConnectable(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if(channel.isConnectionPending()) {
channel.finishConnect();
}
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
}
private static SocketChannel openConnection() throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
socketChannel.configureBlocking(false);
while(!socketChannel.finishConnect()) {
System.out.println("waiting connection....");
}
return socketChannel;
}
}
And this is the non-NIO cliet:
public class NIOClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8080);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while(socket.isConnected()) {
//synchronized (socket) {
writeMessage(socket,writer);
//readServerMessage(socket);
//}
}
}
public static void writeMessage(Socket socket, BufferedWriter writer) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter message: ");
String output = "Client 1: " + scanner.nextLine();
writer.write(output);
writer.flush();
//writer.close();
}
public static void readServerMessage(Socket socket) throws IOException {
}
}
Your code suffers from the usual raft of NIO mistakes:
public class NIOServer implements Runnable {
private void runServer() throws IOException {
ServerSocketChannel server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(8080));
server.configureBlocking(false);
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
while(true) {
int readyChannels = selector.selectNow();
You are selecting without a sleep. If there are no ready channels this loop will smoke the CPU. Use a timeout, even a short one.
SelectionKey selectionKey = client.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
You should not register for OP_WRITE unless you've already written something and got a short return value.
public void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel)key.channel();
channel.configureBlocking(false);
The channel is already in non-blocking mode. You put it there when you accepted it. You couldn't have selected on it unless it was in non-blocking mode. Remove.
ByteBuffer buffer = ByteBuffer.allocate(100);
buffer.clear();
The buffer is already clear. You just created it. Remove.
int bytesRead = channel.read(buffer);
while(bytesRead>0){
System.out.println("Read bytes: "+ bytesRead);
bytesRead=channel.read(buffer);
if(bytesRead==-1){
channel.close();
key.cancel();
Closing the channel cancels the key. You don't need both. Remove the cancel.
//key.cancel();
//channel.close();
Remove. Don't leave dead code lying around to confuse future readers.
Client with NIO Selector:
public class NIOSelectorClient implements Runnable{
private Selector selector;
public void startClient() throws IOException {
SocketChannel socketChannel= openConnection();
selector = Selector.open();
socketChannel.register(selector,SelectionKey.OP_CONNECT|SelectionKey.OP_READ|SelectionKey.OP_WRITE);
See above.
while(!Thread.interrupted()) {
int readyChannels = selector.selectNow();
See above.
if(!currentKey.isValid()) {
continue;
}
Very good but you need this test before every other one below, e.g. currentKey.isValid() && currentKey.isReadable(), because a prior handler may have closed the channel or cancelled the key. Same applies in the server code.
if(currentKey.isConnectable()) {
System.out.println("I'm connected to the server!");
handleConnectable(currentKey);
}
if(currentKey.isWritable()){
handleWritable(currentKey);
}
You never handle isReadable() in the client. Don't you expect any input?
private void handleWritable(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(100);
Scanner scanner = new Scanner(System.in);
System.out.println("Enter message to server: ");
String output = scanner.nextLine();
Here you are blocking the entire client including all its SocketChannels waiting for the user to enter some input. This is very poor design.
buffer.clear();
You don't need this. You're about to release the buffer as a local variable. You're done with it.
channel.close();
You're closing the channel after one write? Why?
key.cancel();
Closing the channel cancels the key. You don't need both. You don't need this. Remove.
private void handleConnectable(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if(channel.isConnectionPending()) {
channel.finishConnect();
finishConnect() can return false, in which case you should do nothing further in this method.
channel.configureBlocking(false);
The channel is already in blocking mode. Otherwise you couldn't have got here. Remove.
channel.register(selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ);
}
See above re OP_WRITE.
private static SocketChannel openConnection() throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
socketChannel.configureBlocking(false);
while(!socketChannel.finishConnect()) {
System.out.println("waiting connection....");
}
Remove this loop. That's what OP_CONNECT is for. You are keeping a dog and barking yourself. If you want not to proceed out of here until the connection is complete, do it in blocking mode. Instead of just smoking the CPU.
And this is the non-NIO cliet:
public class NIOClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8080);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while(socket.isConnected()) {
The socket is connected. You connected it when you constructed it. It stays that way. isConnected() is not a valid test for peer disconnection.
I know this question has been asked before, and I've tried the different solutions, but I got stuck in the implementation part.. :(
Currently multiple clients can connect to the server, I used the multithreaded KnockKnock server/client example from javadocs, and edited it slightly so that you can just send messages to the server, and it will echo them back to you, but I want to be able to make it so that if client 1 sends a message, then the server will broadcast them back to all the clients connected to the server.
I've tried looking around and saw people in the same position as I am in now, and they were told to make a list to keep track of all the connections, and then iterate through the list and send the message, but I really don't know in which class to put it or how to handle it.
If someone could show me or just give me hints to where I should start, it would be greatly appreciated, as I'm really just stuck at the moment :(
Here's where I'm at so far:
Server:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
public static void main(String[] args) throws IOException {
boolean listening = true;
try (ServerSocket serverSocket = new ServerSocket(4444)) {
while (listening) {
ServerThread thread = new ServerThread(serverSocket.accept());
thread.start();
}
} catch (IOException e) {
System.err.println("Could not listen on port " );
System.exit(-1);
}
}
}
ServerThread
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class ServerThread extends Thread{
private Socket socket = null;
public ServerThread(Socket socket) {
super("MultiServerThread");
this.socket = socket;
}
public void run() {
try (
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
) {
while (true) {
String input = in.readLine();
System.out.println(input);
out.println("ecco " + input);
if (input.equals("Bye"))
break;
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client (not sure if necessary, but here is it anyways)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public static void main(String[] args) throws IOException {
try (
Socket kkSocket = new Socket("172.30.242.51", 4444);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));
) {
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));
while (true) {
if(in != null) {
String input = stdIn.readLine();
out.println("Client: " + input);
System.out.println(in.readLine());
out.flush();
}
}
} catch (UnknownHostException e) {
System.err.println("Don't know about host " );
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to " );
System.exit(1);
}
}
}
Have a nice weekend =)
Operation 'write' is blocking in your example. So iterating by all connections can lead to delays and blocking your push thread. Also always set SO_TIMEOUT for socket if you do not want to have memory leaks.
I suggest using netty server
It has very nice functionality for pushing data to all connected clients - look for ChannelGroup
Why don't you use NIO to solve this problem?
A simple example:
public class EchoServer {
public static void main(String[] args) throws Exception {
//Create TCP server channel
ServerSocketChannel serv = ServerSocketChannel.open();
ServerSocket sock = serv.socket();
//Create a socket on your IP and port (i.e: localhost:12345)
SocketAddress addr = new InetSocketAddress(12345);
//Bind server socket and socket address
sock.bind(addr);
//Configure socket so all its methods won't be blocking
serv.configureBlocking(false);
//Create a selector to attend all the incoming requests
Selector selector = Selector.open();
//Register into the selector the accept request type
serv.register(selector,SelectionKey.OP_ACCEPT);
//Create a common buffer
ByteBuffer commonBuffer = ByteBuffer.allocate(10000);
commonBuffer.clear();
Iterator<SelectionKey> it = null;
ByteBuffer channelBuffer = null;
for (;;){ //Infinite loop
System.out.println("Waiting for events......");
selector.select(); // This call do is blocking
System.out.println("New event received");
it = selector.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
System.out.println(String.format("Processing %s", key));
it.remove(); // Remove it to avoid duplications
try{
if (key.isAcceptable()) {
System.out.println("Received new connection request");
processConnectionRequest(serv, selector);
}else if (key.isReadable()) {
System.out.println("Received new reading request");
processReadingRequest(selector, commonBuffer, key);
}else if (key.isWritable()) {
System.out.println("Received new writing request");
processWritingRequest(key);
}
}catch(Exception e){
key.cancel();
try {
key.channel().close();
} catch (Exception ce) {}
}//end catch
}//end while
}//end for
}//end main
private static void processWritingRequest(SelectionKey key) throws IOException {
SocketChannel cli = (SocketChannel) key.channel();
ByteBuffer buf = (ByteBuffer) key.attachment();
System.out.println(String.format("Wrinting into the channel %s", cli));
buf.flip();//prepare the buffer
buf.rewind();
cli.write(buf);
if (buf.hasRemaining()) {
//If there is more content remaining, compact the buffer
buf.compact();
} else {
buf.clear();
key.interestOps(SelectionKey.OP_READ);
}
}
private static void processReadingRequest(Selector selector, ByteBuffer commonBuffer, SelectionKey key)
throws IOException {
SocketChannel cli = (SocketChannel) key.channel();
if (cli.read(commonBuffer) == -1) {
System.out.println(String.format("Closing channel %s", cli));
cli.close(); // internally calls key.cancel()
}
else {//Send the data to all the channels
commonBuffer.flip();//prepare the buffer
Iterator<SelectionKey> it2 = selector.keys().iterator();
System.out.println("Writing data to all the channels");
SelectionKey keys = null;
while(it2.hasNext()) {
keys = it2.next();
System.out.println(String.format("Writing in %s", keys));
ByteBuffer buf = (ByteBuffer) keys.attachment();
if(buf!=null)
{
buf.put(commonBuffer);
keys.interestOps(SelectionKey.OP_WRITE|SelectionKey.OP_READ);
commonBuffer.rewind();
}
}
commonBuffer.clear();
}
}
private static void processConnectionRequest(ServerSocketChannel serv, Selector selector)
throws IOException, ClosedChannelException {
ByteBuffer channelBuffer;
SocketChannel cli = serv.accept();
cli.configureBlocking(false);
channelBuffer = ByteBuffer.allocate(10000);
System.out.println(String.format("Registering new reading channel: %s", cli));
cli.register(selector, SelectionKey.OP_READ, channelBuffer);
}
}
I am reading following tutorial:
http://tutorials.jenkov.com/java-nio/selectors.html
I am feeling that I am didn't understand a lot of details of following article but I am trying to invoke example at the end of the article.
my final code looks so:
public static void main(String[] args) throws IOException {
ServerSocketChannel channel = ServerSocketChannel.open();
Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_READ);
while (true) {
int readyChannels = selector.select();
if (readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();//cause of exception
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
} else if (key.isConnectable()) {
// a connection was established with a remote server.
} else if (key.isReadable()) {
// a channel is ready for reading
} else if (key.isWritable()) {
// a channel is ready for writing
}
keyIterator.remove();
}
}
}
As a result of this method invokation I see follwing stacktrace:
Exception in thread "main" java.lang.IllegalArgumentException
at java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:196)
at java.nio.channels.SelectableChannel.register(SelectableChannel.java:277)
...
update
my full code variant:
public class NioSelectorTest {
public static void main(String[] args) {
new Thread(new Receiver()).start();
new Thread(new Sender()).start();
}
}
class Receiver implements Runnable {
private static byte[] data = new byte[255];
public void run() {
try {
for (int i = 0; i < data.length; i++)
data[i] = (byte) i;
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(9000));
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Set readyKeys = selector.selectedKeys();
Iterator iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);
ByteBuffer source = ByteBuffer.wrap(data);
SelectionKey key2 = client.register(selector, SelectionKey.OP_WRITE);
key2.attach(source);
} else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
if (!output.hasRemaining()) {
output.rewind();
}
client.write(output);
}
key.channel().close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Sender implements Runnable {
public void run() {
try {
SocketChannel sChannel = SocketChannel.open();
sChannel.configureBlocking(false);
sChannel.connect(new InetSocketAddress("localhost", 9000));
while (!sChannel.finishConnect()) {
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
buf.put((byte) 0xFF);
buf.flip();
int numBytesWritten = sChannel.write(buf);
System.out.println("sent " + numBytesWritten + " bytes");
}
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
In console I see
Accepted connection from java.nio.channels.SocketChannel[connected
local=/127.0.0.1:9000 remote=/127.0.0.1:50940]
In debug I see that I cannot to enter into this loop:
while (!sChannel.finishConnect()) {
...
SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_READ);
This should be OP_ACCEPT for a ServerSocketChannel.
In Java documentation it's written that register method throws
IllegalArgumentException - If a bit in ops does not correspond to an operation that is supported by this channel, that is, if set & ~validOps() != 0
(Source: http://bit.ly/1lSBdX8)
I think you need to bind a specific port at first, and register selector on that SocketChannel. So the beginning of your code should look like this:
ServerSocketChannel channel = ServerSocketChannel.open();
channel.socket().bind(new InetSocketAddress(8080)); // 8080 is the port you'll be listening
SocketChannel socketChannel = serverSocketChannel.accept();
Selector selector = Selector.open();
socketChannel.configureBlocking(false);
SelectionKey selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);
...