Java TCP server NIO with Selector in Android - java

I'm developing an app in android that will act as a server, this is, there will be an tablet that will be the "server" and there will be other tablets that will connect to the "server".
I'm trying to use the java NIO with a selector, to save on threads. But my problem is, I have the java code running in a thread in android, but when the thread runs it don't happen nothing. On the client side it gives and exception of Connection Refused.
The code is running in a java application, but in android not.
I also have the internet permission.
The java selector:
Thread t = new Thread(new Runnable() {
private Selector selector;
private ServerSocketChannel sChan;
private List<SocketChannel> sockets;
public void run() {
try {
selector = SelectorProvider.provider().openSelector();
sChan = ServerSocketChannel.open();
InetSocketAddress iaddr = new InetSocketAddress(InetAddress.getLocalHost(), 8000);
sChan.configureBlocking(false);
sChan.socket().bind(iaddr);
System.out.println("Running on port:" + sChan.socket().getLocalPort());
sChan.register(selector, SelectionKey.OP_ACCEPT);
sockets = new LinkedList<SocketChannel>();
}
catch (IOException e) {
e.printStackTrace();
}
Iterator<SelectionKey> it;
try {
while (true) {
selector.select();
it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (!key.isValid()) {
continue;
}
// Finish connection in case of an error
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("new client: " + newClient.socket().getInetAddress().getHostAddress());
}
if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer data = ByteBuffer.allocate(sc.socket().getSendBufferSize());
System.out.println("new message: " + sc.socket().getInetAddress().getHostAddress());
if (sc.read(data) == -1) {
continue;
}
data.flip();
Teste m = (Teste) UdpUtil.byteToMessage(data.array());
System.out.println("message: " + m);
System.out.println("\n\n" + m.cenas);
sc.close();
}
}
}
}
catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
And the Client application:
InetSocketAddress isa = new InetSocketAddress(InetAddress.getByName("192.168.2.102"), 8000);
SocketChannel sc = null;
try {
// Connect
sc = SocketChannel.open();
sc.connect(isa);
// Read the time from the remote host. For simplicity we assume
// that the time comes back to us in a single packet, so that we
// only need to read once.
byte[] message = UdpUtil.messageToByteMessage(new messages.Teste("hello there"));
ByteBuffer buf = ByteBuffer.wrap(message);
sc.write(buf);
}
finally {
// Make sure we close the channel (and hence the socket)
if (sc != null) {
sc.close();
}
}
Note: the Teste class its just an class that will be used as the message between the androids.
I had even tried this code and all went well, but with the selector not.
I hope that I made my self clear on what the problem is.
Thanks in advance :)

Remove the first argument to new InetSocketAddress(), the one you use for binding the ServerSocketChannel. At present you are only binding to 127.0.0.1, which cannot be seen from other hosts. By omitting the argument you are binding to 0.0.0.0, which means 'listen at all interfaces'.

Make sure you have requested the internet permission in Android by going into your AndroidManifest.xml file and putting:
uses-permission android:name="android.permission.INTERNET" />

Related

How to make the accept() function non-blocking?

My server is listening to 2 ports ,and it should execute separate functions on each port simultaneously.
my problem is ,that the server blocks until the client of the first port is connected first.
For example: if a second client tried to connect to the 2nd port before a client connects to the 1st port ,it won't let it connect.
I created 2 classes that extends to thread class,so they should wait to any client in parallel instead of blocking what's after them.
But it doesn't seem to work as i'm expecting.
public static void main(String[] args) throws Exception {
System.out.println("server is running.");
int clientNumber = 0;
ServerSocket listenerTrans = new ServerSocket(9899);
ServerSocket listenerDeter = new ServerSocket(9898);
try {
while (true) {
new Deteriment(listenerDeter.accept(), clientNumber++).start();
new Transpose(listenerTrans.accept(), clientNumber++).start();
}
} finally {
listenerTrans.close();
listenerDeter.close();
}
}
Deteriment and Transpose are my classes that extend to thread class.
I want that the listenerDeter.accept() not to block the listenerTrans.accept(), i want that both of the thread's accept() happen in parallel.
Also why isn't it happening in parallel in my code?
The answer is to use a ServerSocketChannel and a Selector. The Selector allows your application to multiplex I/O on multiple channels using a single thread. It can be used in clocking or non-blocking mode
Here is an example (borrowed from How java nio ServerSocketChannel accept works? and adapted for your use-case):
// Create the 2 server socket channels
ServerSocketChannel server1 = ServerSocketChannel.open();
ServerSocketChannel server2 = ServerSocketChannel.open();
// Configure channels for nonblocking I/O
server1.configureBlocking(false);
server2.configureBlocking(false);
// Bind channels' IP and port
server1.socket().bind(new java.net.InetSocketAddress(host, 9899));
server2.socket().bind(new java.net.InetSocketAddress(host, 9898));
// Create the selector
Selector selector = Selector.open();
// Register channels to selector (type OP_ACCEPT)
SelectionKey key1 = server1.register(selector, SelectionKey.OP_ACCEPT);
SelectionKey key2 = server2.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // blocks until one or more of the registered channels
// has actionable I/O
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey selKey = (SelectionKey) it.next();
if (selKey.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) selKey.channel();
SocketChannel sc = ssc.accept();
if (selKey.equals(key1)) {
new Deteriment(sc.socket() ...).start();
} else {
new Transpose(sc.socket(), ...).start();
}
}
}
}
(Caveats: 1: not tested, 2: could be more elegant, 3: possible resource leaks, 4: you really should be using a thread pool / executor rather than firing off new threads by hand)
So, first of all if you like it to be async you need to use separate thread for every ServerSocket what u declare. Why? Becouse by the conception java.net was blocking not scalable way of dealing with net thinks. If you like it to be non-blocking more scalable but less abstrac (I mean you will allocate buffers ^^) you should look for java nio instead.
**EDIT: **
I little modyf your code it should make job done but its improvable i mean its not the most elegent version ^^
public static void main(String[] args) throws Exception {
System.out.println("server is running.");
final int[] clientNumber = {0};
ServerSocket listenerTrans = new ServerSocket(9899);
ServerSocket listenerDeter = new ServerSocket(9898);
try {
ExecutorService ex = Executors
.newFixedThreadPool(2);
ex.execute(
() -> {
try {
Socket s = listenerDeter.accept();
new Deteriment(s, clientNumber[0]++).start();
} catch (IOException e) {
e.printStackTrace();
}
}
);
ex.execute(
() -> {
try {
Socket s = listenerDeter.accept();
new Transpose(s, clientNumber[0]++).start();
} catch (IOException e) {
e.printStackTrace();
}
}
);
} finally {
//listenerTrans.close();
//listenerDeter.close();
}
}

Java NIO Selector doesn't work when not using localhost

I've been working on a program that uses networking with Java that uses the NIO Selector following this tutorial and for some reason when I try to test the program with my friends (that are far away in another network) it doesn't work,
even though when I try to test it myself on only my computer it works perfectly.
Here is the relevant code for the question:
Class EchoServer (a Thread):
private Selector selector;
private ServerSocketChannel serverSocket;
private boolean stop = false;
private List<String> pendingStrings;
public EchoServer() throws IOException {
// Get selector
this.selector = Selector.open();
System.out.println("Selector open: " + selector.isOpen());
// Get server socket channel and register with selector
this.serverSocket = ServerSocketChannel.open();
InetSocketAddress hostAddress = new InetSocketAddress("", NetworkingSettings.PORT);
serverSocket.bind(hostAddress);
serverSocket.configur eBlocking(false);
int ops = serverSocket.validOps();
SelectionKey selectKy = serverSocket.register(selector, ops, null);
this.pendingStrings = new ArrayList<>();
}
#Override
public void run() {
while (!stop) {
try {
update();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void update() throws IOException {
System.out.println("Waiting for select...");
int noOfKeys = selector.select();
System.out.println("Number of selected keys: " + noOfKeys);
Set selectedKeys = selector.selectedKeys();
Iterator iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey ky = (SelectionKey) iter.next();
if (ky.isAcceptable()) {
acceptClient();
}
else if (ky.isReadable()) {
readDataFromClient(ky);
}
iter.remove();
}
}
Class EchoClient:
private SocketChannel client;
private InetSocketAddress hostAddress;
private boolean connected;
public EchoClient(String ip) {
this.hostAddress = new InetSocketAddress(ip, NetworkingSettings.PORT);
connected = false;
}
public void connect() throws IOException {
if (!connected) {
client = SocketChannel.open(hostAddress);
connected = true;
}
}
public void sendMessage(String message) throws IOException {
try {
byte[] messageBytes = message.getBytes();
ByteBuffer buffer = ByteBuffer.wrap(messageBytes);
client.write(buffer);
buffer.clear();
} catch (IOException e) {
cleanUp();
}
}
Now, it seems that the problem is in the server because I can't even connect to the server when my friend runs it (and I am the client).
I suspect the source of the problem are those lines in EchoServer:
InetSocketAddress hostAddress = new InetSocketAddress("", NetworkingSettings.PORT);
serverSocket.bind(hostAddress);
But I can't seem to figure out what is it.
Important Note:NetworkingSettings.PORT is 80, I know it's a port used for http and maybe that is the problem, but I really want to avoid needing to use port forwarding and firewall settings.
The problem lies with the InetSocketAddress the ServerSocketChannel binds to. To allow connections on both localhost and remote network interfaces, you need to bind to the wildcard address. This is done by using the InetSocketAddress constructor that only takes a port number:
InetSocketAddress hostAddress = new InetSocketAddress(NetworkingSettings.PORT);

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

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.

Moving from IO to NIO - Networking, IllegalBlockingModeException

I'm attempting to move my networking over from standard IO to NIO and following the few tutorials that there are to attempt to figure it out, while me, myself, and I decided it'd be a great idea to spend my first week rewriting the core for all of the applications logic handling, I never would have imagined that I wouldn't be able to set up the basic networking.
Currently the networking is at a very basic stage, everything is tossed inside of a while-loop and I can't honestly say I've put any attempt into it to make it look nice, considering I haven't a clue what I'm doing my goal was to figure out how to do it first, then go back and give it a makeover.
Here's the code I use to initialize my server:
// Initializes the TCP Server and all of its components.
private void initTcpServer(int port) {
try {
// Create a new selector
Selector socketSelector = SelectorProvider.provider()
.openSelector();
// Create a new non-blocking server socket channel;
this.serverSocketChannel = ServerSocketChannel.open();
this.serverSocketChannel.configureBlocking(false);
// Bind the server socket to the specified address and port
this.serverSocketChannel.socket().bind(
new InetSocketAddress("127.0.0.1", port));
// Register the server socket channel, indicating an interest in
// accepting new connections
this.serverSocketChannel.register(socketSelector,
SelectionKey.OP_ACCEPT);
// Set the selector for the server instance.
this.selector = socketSelector;
} catch (IOException e) {
e.printStackTrace();
}
}
Then this class implements the Runnable interface, and a new thread is started directly after this method completes, in this thread we contain the following code:
public void run() {
while (isRunning) {
try {
selector.selectNow();
} catch (IOException io) {
return;
}
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if (!key.isValid()) {
it.remove();
continue;
}
try {
if (key.isAcceptable()) {
this.handleConnection(key);
} else if (key.isReadable()) {
Connection connection = (Connection) key.attachment();
if (connection != null) {
try {
connection.getMasterProtocol()
.decode(connection,
connection.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
}
} finally {
it.remove();
}
}
}
}
This, from my understanding is what allows us to handle our connections and data based off of a SelectionKey.. and is what all of the NIO based networking runs from, you'll see that I'm calling two different methods to make this not so much of a mess, the first one is #handleConnection and the other one is a decode function.
The handle connection method creates a new instance of my Connection class and attatches it to the SelectionKey, like so:
public Connection(SelectionKey key) {
try {
// For an accept to be pending the channel must be a server socket channel.
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
// Accept the connection and make it non-blocking.
this.socketChannel = serverSocketChannel.accept();
this.socketChannel.configureBlocking(false);
// Set up other user data.
this.inputStream = new DataInputStream(socketChannel.socket().getInputStream());
this.masterProtocol = new MasterProtocol();
// Register the new SocketChannel with our Selector, indicating
// we'd like to be notified when there's data waiting to be read.
key = this.socketChannel.register(OGServer.getInstance().getSelector(), SelectionKey.OP_READ);
key.attach(this);
// Add the current <SelectorKey, Connection> to the current connections collection.
connections.put(key, this);
Log.debug(getClass(), "Connection constructed successfully.");
} catch(IOException e) {
e.printStackTrace();
}
}
The error is called when I attempt to call the MasterProtocol#decode method, which looks like this:
public Object decode(Connection connection, DataInputStream dataInputStream) throws IOException {
if(connection.getState() == ConnectionState.CONNECTED) {
byte[] bytes = ByteStreams.toByteArray(dataInputStream);
if(bytes.length < 4) {
System.out.println("Not enough bytes read.");
return null;
}
int bufferSize = dataInputStream.readInt();
System.out.println("Buffer Size: " + bufferSize);
while(bytes.length < bufferSize) {
return null;
}
int test = dataInputStream.readInt();
System.out.println("Test: " + test);
return null;
}
return null;
}
The error seems to be called when the DataInputStream tries to read from the network, more specifically on this line of code:
byte[] bytes = ByteStreams.toByteArray(dataInputStream);
The error:
Exception in thread "Thread-0" java.nio.channels.IllegalBlockingModeException
at sun.nio.ch.SocketAdaptor$SocketInputStream.read(SocketAdaptor.java:190)
at sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:103)
at java.io.DataInputStream.read(DataInputStream.java:100)
at com.google.common.io.ByteStreams.copy(ByteStreams.java:70)
at com.google.common.io.ByteStreams.toByteArray(ByteStreams.java:115)
at net.ogserver.framework.net.protocol.MasterProtocol.decode(MasterProtocol.java:29)
at net.ogserver.framework.net.OGServer.run(OGServer.java:146)
at java.lang.Thread.run(Thread.java:745)
The 'IllegalBlockingModeException' exception is what's throwing me off, as all of the information I've found was for setting up a Non-blocking server, but the DataInputStream implementation was my own, so I must have done something wrong somewhere. NIO is a completely different world from IO, but learning is learning, eh?
EDIT: I guess it'd help to know how I'm sending the data from the client, it's just a very basic test application that does this:
socket = new Socket("127.0.0.1", 5055);
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeBoolean(false);
If you're moving to NIO in non-blocking you can't keep using streams. If you want to use streams there is no advantage to using NIO at all. I would just stop the migration project now.

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