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?
Related
I am trying to write a simple client/server in Java using NIO and Selectors. The server is very easy and it's the most typical implementation that you can find everywhere. Here's the code of the server (look at the start() method):
public final class MyServer {
private int port;
private String address;
public MyServer(String address, int port) {
this.address = address;
this.port = port;
}
public void start() throws IOException {
try {
Selector selector = Selector.open();
ServerSocketChannel socket = ServerSocketChannel.open();
InetSocketAddress addr = new InetSocketAddress(address, port);
socket.bind(addr);
socket.configureBlocking(false);
boolean isAlive = true;
SelectionKey selectKy = socket.register(selector, SelectionKey.OP_ACCEPT, null);
while (isAlive) {
selector.select();
Set<SelectionKey> keysList = selector.selectedKeys();
Iterator<SelectionKey> keys = keysList.iterator();
while (keys.hasNext()) {
SelectionKey theKey = keys.next();
if (theKey.isAcceptable()) {
SocketChannel clientSocket = socket.accept();
clientSocket.configureBlocking(false);
clientSocket.register(selector, SelectionKey.OP_READ);
}
if (theKey.isReadable()) {
SocketChannel clientSocket = (SocketChannel) theKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(9000);
clientSocket.read(buffer);
String result = new String(buffer.array()).trim();
System.out.println(" > SERVER: Request from " + clientSocket.getLocalAddress() + " [ toValidate = " + result + " ], sending tokens...");
//Now I send to the client a list
buffer.flip();
for (int i = 0; i < 5; ++i)
buffer.put((byte) getRandom(1, 10));
clientSocket.write(buffer);
buffer.clear();
System.out.println(" > SERVER: Response successfully sent");
}
keys.remove();
}
}
} catch (Exception e) {
System.out.println("[ SERVER ALERT: " + e.getMessage() + " ]");
}
}
}
As you can see, the code is pretty basic. Inside the if (theKey.isReadable()) I try to read a small string from the client, I print it to the console, then I flip the buffer and I send some data back to the client.
Here we have the client that sadly has a problem I cannot find:
public void something() throws IOException, InterruptedException {
ByteBuffer buffer = ByteBuffer.allocate(9000);
//Note that mRequests is a List of strings
try (SocketChannel client = SocketChannel.open(new InetSocketAddress(authIPAddr, authPort));) {
//For each transaction in mRequests, get the tokens from the server and verify them
for (String s : mRequests) {
//Write the string to the buffer and send the string
buffer.put(s.getBytes());
client.write(buffer);
buffer.rewind();
//Get response from the server
client.read(buffer);
buffer.clear();
Thread.sleep(1000);
}
}
}
The problem is exactly here:
buffer.put(s.getBytes());
client.write(buffer);
buffer.rewind();
I can say that the problem is there because the server should print
SERVER: Request from /127.0.0.1:2323 [ toValidate = {some_value} ], sending
tokens...
but instead it prints
SERVER: Request from /127.0.0.1:2323 [ toValidate = ], sending
tokens...
and from this I guess that data arent sent to the server. How can I solve this?
I am running a messenger program with both server and clients on the same host (a mac pro pc). The uni-cast connection between the server and client works as expected on the same host. A client sends a message to the server via uni-cast connection. The server sends the message to a multi-cast address. Clients listen to the multi-cast address and suppose to receive the message from the server.
The multi-cast connection doesn't work as expected.The server sends the message without any exception but clients don't receive the message via multi-cast. Here are the code snippet for sender and receiver.
public interface SocketMessengerConstants
{
// address for multicast datagrams
public static final String MULTICAST_ADDRESS = "239.0.0.1";
// port for listening for multicast datagrams
public static final int MULTICAST_LISTENING_PORT = 5555;
// port for sending multicast datagrams
public static final int MULTICAST_SENDING_PORT = 5554;
......
public class MulticastSender implements Runnable
{
private byte[] messageBytes; // message data
public MulticastSender( byte[] bytes )
{
messageBytes = bytes; // create the message
} // end MulticastSender constructor
// deliver message to MULTICAST_ADDRESS over DatagramSocket
public void run()
{
try // deliver message
{
// create DatagramSocket for sending message
DatagramSocket socket =
new DatagramSocket( MULTICAST_SENDING_PORT );
// use InetAddress reserved for multicast group
InetAddress group = InetAddress.getByName( MULTICAST_ADDRESS );
// create DatagramPacket containing message
System.out.println(messageBytes.length);
DatagramPacket packet = new DatagramPacket( messageBytes,
messageBytes.length, group, MULTICAST_LISTENING_PORT );
socket.send( packet ); // send packet to multicast group
socket.close(); // close socket
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
} // end method run
} // end class MulticastSender
public class PacketReceiver implements Runnable
{
private MessageListener messageListener; // receives messages
private MulticastSocket multicastSocket; // receive broadcast messages
private InetAddress multicastGroup; // InetAddress of multicast group
private boolean keepListening = true; // terminates PacketReceiver
public PacketReceiver( MessageListener listener )
{
messageListener = listener; // set MessageListener
try // connect MulticastSocket to multicast address and port
{
// create new MulticastSocket
multicastSocket = new MulticastSocket(
MULTICAST_LISTENING_PORT );
// use InetAddress to get multicast group
multicastGroup = InetAddress.getByName( MULTICAST_ADDRESS );
// join multicast group to receive messages
multicastSocket.joinGroup( multicastGroup );
// set 5 second timeout when waiting for new packets
multicastSocket.setSoTimeout( 5000 );
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
} // end PacketReceiver constructor
// listen for messages from multicast group
public void run()
{
// listen for messages until stopped
while ( keepListening )
{
// create buffer for incoming message
byte[] buffer = new byte[ MESSAGE_SIZE ];
// create DatagramPacket for incoming message
DatagramPacket packet = new DatagramPacket( buffer,
MESSAGE_SIZE );
try // receive new DatagramPacket (blocking call)
{
multicastSocket.receive( packet );
} // end try
catch ( SocketTimeoutException socketTimeoutException )
{
continue; // continue to next iteration to keep listening
} // end catch
catch ( IOException ioException )
{
ioException.printStackTrace();
break;
} // end catch
// put message data in a String
String message = new String( packet.getData() );
message = message.trim(); // trim whitespace from message
System.out.println(message);
// tokenize message to retrieve user name and message body
StringTokenizer tokenizer = new StringTokenizer(
message, MESSAGE_SEPARATOR );
// ignore messages that do not contain a user
// name and message body
if ( tokenizer.countTokens() == 2 )
{
// send message to MessageListener
messageListener.messageReceived(
tokenizer.nextToken(), // user name
tokenizer.nextToken() ); // message body
} // end if
} // end while
try
{
multicastSocket.leaveGroup( multicastGroup ); // leave group
multicastSocket.close(); // close MulticastSocket
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
} // end method run
......
} // end class PacketReceiver
Multi-cast feature was disabled on the router. I unchecked "Disable IGMP Proxying" from router's gateway. But multi-cast datagram packet is still not received.
Update: The program was behind firewall. After the firewall permits incoming connections, everything works as expected.
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'm trying to write a chat server and client that uses sockets. Connecting the client works properly and I get the correct output from the server:
Listening on port 8000
Got connection from Socket[addr=/127.0.0.1,port=50628,localport=8000]
But when I send something from the client I get IllegalBlockingModeException. Here's what I have:
public void listen()
{
try {
// Open a non-blocking socket channel
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
// Get the socket and bind it
ServerSocket ss = ssc.socket();
InetSocketAddress isa = new InetSocketAddress(this.getServerName(), this.getServerPort());
ss.bind(isa);
// Create the selector
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Listening on port " + this.getServerPort());
while (true)
{
// Wait for at least one channel to be selected
if (selector.select() == 0)
continue;
// Iterate the key set and dispatch messages
Set keys = selector.selectedKeys();
Iterator it = keys.iterator();
while (it.hasNext())
{
SelectionKey key = (SelectionKey)it.next();
if (key.isAcceptable()) {
Socket s = ss.accept();
System.out.println("Got connection from " + s);
SocketChannel sc = s.getChannel();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
}
else if (key.isReadable()) {
SocketChannel sc = null;
try {
sc = (SocketChannel)key.channel();
String message = processInput(sc);
if (message == null) {
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);
}
}
else {
String response = getListener().process(sc, message);
ObjectOutputStream oos = new ObjectOutputStream(Channels.newOutputStream(sc));
//ObjectOutputStream oos = new ObjectOutputStream(sc.socket().getOutputStream());
oos.writeObject(response + "\n");
oos.close();
}
}
catch(IOException ie) {
key.cancel();
try {
sc.close();
} catch(IOException ie2) {
System.out.println(ie2);
}
System.out.println("Closed " + sc);
}
}
}
keys.clear();
}
}
catch (IOException ex) {
System.out.println(ex);
System.exit(1);
}
}
private String processInput( SocketChannel sc ) throws IOException {
buffer.clear();
sc.read( buffer );
buffer.flip();
if (buffer.limit()==0) {
return null;
}
return decoder.decode(buffer).toString();
}
Here's the stack trace:
java.nio.channels.SocketChannel[connected local=/127.0.0.1:8000 remote=/127.0.0.1:50656]
Exception in thread "main" java.nio.channels.IllegalBlockingModeException
at java.nio.channels.Channels.writeFully(Channels.java:97)
at java.nio.channels.Channels.access$000(Channels.java:61)
at java.nio.channels.Channels$1.write(Channels.java:174)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1870)
at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1779)
at java.io.ObjectOutputStream.<init>(ObjectOutputStream.java:247)
at ChatServer$Server.listen(ChatServer.java:171)
at ChatServer.run(ChatServer.java:231)
at ChatServer.main(ChatServer.java:247)
Java Result: 1
Does anybody know how to solve this?
You can't use Channels.newXXXStream() on a channel in non-blocking mode. See the Javadoc.
Ok, I managed to put it to work. Here's how:
String response = getListener().process(sc, message) + "\n";
ByteBuffer bb = ByteBuffer.wrap(response.getBytes("utf-8"));
sc.write(bb);
Instead of using an OutputStream, write directly to the SocketChannel.
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);