java.net.ConnectException: Connection timed out: no further information - java

Now today I was testing Server and Client code on different machine.
Both were on same Wi-fi network.
I created clients using below code and got this exception for many threads :
java.net.ConnectException: Connection timed out: no further information
at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.SocketChannelImpl.finishConnect(Unknown Source)
at SocketTest.connect(Client.java:188)
at SocketTest.run(Client.java:73)
at java.lang.Thread.run(Unknown Source)
the line 73 is connect(key)
and line 188 is if(!(channel.finishConnect()))
So the client thread was unable to connect because no reply came from server ? Right ?
Question)When I run both Server and Client on same machine localhost this exception does not arise. What may be the reasons ? (network problem ?).
Also I also use Backlog queue parameter in public void bind(SocketAddress endpoint,int backlog) as 2000. While exact size is unknown(around 200 ?) but I am using a large value so that maximum value will be used.(Right ? or Java will make a queue ?).
Can this be a reason : The Server puts the request in backlog queue and till it gets time to serve it, the timeout may have happened at Client ?
Client :
public class Client {
public static void main(String[] args) {
int n=100;
SocketTest [] st= new SocketTest[n];
for(int i=0;i<n;i++)
st[i]= new SocketTest("hi");
for(int i=0;i<n;i++)
{
if(i%50 == 0)
try{
Thread.sleep(5000);
}
catch(InterruptedException ie)
{
System.out.println(""+ie);
}
new Thread(st[i]).start();
}
}
}
class SocketTest implements Runnable {
private String message = "";
ByteBuffer readBuffer = ByteBuffer.allocate(1000);
private Selector selector;
private int i;
public static AtomicInteger cnt= new AtomicInteger(0);
public SocketTest(String message){
this.message = message;
}
#Override
public void run() {
SocketChannel channel;
try {
selector = Selector.open();
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_CONNECT);
channel.connect(new InetSocketAddress("192.168.1.10", 8511));
while (!Thread.currentThread().isInterrupted()){
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()){
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid()) continue;
if (key.isConnectable()){
connect(key);
System.out.println("I am connected to the server");
}
if (key.isWritable()){
write(key);
}
if (key.isReadable()){
read(key);
}
}
}
}
catch(ClosedByInterruptException e)
{
// let go of thread
}
catch(CancelledKeyException e){
e.printStackTrace();
}
catch (IOException e1) {
System.out.println("IOE Occured|maybe Server died");
e1.printStackTrace();
} finally {
close();
}
}
private void close(){
try {
if(selector!=null)
selector.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
private void read (SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
readBuffer.clear();
int length;
try{
length = channel.read(readBuffer);
} catch (IOException e){
System.out.println("Reading problem, closing connection for : "+channel.getLocalAddress());
key.cancel();
channel.close();
return;
}
if (length == -1){
System.out.println("Nothing was read from server");
channel.close();
key.cancel();
return;
}
readBuffer.flip();
byte[] buff = new byte[1024];
readBuffer.get(buff, 0, length);
//length=buff.length;
String fromserver = new String(buff,0,length,"UTF-8");
length = fromserver.length();
System.out.println("Server said: "+fromserver);
key.interestOps(SelectionKey.OP_WRITE);
}
private void write(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
i++;
message = "location now "+i;
if(i==2)
{
cnt.addAndGet(1);
System.out.println("****"+cnt.get()+"****");
}
try{
Thread.sleep(5000);
}
catch(InterruptedException ie)
{
System.out.println(""+ie);
//Thread.currentThread().interrupt();
}
//assuming all goes in one shot
channel.write(ByteBuffer.wrap(message.getBytes()));
key.interestOps(SelectionKey.OP_READ/*|SelectionKey.OP_WRITE*/);
}
private void connect(SelectionKey key){
SocketChannel channel= (SocketChannel) key.channel();
try
{
if(!(channel.finishConnect())){
//System.out.println("* Here *");
return;
}
}
catch(ConnectException e){
System.out.println("Conect Exception");
e.printStackTrace();
try{channel.close();}
catch(IOException ie){ie.printStackTrace();key.cancel();return;}
return;
}
catch(IOException e)
{
System.out.println("BP 1"+e);
e.printStackTrace();
try{channel.close();}
catch(IOException ie){ie.printStackTrace();key.cancel();return;}
return;
}
//channel.configureBlocking(false);
//channel.register(selector, SelectionKey.OP_WRITE);
key.interestOps(SelectionKey.OP_WRITE);
}
}

The connect timed out because the server didn't reply.
When I run both Server and Client on same machine localhost this exception does not arise. What may be the reasons ? (network problem ?).
Why should it arise? The server is there, the client is there, no network problems in between. The question doesn't make sense.
Also I also use Backlog queue parameter in public void bind(SocketAddress endpoint,int backlog) as 2000. While exact size is unknown(around 200 ?) but I am using a large value so that maximum value will be used. Right?
Right.
or Java will make a queue?
I don't know what this means. Java doesn't do anything with the backlog parameter. It goes straight to TCP.
Can this be a reason: The Server puts the request in backlog queue and till it gets time to serve it, the timeout may have happened at Client ?
No. Only completed connections go on the backlog queue.

Related

HTTP1.1 Connection:keepalive implement with java occurs withjava.net.SocketTimeoutException: Read timed out

I'm implement a http server with version1.1 using java socket programming. I use a version 1.0 sample code and I want add the persistent connection feature by not closing socket utilt a "Connection : close" send to the server. However, I came accross with "java.net.SocketTimeoutException: Read timed out" info after an input like"localhost:8080/xxxx" on my browser and not receiving anything when tested with a client program. Code is too long, and I mention the matter parts bellow! Can you find the problems for me, thanks!!!
////////here is the server part using thread pool techs
//Webserver class
protected static Properties props = new Properties();
/* Where worker threads stand idle */
static Vector threads = new Vector();
public static void main(String[] a) throws Exception {
int port = 8080;
if (a.length > 0) {
port = Integer.parseInt(a[0]);
}
loadProps();
printProps();
/* start worker threads */
for (int i = 0; i < workers; ++i) {
Worker w = new Worker();
(new Thread(w, "worker #"+i)).start();
threads.addElement(w);
}
ServerSocket ss = new ServerSocket(port);
while (true) {
Socket s = ss.accept();
Worker w = null;
synchronized (threads) {
if (threads.isEmpty()) {
Worker ws = new Worker();
ws.setSocket(s);
(new Thread(ws, "additional worker")).start();
} else {
w = (Worker) threads.elementAt(0);
threads.removeElementAt(0);
w.setSocket(s);
}
}
}
}
//Worker class inherit from Webserver class
byte[] buf;
Worker() {
buf = new byte[BUF_SIZE];
s = null;
}
synchronized void setSocket(Socket s) {
this.s = s;
notify();
}
public synchronized void run() {
while(true) {
if (s == null) {
/* nothing to do */
try {
wait();
} catch (InterruptedException e) {
/* should not happen */
continue;
}
}
try {
handleClient();
} catch (Exception e) {
e.printStackTrace();
}
/* go back in wait queue if there's fewer
* than numHandler connections.
*/
if(!headAttri.getPersistConnec())
s = null;
//
Vector pool = WebServer.threads;
synchronized (pool) {
if (pool.size() >= WebServer.workers) {
/* too many threads, exit this one */
try{
if(s != null)
s.close();
}catch (IOException e) {
e.printStackTrace();
}
return;
} else {
if(!headAttri.getPersistConnec())
pool.addElement(this);
}
}
}
}
//in handle client I mention the socket handles here(s is the socket)
void handleClient() throws IOException {
//...
s.setSoTimeout(WebServer.timeout);
s.setTcpNoDelay(true);
//...
try{
//...handle request and response the client
//...
}finally{
//close socket if head info "Connection: close" is found
if(headAttri.getPersistConnec()){
s.setKeepAlive(true);
}
else{
s.close();
}
}
}
//////////end server part
//////here is the client part
public SimpleSocketClient()
{
String testServerName = "localhost";
int port = 8080;
try
{
// open a socket
Socket socket = openSocket(testServerName, port);
// write-to, and read-from the socket.
// in this case just write a simple command to a web server.
String result = writeToAndReadFromSocket(socket, request_str[1]);
// print out the result we got back from the server
System.out.println(result);
// close the socket, and we're done
socket.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
private Socket openSocket(String server, int port) throws Exception
{
Socket socket;
// create a socket with a timeout
try
{
InetAddress inteAddress = InetAddress.getByName(server);
SocketAddress socketAddress = new InetSocketAddress(inteAddress, port);
// create a socket
socket = new Socket();
// this method will block no more than timeout ms.
int timeoutInMs = 10*1000; // 10 seconds
socket.connect(socketAddress, timeoutInMs);
return socket;
}
catch (SocketTimeoutException ste)
{
System.err.println("Timed out waiting for the socket.");
ste.printStackTrace();
throw ste;
}
}
private String writeToAndReadFromSocket(Socket socket, String writeTo) throws Exception
{
try
{
// write text to the socket
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write(writeTo);
bufferedWriter.flush();
//test
//bufferedWriter.write("GET src/WebServer.java HTTP/1.1\r\nHost: localhost\r\nConnection: close");
//bufferedWriter.flush();
// read text from the socket
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder sb = new StringBuilder();
//string handling code
String str;
while ((str = bufferedReader.readLine()) != null)
{
sb.append(str + "\n");
}
// close the reader, and return the results as a String
bufferedReader.close();
return sb.toString();
}
catch (IOException e)
{
e.printStackTrace();
throw e;
}
}
////end client part
//close socket if head info "Connection: close" is found
if(headAttri.getPersistConnec()){
s.setKeepAlive(true);
It is hard to tell from your code what you are really doing but based on this code fragment it looks like you are mixing up HTTP keep alive (i.e. Connection: keep-alive handling, multiple requests in a single TCP connection) with TCP keep alive (detect broken TCP connection). See Relation between HTTP Keep Alive duration and TCP timeout duration and HTTP Keep Alive and TCP keep alive for explanations about the difference.
I want add the persistent connection feature by not closing socket utilt a "Connection : close" send to the server
That's not how you do it. You have to close the connection yourself, either
after a request with a Connection: close header is received and you've sent the response, or
when you get a read timeout on the socket reading the next request.
The length of the read timeout is entirely up to you, because it is up to you to protect yourself from DOS attacks among other things.
NB calling Socket.setKeepAlive(true) has absolutely nothing whatsoever to do with it.
NB 2 You should look into java.util.concurrent.Executor rather than implement your own thread pool.

Java how to always listen for responses from async server socket

Im tryng to build an async java client socket, which is always listening for responses from a server.
My java program has a GUI so I understand I can not simply put the read method in a thread or runnable because it will block my gui from showing, etc.. I've tried using a swingworker and also an executorservice but it has not worked.
Any help would be greatly appreciated, here's some code!
public class ClientWindow {
//Variable declarations
public Selector selector;
public SocketChannel channel;
//Connects when program starts up..
btnConnectActionPerformed (ActionEvent evt){
selector = Selector.open();
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_CONNECT);
channel.connect(new InetSocketAddress(getProperties.server, port));
connect(channel);
//..stuff to make gui let me know it connected
}
//connect method
private void connect(SocketChannel channel) throws IOException {
if (channel.isConnectionPending()) {
channel.finishConnect();
}
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_WRITE);
}
//read method
private void read(SocketChannel channel) throws IOException
{
ByteBuffer readBuffer = ByteBuffer.allocate(1000);
readBuffer.clear();
int length;
try {
length = channel.read(readBuffer);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "Trouble reading from server\nClosing connection", "Reading Problem", JOptionPane.ERROR_MESSAGE);
channel.close();
btnDiscon.doClick();
return;
}
if (length == -1) { //If -1 is returned, the end-of-stream is reached (the connection is closed).
JOptionPane.showMessageDialog(null, "Nothing was read from server\nClosing connection", "Closing Connection", JOptionPane.ERROR_MESSAGE);
channel.close();
btnDiscon.doClick();
return;
}
readBuffer.flip();
byte[] buff = new byte[1024];
readBuffer.get(buff, 0, length);
String buffRead = new String(buff);
System.out.println("Received: " + buffRead);
}
}
So I managed to get it working, probably was missing a line or two or something. Not sure since it's basically how i was tryng to do it before.
//* Nested class to constantly listen to server *//
public class ReaderWorker extends SwingWorker<Void, Void> {
private SocketChannel channel;
public ReaderWorker(SocketChannel channel) {
this.channel = channel;
}
#Override
protected Void doInBackground() throws Exception {
while (true) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
System.out.println("Executed");
try {
read(channel);
} catch (IOException ex) {
Logger.getLogger(ClientWindow.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Java - Keeping a socket client alive after finishing reading

I'm a bit new to sockets in Java but here is my question:
I have written a client thread class that will request to connect to a server, created by another application (therefore I do not have a class made for server class). Basically, this application will transmit a certain number of bytes and close the server side of the socket. I have been fully able to receive and process those bytes.
My question is can the client side socket be told to "wait" for another connection from the same address/port to be available and then continue to read bytes? (In essence, I run the application, it reads the bytes and finished, then I run the application again, and the client will still be able to read)
Here is the code for my client thread:
public class ClientThread extends Thread{
private Socket soc;
private InputStream in;
private String host;
private int port;
public ClientThread(String host, int port)
{
this.host = host;
this.port = port;
soc = null;
in = null;
}
public boolean connectToServer()
{
try {
soc = new Socket(host, port);
in = new BufferedInputStream(soc.getInputStream());
System.err.println("Connection accepted: "+soc);
return true;
} catch (UnknownHostException e) {
System.err.println("Unable to determine IP of host: "+host+".");
return false;
} catch (SocketException e) {
System.err.println("Error creating or accessing the socket.");
return false;
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to: "+host+".");
return false;
}
}
public boolean disconnectFromServer()
{
try {
if(in != null)
in.close();
if(soc != null && soc.isConnected()) {
soc.close();
soc = null;
System.err.println("Connection successfully closed!");
}
return true;
} catch (Exception e) {
System.err.println("Exception: "+e);
return false;
}
}
#Override
public void run() {
try {
int sz = 0;
byte[] tmp = new byte[25];
while(true)
{
if(sz == -1) {
sz = 0;
}
sz += in.read(tmp, sz, 25-sz);
System.out.println(sz);
if(sz == 25) {
tmp = new byte[25];
for(byte b: tmp)
System.out.print(b);
sz = 0;
Thread.sleep(500);
}
}
} catch (SocketException e) {
System.err.println("Connection closed abruptly.");
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to: "+host+".");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The server will receive a socket when accept() returns, and as long as that socket does not get closed then the connection remains open.
So when you run the application for the first time, then shut down the server and then run it for the second time, I think the connection will be broken and you have to re-initialize it.
The isConnected() method will give you a false result, because this will always return true if the socket is not closed explicitly by you at the client side.
If you want to be sure, you can run your client-server connection once, then shut down the server, restart it and then try reading/writing from/to the server. If the connection is broken, you'll get -1 as a result from reading and an IOExceptionfrom writing.

Why my non blocking Java server refuses client connections?

Good day everybody! I'm developing NIO based server and I'm trying to test it with simple client programm.
Before posting code I would like to briefly describe problem: in the test case where server does his job immediately everything is OK. But when I'm trying to add some real life behavior such as short delay on servicing I'm getting "java.net.ConnectException: Connection refused" exceptions. More precisely, part of 100 client threads get this connection refused exception.
I use the following code:
Client
public class TCPClient implements Runnable{
private String name;
public TCPClient(String name)
{
this.name = name;
}
public static void main(String[] args)
{
for(int i=0;i<100;i++)
{
Thread t = new Thread(new TCPClient("thread # "+Integer.toString(i)));
t.start();
}
}
#Override
public void run()
{
Socket socket = null;
OutputStream out = null;
int counter = 0;
try
{
socket = new Socket();
socket.connect(new InetSocketAddress("192.168.3.109",2345), 0);
out = socket.getOutputStream();
byte[] bytes;
while(counter<100)
{
counter++;
bytes = (name+ ", message # "+Integer.toString(counter)+System.lineSeparator()).getBytes();
out.write(bytes);
out.flush();
Thread.sleep(200);
}
}
catch(Exception ex)
{
System.out.println(name+" "+Integer.toString(counter));
ex.printStackTrace(new PrintStream(System.out));
System.out.println();
}
finally
{
if(socket!=null && out!=null)
{
try
{
socket.close();
out.close();
}
catch(Exception ex)
{
System.out.println("client close error");
}
}
}
}
}
Server
public class TCPServer {
private Selector selector;
private boolean isRunning;
private ServerSocketChannel server;
private int counter;
private PrintWriter times;
private PrintWriter logger;
private Charset charset;
private CharsetDecoder decoder;
ByteBuffer bb;
long serviceTime,curTime;
Random random;
public TCPServer(int port)
{
counter = 0;
isRunning = false;
serviceTime = 0;
random = new Random();
random.setSeed(System.currentTimeMillis());
bb = ByteBuffer.allocate(2048);
try
{
selector = Selector.open();
server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(port));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
}
catch(Exception ex)
{
System.out.println("initialization error "+ex.getMessage());
}
}
public void startServer() {
isRunning = true;
int acc = 0;
boolean error = false;
while (isRunning) {
try
{
selector.select();
Set keys = selector.selectedKeys();
Iterator it = keys.iterator();
while(it.hasNext())
{
SelectionKey key = (SelectionKey)it.next();
if (key.isConnectable())
{
((SocketChannel)key.channel()).finishConnect();
}
if (key.isAcceptable())
{
//logger.println("socket accepted");
//logger.flush();
acc++;
System.out.println("accepted sockets count = "+acc);
SocketChannel client = server.accept();
client.configureBlocking(false);
client.socket().setTcpNoDelay(true);
client.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable())
{
curTime = System.currentTimeMillis();
SocketChannel sc = (SocketChannel) key.channel();
bb.clear();
int x = sc.read(bb);
if(x==-1)
{
key.cancel();
continue;
}
counter++;
// Thread.sleep(2);
int sum=0;
for(int dummy=0;dummy<4000000;dummy++) // without this delay client works fine
{
sum+=random.nextInt();
sum%=1005;
}
serviceTime+= System.currentTimeMillis() - curTime;
if(counter>=10000)
{
System.out.println("recieved messages count = "+counter);
System.out.println("service time = "+serviceTime+" milliseconds");
}
}
}
keys.clear();
}
catch (Exception ex)
{
System.out.println("error in recieving messages "+ex.getMessage());
}
}
}
public static void main(String[] args)
{
TCPServer deviceServer = new TCPServer(2345);
deviceServer.startServer();
}
}
The problem is in for(dummy...) loop - it's just simulation of service delay - time needed to parse incoming messages, write something to DB and so on. When delay is small code works fine, all of 10000 messages come to server (100 client threads X 100 messages from each client) but when dummy loop makes over 3.000.000 iterations some of client threads fail to connect to server. One more strange thing here is ignoring infinite timeout property by client socket. I mean socket.connect(InetAddress,timeout) with timeout equal to zero means infinite timeout - in other words service delay doesn't make sense at least I expect such behavior.
It looks like the server socket has a maximum number of pending connections it will allow. The JavaDoc for ServerSocket says:
The maximum queue length for incoming connection indications (a
request to connect) is set to 50. If a connection indication arrives
when the queue is full, the connection is refused.
Right now, I can't find the same information for ServerSocketChannel, but I'm sure it must exist.
ServerSocketChannel.bind allows configuration of the number of pending connections allowed.

Simple non-blocking server

For the purpose of writing an instant messenger program, I am trying to make up a simple server class which will run in its own thread.
What the server should do
accept connections from / connect to other instances of the server and associate the selection keys for the connections in Map<Integer, SelectionKey> keys wit an ID so the messenger thread can access the connections by ID
read from / write to connections
store incoming messages in a queue
messenger thread can
fetch incoming messages
queue messages to be sent : send_message(int id, String msg)
My current approach is based mainly on this example: A simple non-blocking Echo server with Java nio.
I also used Using a Selector to Manage Non-Blocking Sockets and the realted pages to learn about non-blocking sockets and selectors.
Current code
Suggestions by EJP implemented
small changes
package snserver;
/* imports */
//class SNServer (Simple non-blocking Server)
public class SNServer extends Thread {
private int port;
private Selector selector;
private ConcurrentMap<Integer, SelectionKey> keys; // ID -> associated key
private ConcurrentMap<SocketChannel,List<byte[]>> dataMap_out;
ConcurrentLinkedQueue<String> in_msg; //incoming messages to be fetched by messenger thread
public SNServer(int port) {
this.port = port;
dataMap_out = new ConcurrentHashMap<SocketChannel, List<byte[]>>();
keys = new ConcurrentHashMap<Integer, SelectionKey>();
}
public void start_server() throws IOException {
// create selector and channel
this.selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
// bind to port
InetSocketAddress listenAddr = new InetSocketAddress((InetAddress)null, 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);
}
else if(key.isConnectable()) {
this.connect(key);
}
}
}
}
private void accept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
send_message(key, "Welcome."); //DEBUG
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connected to: " + remoteAddr);
// register channel with selector for further IO
dataMap_out.put(channel, new ArrayList<byte[]>());
channel.register(this.selector, SelectionKey.OP_READ);
//store key in 'keys' to be accessable by ID from messenger thread //TODO first get ID
keys.put(0, key);
}
//TODO verify, test
public void init_connect(String addr, int port){
try {
SocketChannel channel = createSocketChannel(addr, port);
channel.register(this.selector, channel.validOps()/*, SelectionKey.OP_?*/);
}
catch (IOException e) {
//TODO handle
}
}
//TODO verify, test
private void connect(SelectionKey key) {
SocketChannel channel = (SocketChannel) key.channel();
try {
channel.finishConnect(); //try to finish connection - if 'false' is returned keep 'OP_CONNECT' registered
//store key in 'keys' to be accessable by ID from messenger thread //TODO first get ID
keys.put(0, key);
}
catch (IOException e0) {
try {
//TODO handle ok?
channel.close();
}
catch (IOException e1) {
//TODO handle
}
}
}
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_out.remove(channel);
Socket socket = channel.socket();
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
log("Connection closed by client: " + remoteAddr); //TODO handle
channel.close();
return;
}
byte[] data = new byte[numRead];
System.arraycopy(buffer.array(), 0, data, 0, numRead);
in_msg.add(new String(data, "utf-8"));
}
private void write(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap_out.get(channel);
Iterator<byte[]> items = pendingData.iterator();
while (items.hasNext()) {
byte[] item = items.next();
items.remove();
//TODO is this correct? -> re-doing write in loop with same buffer object
ByteBuffer buffer = ByteBuffer.wrap(item);
int bytes_to_write = buffer.capacity();
while (bytes_to_write > 0) {
bytes_to_write -= channel.write(buffer);
}
}
key.interestOps(SelectionKey.OP_READ);
}
public void queue_data(SelectionKey key, byte[] data) {
SocketChannel channel = (SocketChannel) key.channel();
List<byte[]> pendingData = this.dataMap_out.get(channel);
key.interestOps(SelectionKey.OP_WRITE);
pendingData.add(data);
}
public void send_message(int id, String msg) {
SelectionKey key = keys.get(id);
if (key != null)
send_message(key, msg);
//else
//TODO handle
}
public void send_message(SelectionKey key, String msg) {
try {
queue_data(key, msg.getBytes("utf-8"));
}
catch (UnsupportedEncodingException ex) {
//is not thrown: utf-8 is always defined
}
}
public String get_message() {
return in_msg.poll();
}
private static void log(String s) {
System.out.println(s);
}
#Override
public void run() {
try {
start_server();
}
catch (IOException e) {
System.out.println("IOException: " + e);
//TODO handle exception
}
}
// Creates a non-blocking socket channel for the specified host name and port.
// connect() is called on the new channel before it is returned.
public static SocketChannel createSocketChannel(String hostName, int port) throws IOException {
// Create a non-blocking socket channel
SocketChannel sChannel = SocketChannel.open();
sChannel.configureBlocking(false);
// Send a connection request to the server; this method is non-blocking
sChannel.connect(new InetSocketAddress(hostName, port));
return sChannel;
}
}
My question: Is the above code correct and good or what should I change? How do I implement the requirements I mentioned above correctly? Also note my "TODO"s.
Thank you for any help!
There are several problems here.
You aren't checking the result of write(). It can return anything from zero up. You may have to re-do it more than once.
If finishConnect() returns false it isn't an error, it just hasn't finished yet, so just leave OP_CONNECT registered and wait for it to fire (again). The only validOps() for a SocketChannel you have just created via SocketChannel.open() is OP_CONNECT. If finishConnect() throws an Exception, that's an error, and you should close the channel.
Closing a channel cancels the key, you don't have to cancel it yourself.
Generally you should use null as the local InetAddress when binding.

Categories