If I configure the ServerSocketChannel as non-blocking, then is there any difference between calling
ServerSocketChannel.accept().socket() and ServerSocket.accept() ?
from below
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking( false );
ServerSocket ss = ssc.socket();
InetSocketAddress isa = new InetSocketAddress( port );
ss.bind( isa );
Selector selector = Selector.open();
ssc.register( selector, SelectionKey.OP_ACCEPT );
System.out.println( "Listening on port "+port );
ByteBuffer buffer = ByteBuffer.allocate( 4096 );
while (true) {
int numKeys = selector.select();
if (numKeys>0) {
Set skeys = selector.selectedKeys();
Iterator it = skeys.iterator();
while (it.hasNext()) {
SelectionKey rsk = (SelectionKey)it.next();
int rskOps = rsk.readyOps();
if ((rskOps & SelectionKey.OP_ACCEPT) ==
SelectionKey.OP_ACCEPT) {
Socket socket = ss.accept()
}
}
}
}
The difference is that one returns a SocketChannel and the other a Socket.
As you are presumably going to select on the result, there really isn't any point in using SsrverSocket.accept(), as you would only have to get the SocketChannel from it anyway, put it into non-blocking mode, register it with the Selector, and do all the I/O with it, making little or no further use of the Socket.
Related
I just found that the problem might due to multi-threading problem. After the read() method I sent the request to several worker threads to process the data. When the thread pool size is 1, the problem doesn't occur. However, when the thread pool size is bigger than 1, this problem occurs.
I initialized my threadpool before the while(true) loop, and execute my worker thread after the read() method.
I'm using Java NIO in a middleware that connects a memtier client and a memcached server. The system works fine with only one clients connected, however, when there are more clients connected, when one client finishes and closes its channel, the other client won't be able to read or write to its channel any more.
My implementation looks like the following:
public void run() {
try{
// Connect to client
selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
InetSocketAddress address = new InetSocketAddress(8000);
server.socket().bind(address);
server.register(selector,SelectionKey.OP_ACCEPT);
// Connect to Server
Socket socket = new Socket("localhost",8090);
// Select keys
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
// check if key is valid
if(!key.isValid()) {
continue;
}
if(key.isAcceptable()) {
accept(key);
}else if(key.isReadable()) {
read(key);
}
}
}
}
}
My accept() and read() looks like this:
private void accept(SelectionKey key) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
private void read(SelectionKey key) {
SocketChannel channel = (SocketChannel) key.channel();
buffer.clear();
int n = -1;
n = channel.read(buffer);
if(n == -1) {
key.cancel();
channel.close();
}else{
//read to buffer
}
}
I have tried many ways but still cannot find out the problem. Thank you very much!
I can send data from io client(using just socket not socket channel), but it was not possible to send data from nio server(using socket channel). What is the problem?
Is there a difference between the way to read/write through I/O stream and bytebuffer?
public class Server {
// this is equivalent to the server socket in the non nio world
ServerSocketChannel serverSocketChannel;
// this is the multiplexer which multiplexes the messages received from
// different clients
Selector selector;
public Server() {
try {
// get a selector
selector = Selector.open();
// get a server socket channel
serverSocketChannel = ServerSocketChannel.open();
// we force the socket to be Non-blocking.
// if it is set to "true" then this socket acts as a normal
// (blocking) server socket
serverSocketChannel.configureBlocking(false);
// port and ip address where the server listens for connections
InetSocketAddress add = new InetSocketAddress(
InetAddress.getLocalHost(), 9999);
// bind the server socket to the ip/port
serverSocketChannel.socket().bind(add);
// register the serverSocketChannel (for incoming connection events)
// to the selector.
// The "SelectionKey.OP_ACCEPT" parameter tells the selector that
// this serverSocketChannel registers
// itself for incoming (acceptable) connections
SelectionKey key = serverSocketChannel.register(selector,
SelectionKey.OP_ACCEPT);
System.out.println("serverSocketChannel's registered key is : "
+ key.channel().toString());
System.out.println();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
server.startListening();
}
private void startListening() {
System.out.println("Server is listening on: "
+ serverSocketChannel.socket().getInetAddress()
.getHostAddress() + ":"
+ serverSocketChannel.socket().getLocalPort());
while (true) {
try {
// this line blocks until some events has occurred in the
// underlying socket
selector.select();
// get the selected keys set
Set selectedKeys = selector.selectedKeys();
Iterator iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
// a client has asked for a new connection
if (key.isAcceptable()) {
// only ServerSocketsChannels registered for OP_ACCEPT
// are excepted to receive an
// "acceptable" key
System.out.println("Key ready to perform accept() : "
+ key.channel().toString());
// as usual the accept returns the plain socket towards
// the client
SocketChannel client = serverSocketChannel.accept();
// set the client socket to be non blocking
client.configureBlocking(false);
// register the client socket with the same selector to
// which we have registered the
// serverSocketChannel
client.register(selector, SelectionKey.OP_READ);
// client.register(selector, SelectionKey.OP_WRITE);
continue;
}
// the client has sent something to be read by this server
if (key.isReadable()) {
System.out.println("Key ready to perform read() : "
+ key.channel().toString());
// get the underlying socket
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer bb = ByteBuffer.allocate(10000);
// read the msg sent by the client
client.read(bb);
// display the message
bb.flip();
byte[] array = new byte[bb.limit()];
bb.get(array);
System.out.println(new String(array));
// send the message
ByteBuffer bb1 = ByteBuffer.allocate(10000);
String s = "server data";
byte[] array1 = new byte[bb1.limit()];
array1 = s.getBytes();
bb1.put(array1);
client.write(bb1);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
void write() {
}
}
public class Client {
static String IP = "192.168.123.105"; // server IP address
public static void main(String[] args) throws UnknownHostException,
IOException {
// TODO Auto-generated method stub
String msg;
Socket socket = new Socket(IP, 9999);
System.out.println("client: complete making socket");
Scanner scan = new Scanner(System.in);
System.out.println("client: input data to send");
while (true) {
// read
System.out.print(">>>");
msg = scan.nextLine();
OutputStream out = socket.getOutputStream();
DataOutputStream dou = new DataOutputStream(out);
dou.writeUTF(msg);
// write from server
InputStream in = socket.getInputStream();
DataInputStream din = new DataInputStream(in);
String remsg = din.readUTF();
System.out.println("client: data from server" + remsg);
if (remsg.equalsIgnoreCase("END")) {
System.out.println("SOCKET END");
socket.close();
break;
}
}
}
}
i solved problem!
it was a problem about how to read from inputstream in client
i changed a part of client code
from
InputStream in = socket.getInputStream();
DataInputStream din = new DataInputStream(in);
String remsg = din.readUTF();
System.out.println("client: data from server" + remsg);
to
ByteBuffer bb1 = ByteBuffer.allocate(10000);
byte[] array1 = new byte[bb1.limit()];
InputStream in = socket.getInputStream();
in.read(array1);
String remsg=new String(array1);
and also added OP_WRITE/isWritable() in server code like this
if (key.isReadable()) {
System.out.println("Key ready to perform read() : "
+ key.channel().toString());
// get the underlying socket
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer bb = ByteBuffer.allocate(1024);
// read the msg sent by the client
client.read(bb);
// display the message
bb.flip();
byte[] array = new byte[bb.limit()];
bb.get(array);
System.out.println(new String(array));
client.register(selector, SelectionKey.OP_WRITE);
continue;
}
if(key.isWritable()){
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer bb1 = ByteBuffer.allocate(10000);
String s = "server data";
byte[] array1 = new byte[bb1.limit()];
array1 = s.getBytes();
bb1.put(array1);
bb1.flip();
client.write(bb1);
}
This question already has an answer here:
Timeout on connect to socket using NIO SocketChannel [duplicate]
(1 answer)
Closed 8 years ago.
I am trying to understand how to write simplest application with one selector and multiple channels.
After incredible effort I was able to write following code:
server:
private static byte[] data = new byte[255];
public static void main(String[] args) throws IOException {
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();
}
}
}
client(sender):
public static void main(String[] argv) throws Exception {
SocketChannel sChannel = SocketChannel.open();
sChannel.configureBlocking(false);
sChannel.connect(new InetSocketAddress("localhost", 9000));
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
buf.put((byte) 0xFF);
buf.flip();
int numBytesWritten = sChannel.write(buf);
}
my steps:
run server
run client
After this step in server console I see following:
Accepted connection from java.nio.channels.SocketChannel[connected local=/127.0.0.1:9000 remote=/127.0.0.1:49184]
In client console I see following:
Exception in thread "main" java.nio.channels.NotYetConnectedException
at sun.nio.ch.SocketChannelImpl.ensureWriteOpen(SocketChannelImpl.java:269)
at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:474)
at io.nio.SocketSender.main(SocketSender.java:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Please help to understand the reason of stack trace.
The message is clear enough. I would do the client connect in blocking mode: then it either completes or fails. Otherwise you have to use a Selector, select for OP_CONNECT, use finishConnect(), test its return value, etc. In fact I would use blocking mode through the client, and I probably wouldn't use NIO at all.
I am writing a server program that can accept communication from multiple (but fixed) number of clients. I want to keep the program single-threaded. To do so, I am using non-blocking socket to iterate over each client, but each client's channel uses blocking mode. Here's my server code:
class server {
public static void main(String args[])
throws Exception {
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new java.net.InetSocketAddress("localhost", 8005));
System.out.println("Server attivo porta 8005");
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_ACCEPT);
for(;;) {
selector.select();
Set keys = selector.selectedKeys();
Iterator i = keys.iterator();
while(i.hasNext()) {
SelectionKey key = (SelectionKey) i.next();
i.remove();
if (key.isAcceptable()) {
SocketChannel client = channel.accept();
client.configureBlocking(true);
ObjectInputStream ois = new ObjectInputStream(
client.socket().getInputStream());
String s = (String)ois.readObject();
System.out.println(s);
}
}
}
}
}
The client uses simple blocking I/O, as shown here:
class client {
public static void main(String args[]) throws Exception {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(true);
channel.connect(new java.net.InetSocketAddress("localhost", 8005));
ObjectOutputStream oos = new ObjectOutputStream
(channel.socket().getOutputStream());
for (int i = 0; i < 100; i++) {
oos.writeObject(new String("Hello " + i));
System.out.println(i);
}
}
}
The problem is that although the client wants to write 100 times, the server is reading the message just once. Neither the server nor the client is giving any exception, but I am only getting the output "Hello 0" from the server. Is there any problem in what I am doing here? If so, what alternatives do I have?
Thanks.
Update: Closing the ObjectInputStream within the server's loop gives a BrokenPipeException by the client (the server behaves in the same way).
The issue is that you're just checking for new connections with key.isAcceptable(). You also need to check for reads with key.isReadble(). You should only be doing connection setup from key.isAcceptable().
See Java ServerSocketChannel SocketChannel (Callback)
The problem is that the server is not waiting for the client to send all it's data. In client server programs, what you need to do is to establish a clear protocol between both so that they are in sync when data is transmitted/received. This is usually done by signaling an end of transmission by either side by sending a designated symbol or closing the connection when they are done
I am converting some Java NIO code to run in Scala and I am getting an error because the SelectionKey I'm calling returns a SelectableChannel rather than a DatagramChannel, which is a subclass of SelectableChannel and an instance of which I declare at the beginning of the code. I did not come to Scala from Java, so my knowledge of Java is actually very limited. It seems to me that the Java code DatagramChannel channel = (DatagramChannel) key.channel(); typecasts the channel to a DatagramChannel. Is this what I need to do in the Scala code?
Scala code:
val channel = DatagramChannel.open()
val selector = Selector.open()
println("Attempting to bind to socket " + port)
channel.socket().bind(new InetSocketAddress(port))
println("Bound to socket " + port)
channel.configureBlocking(isBlocking)
println("Attempting to registered selector")
channel.register(selector, SelectionKey.OP_READ)
println("Registered selector")
println("Ready to receive data!");
while (true) {
try {
while(selector.select() > 0) {
val keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
val key = keyIterator.next();
if (key.isReadable()) {
val channel = key.channel(); // FIXME: returning a SelectableChannel instead of a DatgramChannel
var buffer: Array[Byte] = Array();
val byteBuffer = ByteBuffer.wrap(buffer);
val sockAddress = channel.receive(byteBuffer);
// ...
Original Java code:
channel = DatagramChannel.open();
selector = Selector.open();
System.out.println("Attempting to bind to socket " + port);
channel.socket().bind(new InetSocketAddress(port));
System.out.println("Bound to socket " + port);
channel.configureBlocking(isBlocking);
System.out.println("Attempting to registered selector");
channel.register(selector, SelectionKey.OP_READ);
System.out.println("Registered selector");
System.out.println("Ready to receive data!");
while (true) {
try {
while(selector.select() > 0) {
Iterator keyIterator = selector.selectedKeys().iterator();
while (keyIterator.hasNext()) {
SelectionKey key = (SelectionKey) keyIterator.next();
if (key.isReadable()) {
DatagramChannel channel = (DatagramChannel) key.channel();
byte[] buffer = new byte[2048];
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
SocketAddress sockAddress = channel.receive(byteBuffer);
// ...
SelectionKey.channel() always returns a SelectableChannel. The assigned type of the channel is not really relevant at this point, so you'll have to cast it.