Can someone explain me what this selector.select() method does? - java

Hi there,
I'm reading the book [Java Network Programming](http://sufuq.com/books/java/Java%20Network%20Programming,%204th%20Edition.pdf) to study a way of creating my own web server and i reached a page where it talks about multithreaded server and how to writing to it using the [echo protocol](https://www.rfc-editor.org/rfc/rfc862) as an example (page 293).
Here is the code i'm talking about:
ServerSocketChannel sChannel = null;
Selector selector = null;
try {
sChannel = ServerSocketChannel.open();
ServerSocket echoServer = sChannel.socket();
InetSocketAddress addr = new InetSocketAddress(PORT);
selector = Selector.open();
echoServer.bind(addr);
sChannel.configureBlocking(false);
sChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server running.");
} catch (IOException e) {//If something goes wrong, the server must be stopped
e.printStackTrace();
}
while(true) {
try {
selector.select();
} catch (IOException e) {
e.printStackTrace();
}
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
And my question is about
selector.select()
because that method it's supposed to block the thread when there is no channel registered to this selector but i don't really know how it can create a new channel when a client tries to connect or how this channel is created and when.
I tried reading the documentation and Java NIO by Ron Hitchens book but i didn't succeed, Can anyone explain me this please?

Related

Java NIO closing one channel resulting the other channel not responding

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!

How to disconnect a client from NIO server socket

I am developing a server that is connected with many clients. I need to know when a client is disconnecting from server. So each client is sending a specific character to the server. If the character is not received after two seconds then I should disconnect the server from the client (releasing allocated resource for this client).
This is the main code of my server:
public EchoServer(int port) throws IOException {
this.port = port;
hostAddress = InetAddress.getByName("127.0.0.1");
selector = initSelector();
loop();
}
private Selector initSelector() throws IOException {
Selector socketSelector = SelectorProvider.provider().openSelector();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
InetSocketAddress isa = new InetSocketAddress(hostAddress, port);
serverChannel.socket().bind(isa);
serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
return socketSelector;
}
private void loop() {
for (;true;) {
try {
selector.select();
Iterator<SelectionKey> selectedKeys = selector.selectedKeys()
.iterator();
while (selectedKeys.hasNext()) {
SelectionKey key = selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
continue;
}
// Check what event is available and deal with it
if (key.isAcceptable()) {
accept(key);
} else if (key.isReadable()) {
read(key);
} else if (key.isWritable()) {
write(key);
}
}
Thread.sleep(1000);
timestamp++;
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
The first question is that, whether the way that I used in order to recognizing online clients (sending specific message every second) is a good approach or not?
If it is good, how can I detect with SelectionKey is related to witch client and then how can I disconnect the key from server?
The first question is that, whether the way that I used in order to recognizing online clients (sending specific message every second) is a good approach or not?
Not in the case of an echo server. In many cases such as this, all you need is to recognize end of stream and connection failure appropriately.
how can I detect with SelectionKey is related to which client
The SelectionKey has a channel, the channel has a socket, and the Socket has a remote IP address:port. That's all you need.
and then how can I disconnect the key from server?
Close the channel when you get -1 from the read() method, or any IOException when reading or writing.
whether the way that I used in order to recognizing online clients (sending specific message every second) is a good approach or not?
Yes, it is called a heartbeat.
how can I detect with SelectionKey is related to witch client and then how can I disconnect the key from server?
You can attach an object which has all the information need regarding a channel. You include this when you register the channel.

Muitiplexed socket communication in Java

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

IP address of clients in socket using NIO

Using NIO, we have bound two ports to ServerSocket class.
serverChannelPrimary = ServerSocketChannel.open();
serverChannelSecondary = ServerSocketChannel.open();
// Retrieves a server socket associated with this channel
serverSocketPrimary = serverChannelPrimary.socket();
serverSocketSecondary = serverChannelSecondary.socket();
// Opens a connection selector
connectionSelector = Selector.open();
// Bind the specified port num
serverSocketPrimary.bind(new InetSocketAddress(portOne));
serverSocketSecondary.bind(new InetSocketAddress(portTwo));
// Set nonblocking mode for the listening socket
serverChannelPrimary.configureBlocking(false);
serverChannelSecondary.configureBlocking(false);
// Register the ServerSocketChannel with the Selector
serverChannelPrimary.register(connectionSelector, SelectionKey.OP_ACCEPT);
serverChannelSecondary.register(connectionSelector, SelectionKey.OP_ACCEPT);
Now, we are also able to fetch the IP address of the clients that are connected when the the new client makes the first request, which we are adding to a vector clientIps.
while (isActive) {
try {
numberOfKeys = 0;
numberOfKeys = connectionSelector.select(timeOut);
if (numberOfKeys == 0) {
continue; // None of request available
}
// Get iterator through the selected keys list
Iterator<SelectionKey> iterKeys = connectionSelector
.selectedKeys().iterator();
while (iterKeys.hasNext()) {
try {
SelectionKey selectedKey = (SelectionKey) iterKeys
.next();
// Verify the key validity
if (!selectedKey.isValid()) {
logger.error("Received key is invalid");
continue;
} else if (selectedKey.isAcceptable()) {
// Accept the client request
ServerSocketChannel server = (ServerSocketChannel) selectedKey
.channel();
SocketChannel channel = server.accept();
// Get the socket associated with this channel
Socket clientInfo = channel.socket();
logger.debug("Application got client request from (Host name:"
+ clientInfo.getInetAddress().getHostName()
+ ",Ip address:"
+ clientInfo.getInetAddress()
.getHostAddress()
+ ",port:"
+ clientInfo.getPort());
String clientAddress=clientInfo.getInetAddress().getHostAddress();
if(!clientIps.contains(clientAddress)){
clientIps.add(clientAddress);
}
logger.debug("List of client : "+clientIps);
clientMgr.includeClient(channel);
}
} catch (Exception e) {
logger.error(e.getMessage());
} finally {
logger.debug("Since this key has been handled, remove the SelectedKey from the selector list.");
iterKeys.remove();
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
However, after the connection has been made, once we start getting data from multiple clients on both the ports, is it possible to determine, the IP address of each client whenever each client sends the data. I hope the code that I have provided is sufficient and clear to explain the situation we are having.
ServerSocketChannel is TCP, so the IP addresses at the two ends can't change.
In your line
SocketChannel channel = server.accept();
channel is specific to a particular client. These are the objects you will be using to communicate with each client and each one represents a single TCP session with a single remote ip/port tuple.
You can call SocketChannel.socket().getSocketAddress() to get the remote address of any specific SocketChannel.
Once you get the socketChannel to be able to send back to client, you get use the functions below.
//Not complete example
SocketChannel ssc;
/* after accepting and other such required operations */
ssc.socket().getInetAddress().toString();
/**
Returns:
the remote IP address to which this socket is connected, or null if the socket is not connected.
will return 10.50.10.20 as a string
*/
//To get remote port as an int
ssc.socket().getPort();
I do not see "reading" part of code, but I am sure you have one. You can try to get remote socket address (ip + port) like this:
if (selectionKey.isReadable()) {
SocketChannel client = (SocketChannel) selectionKey.channel();
// you can here read data from given socket; client.read(buffer);
// and also get remote (and local too) address
client.getRemoteAddress();
}

Java Async Sockets multithread performance

I need to question about 11M nameservers and to find out which of them alive. In Java I use async sockets to send udp-requests and everything ok until I try to use multiple threads. The speed rises proportionally, but positive responses decrease dramatically, although I use a high-perfomance 16-core cluster.
I create a separate channel per thread and see no obvious reason why this happening. Can anyone explain what I'm doing wrong and is it okay to use different async sockets in threads?
Here's some code. So I have a lot of threads with id and it's list of hosts, each of them do the following:
#Override
public void run() {
DatagramChannel channel = null;
try {
channel = DatagramChannel.open();
InetSocketAddress isa = new InetSocketAddress(Settings.LOCAL_PORT+id);
channel.socket().bind(isa);
channel.configureBlocking(false);
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
ByteBuffer outBuffer = ByteBuffer.wrap(Settings.QUERY);
ByteBuffer inBuffer = ByteBuffer.allocate(200);
while (true) {
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (!key.isValid()) {
continue;
}
if (key.isReadable()) {
inBuffer.clear();
channel.receive(inBuffer);
inBuffer.flip();
inCounter++;
//some analize of response
continue;
}
if (key.isWritable()) {
if (outCounter < hosts.size()) {
channel.send(outBuffer, new InetSocketAddress(hosts.get(outCounter), Settings.DNS_PORT));
outBuffer.flip();
outCounter++;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (channel != null)
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
As UDP is an unreliable protocol, you have to be careful not to overload your system or your network buffers or packets will be lost. How to do this is likely to be sensitive to a number of factors, so it doesn't surprise me that doing this a little differently could improve your response rate.
Are you using UDP Datagrams or TCP Sockets? Why are trying to poll 11 million names servers?

Categories

Resources