This is an Echo Server. I cannot understand why after first connection with client, even after client close the connection still the server is printing "Reading.." and "Writing..". Shouldn't the server block with select() method?
Thanks
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.io.IOException;
public class EchoServer
{
public static int DEFAULT_PORT=7;
public static void main(String [] args)
{
ServerSocketChannel serverChannel;
Selector selector;
try
{
serverChannel = ServerSocketChannel.open();
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(DEFAULT_PORT);
ss.bind(address);
serverChannel.configureBlocking(false);
selector=Selector.open();
serverChannel.register(selector,SelectionKey.OP_ACCEPT);
} catch(IOException ex) {ex.printStackTrace(); return;}
while(true)
{
int selectednum=0;
try{
selectednum=selector.select(); //blocks
}catch (IOException ex) {ex.printStackTrace(); break;}
if (selectednum>0) {
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key=iterator.next();
iterator.remove();
try{
if (key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted from "+client);
client.configureBlocking(false);
SelectionKey clientKey=client.register(
selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.allocate(100);
clientKey.attach(buffer);
}
if (key.isReadable()){
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
System.out.println("Reading..");
client.read(output);
}
if (key.isWritable()){
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
output.flip();
System.out.println("Writing..");
client.write(output);
output.compact();
}
} catch (IOException ex) {
key.cancel();
try { key.channel().close();}
catch (IOException cex) {};
}
}
}
}
}
}
You aren't detecting end of stream when reading from the client. The read() method returns -1. When that happens you should close the channel.
Related
i use pseudo code to describe the problem first,and i'll paste the whole code in the follow,which can run in the local.
1.selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
2.while(true){
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
3. if(!key.isAcceptable()){
continue;
}
4. ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
...
}
}
the exception occured in the step 4,then i do a check in the step 3,but it can not pass the acceptable check and go into a dead loop.
occasionally ,it can receive and response normally and i have not make any change,it's too strange to me.
here i paste the code and hope someone can help me. thanks.
package io.Nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import io.util.IOUtil;
public class NioServer extends Thread{
private int port;
private Selector selector;
private ServerSocketChannel serverChannel;
public NioServer(int port){
this.port = port;
}
#Override
public void run() {
try{
selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}catch(IOException e){
IOUtil.close(serverChannel);
}
System.out.println("server start:" + port);
while(true){
try {
selector.select();
} catch (ClosedSelectorException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if(!key.isValid()){
key.cancel();
IOUtil.close(key.channel());
IOUtil.close(key.selector());
System.out.println(IOUtil.now() + "clear a invalid key.");
continue;
}
// i put a check here,if is not Acceptable,then continue, but it's a dead loop
if(!key.isAcceptable()){
System.out.println("not Acceptable");
continue;
}
try {
//Exception here: SocketChannelImpl cannot be cast to ServerSocketChannel
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
if(channel == null){
continue;
}
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
// if (key.isReadable()){
// System.out.println("not read");
// }
ByteBuffer buffer = ByteBuffer.allocate(1024);
if (channel.read(buffer) > 0) {
buffer.flip();
byte[] byteArray = new byte[buffer.remaining()];
buffer.get(byteArray);
String expression = new String(byteArray, "UTF-8");
System.out.println(IOUtil.now() + "receive request:" + expression);
String result = null;
response(channel, result);
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void shutdown(){
IOUtil.close(selector);
IOUtil.close(serverChannel);
}
private void response(SocketChannel channel, String response) throws IOException {
response = "hello response";
System.out.println(IOUtil.now() + "send response:"+ response);
byte[] bytes = response.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
buffer.flip();
channel.write(buffer);
}
public static void main(String[] args) {
new NioServer(IOUtil.DEFAULT_PORT).start();
}
}
package io.Nio;
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.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import io.util.IOUtil;
public class NioClient extends Thread{
private volatile CountDownLatch connectLatch;
private String ip;
private int port;
private Selector selector;
private SocketChannel socketChannel;
private NioClient(String ip, int port) {
this.ip = ip;
this.port = port;
connectLatch = new CountDownLatch(1);
}
public static NioClient open(String ip, int port){
NioClient client = new NioClient(ip,port);
client.start();
return client;
}
#Override
public void run(){
try{
long begin = System.currentTimeMillis();
System.out.println(IOUtil.now() + "start client");
selector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(ip,port));
while(!socketChannel.finishConnect()){
yield();
}
System.out.println(IOUtil.now() + "cost time:" + (System.currentTimeMillis() - begin) + "ms");
connectLatch.countDown();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
while(true){
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key = it.next();
if(!key.isValid() || !key.isReadable()){
continue;
}
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
if(channel.read(buffer) > 0){
buffer.flip();
byte[] byteArray = new byte[buffer.remaining()];
buffer.get(byteArray);
String response = new String(byteArray,"UTF-8");
System.out.println(IOUtil.now() + "receive response:" + response);
}
}
}
}catch(IOException e){
e.printStackTrace();
}
}
public void request(String request) {
try {
connectLatch.await();
} catch (InterruptedException e) {
System.out.println(IOUtil.now() + "interrupted" + e.getMessage());
}
try {
byte[] bytes = request.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
buffer.put(bytes);
buffer.flip();
socketChannel.register(selector, SelectionKey.OP_READ);
//TODO
System.out.println(IOUtil.now() + "send request:" + request);
socketChannel.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(final String[] args) throws InterruptedException {
NioClient client = NioClient.open(IOUtil.DEFAULT_HOST, IOUtil.DEFAULT_PORT);
client.request("hello");
// while(true){
// sleep(500);
// String request = IOUtil.buileRequest(1991);
// client.request(request);
// }
}
}
package io.util;
import java.io.Closeable;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class IOUtil {
public static final String DEFAULT_HOST = "127.0.0.1";
public static final int DEFAULT_PORT = 8080;
public static final String operators[] = {"+", "-", "*", "/"};
public static final int CLIENNT_NUM = 10;
public static final boolean CLIENT_CLOSEABLE = true;
public static String now(){
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss SSS ").format(new Date());
}
public static String buileRequest(int seed){
Random random = new Random(seed);
return random.nextInt(10) + IOUtil.operators[random.nextInt(4)] + (random.nextInt(10) + 1);
}
public static void close(Closeable io) {
if (io != null) {
try {
io.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The only way the exception you describe could occur is if you attempt that typecast on a channel which is not a ServerSocketChannel, which could happen if its key is ready but not 'acceptable'. Clearly you didn't have your step 2 when you got this exception, so you processed a 'readable' channel as though it was an 'acceptable' channel.
So the code you posted doesn't actually exhibit that problem, but it does have a large number of others. You don't need to close the selector just because a key is invalid, and if it is invalid it is already cancelled, so you don't need to cancel it, and anyway closing the channel cancels the key. Why aren't you interested in OP_READ/isReadable() on the channels you have accepted and registered for OP_READ? And why are you trying to read from a channel you have just accepted, without waiting for OP_READ?
Throw it away and have a good look at the Java NIO tutorial.
I do want to comment on one particular piece of nonsense in the client:
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(ip,port));
while(!socketChannel.finishConnect()){
yield();
}
Here you are performing the equivalent of a blocking-mode connect in non-blocking mode. It can all be replaced by:
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress(ip,port));
socketChannel.configureBlocking(false);
Then:
socketChannel.register(selector, SelectionKey.OP_CONNECT);
Here you are registering for an event that has already happened. Truly bizarre. You will never get OP_CONNECT from an already-connected socket. Remove.
I got a problem when using Java nio to do networking.
I start a server in a thread and register read operationsin the selector. and another thread connect to the server.
client write a byte to the server
server reads the datas and back a byte to the client
client get the response and write a byte to the server again.
server loop step 2.
but server cannot do the step 4 successfully because I can't get any read Key from selector any more. why ?? I have registered it before, and client has written data.
there is my code below :
package test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
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.util.Iterator;
public class JavaNIO {
public static void main(String[] args) throws Exception {
final Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(5555));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
new Thread() {
public void run() {
while(true) {
try {
long count = selector.select();
if(count == 0) continue;
Iterator<SelectionKey> itKey = selector.selectedKeys().iterator();
while(itKey.hasNext()) {
SelectionKey key = itKey.next();
itKey.remove();
if(key.isAcceptable()) {
ServerSocketChannel channel = (ServerSocketChannel)key.channel();
SocketChannel socket = channel.accept();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
}
if(key.isReadable()) {
ByteBuffer buffer= ByteBuffer.allocate(1);
SocketChannel channel = (SocketChannel)key.channel();
while(true) {
buffer.flip();
int len = channel.read(buffer);
if(len == 0) break;
buffer.clear();
}
channel.read(buffer);
channel.register(selector, SelectionKey.OP_WRITE, null);
}
if(key.isWritable()) {
SocketChannel channel = (SocketChannel) key.channel();
channel.write(ByteBuffer.wrap("ce".getBytes()));
key.cancel();
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
try {
Socket socket = new Socket("localhost", 5555);
byte[] reads = new byte[1024];
for(int i = 0; i < 1000; i++) {
socket.getOutputStream().write(new byte[]{1});
socket.getInputStream().read(reads);
System.out.println(new String(reads));
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
You re-registered the channel for OP_WRITE, so its interestOps ceased to include OP_READ. If you want both, change accordingly. 'Or' them together.
I am creating a server that will handle >1000 connections. I decided to go with non-blocking IO in my server. I found some code on the internet, which is basically an echo server. I think everything is fine, but I don't understand a few of the concepts in the server.
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
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.util.*;
public class EchoServer {
private InetAddress addr;
private int port;
private Selector selector;
private Map<SocketChannel,List<byte[]>> dataMap;
public EchoServer(InetAddress addr, int port) throws IOException {
this.addr = addr;
this.port = port;
dataMap = new HashMap<SocketChannel,List<byte[]>>();
startServer();
}
private void startServer() throws IOException {
// create selector and channel
this.selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
// bind to port
InetSocketAddress listenAddr = new InetSocketAddress(this.addr, this.port);
serverChannel.socket().bind(listenAddr);
serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
log("Echo server ready. Ctrl-C to stop.");
// processing
while (true) {
// wait for events
this.selector.select();
// wakeup to work on selected keys
Iterator keys = this.selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
// this is necessary to prevent the same key from coming up
// again the next time around.
keys.remove();
if (! key.isValid()) {
continue;
}
if (key.isAcceptable()) {
this.accept(key);
}
else if (key.isReadable()) {
this.read(key);
}
else if (key.isWritable()) {
this.write(key);
}
}
}
}
private void accept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
// write welcome message
channel.write(ByteBuffer.wrap("Welcome, this is the echo server\r\n".getBytes("US- ASCII")));
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connected to: " + remoteAddr);
// register channel with selector for further IO
dataMap.put(channel, new ArrayList<byte[]>());
channel.register(this.selector, SelectionKey.OP_READ);
}
private void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(8192);
int numRead = -1;
try {
numRead = channel.read(buffer);
}
catch (IOException e) {
e.printStackTrace();
}
if (numRead == -1) {
this.dataMap.remove(channel);
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connection closed by client: " + remoteAddr);
channel.close();
key.cancel();
return;
}
byte[] data = new byte[numRead];
System.arraycopy(buffer.array(), 0, data, 0, numRead);
log("Got: " + new String(data, "US-ASCII"));
// write back to client
doEcho(key, data);
}
private void write(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap.get(channel);
Iterator<byte[]> items = pendingData.iterator();
while (items.hasNext()) {
byte[] item = items.next();
items.remove();
channel.write(ByteBuffer.wrap(item));
}
key.interestOps(SelectionKey.OP_READ);
}
private void doEcho(SelectionKey key, byte[] data) {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap.get(channel);
pendingData.add(data);
key.interestOps(SelectionKey.OP_WRITE);
}
private static void log(String s) {
System.out.println(s);
}
public static void main(String[] args) throws Exception {
new EchoServer(null, 8989);
}
}
So For this code, I have a few questions. One, if I read 10 bytes, but I don't want to do anything until I read 100 bytes, how would I implement that? Also, say I only want to write when a counter reaches a certain number, how would I implement that non-blocking? The thing about this code is, is that it will echo no matter how big the bytebuffer is. How do I change that so it would only echo when it has 100 bytes? How can I write only if a counter is a certain size? Thanks!
Would putting an if(numRead < 100) {do rest} else {return} in the read method work for the first problem?
Also, would putting an if(counter > 100) {do rest} else{return} in the write method work for the second?
You have to code that part, basically you need to keep track of bytes read, keep adding bytes read to temporary buffer and once you reached your required limit, you can pass that buffer to your worker thread.
I'll recommend you to use netty it provides all the things which you are looking for out of the box.
Look at this link.
Hope this helps
Did channel with non blocking mode return -1 while reading?? For your question, You can set the bytebuffer limit:-
For example:-
ByteBuffer buff = ByteBuffer.allocate(1024);
buff.clear();
buff.limit(your_limit);//is this what you want??
while(buff.remaining>0&&channel.read(buff)); // if will reach till your limit only.
System.out.println(new String(buff.array()));
hope this help
I am using the java.nio package to create a TCP client-server connection between two android devices.
Here is what the client does:
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress(gameAddr, 8001));
Here is what the server does:
try
{
tokenizer = new FixedSeparatorMessageTokenizer(Strings.DELIMITER, Charset.forName("UTF-8"));
selector = SelectorProvider.provider().openSelector();
sChan = ServerSocketChannel.open();
InetSocketAddress iaddr = new InetSocketAddress(InetAddress.getLocalHost(), 8001);
sChan.configureBlocking(false);
sChan.socket().bind(iaddr);
sChan.register(selector, SelectionKey.OP_ACCEPT);
sockets = new Vector<SocketChannel>();
}
catch (Exception e)
{
e.printStackTrace();
}
Iterator<SelectionKey> it;
try {
while (selector.isOpen())
{
selector.select();
it = selector.selectedKeys().iterator();
while (it.hasNext())
{
SelectionKey key = it.next();
it.remove();
if (!key.isValid())
{
continue;
}
if (key.isConnectable())
{
SocketChannel ssc = (SocketChannel) key.channel();
if (ssc.isConnectionPending()) ssc.finishConnect();
}
if (key.isAcceptable())
{
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel newClient = ssc.accept();
newClient.configureBlocking(false);
newClient.register(selector, SelectionKey.OP_READ);
sockets.add(newClient);
System.out.println("Connection from " + newClient.socket().getInetAddress().getHostAddress());
}
if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer data = ByteBuffer.allocate(sc.socket().getSendBufferSize());
System.out.println("Data from " + sc.socket().getInetAddress().getHostAddress());
if (sc.read(data) == -1) //is this a socket close?
{
continue;
}
data.flip();
tokenizer.addBytes(data);
while(tokenizer.hasMessage())
{
ParsedMessage pm = new ParsedMessage(tokenizer.nextMessage());
pm.setAttachment(sc);
synchronized(Q) { Q.add(pm); }
}
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
They both are using the same port 8001 (I tried different ports) and they are in the same LAN because UDP packets actually make it from one device to the other. What can be the problem?
"Connection refused" means that the connection attempt from the client reached the server (or a server), but there was nothing accepting connections at the specified IP address and port. Since you are using port 8001 in both cases, the simplest explanation is that either your server or your client aren't using the correct IP address to communicate with the other end.
You're using this line to create the server socket:
InetSocketAddress iaddr = new InetSocketAddress(InetAddress.getLocalHost(), 8001);
the simplest explanation is that InetAddress.getLocalHost() isn't returning the same IP address that the client is using.
How can you read an object directly from a SocketChannel that is non-blocking? It's being accessed with a Selector. The following code is broken (it throws an IllegalBlockingModeException) and I don't know how to fix it, except for perhaps using ByteBuffer, which I'd rather not (for now, at least):
public static void main(String[] args) {
try {
Selector selector = Selector.open();
ServerSocketChannel listener = ServerSocketChannel.open();
listener.socket().bind(new InetSocketAddress(50001));
listener.configureBlocking(false);
listener.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Iterator<SelectionKey> i = selector.selectedKeys().iterator();
while (i.hasNext()) {
SelectionKey key = i.next();
i.remove();
if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
ObjectInputStream ois = new ObjectInputStream(channel.socket().getInputStream());
String message = (String) ois.readObject();
System.out.println("Server received message: " + message);
}
}
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}