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);
Related
i'am writing a small app to remote control a (Behringer x32) Mixing console. And i got a problem with the communication.
I'am sending data from the pc (app) to the console (port 10023 UDP Protocol), then the console answers to the port from the pc has send data, (random port).
So i have 2 Threads one for sending data, and one for listening for data from the console..... so every time i send data to the console, i need to change the listening port... so i have to kill the listening thread and start it new.
But after some time a have and the app has about x1000 threads open.
How can i restart the Thread or update the listening port without create a new thread?
here's the code for this section, the whole files are # gihub
the listening thread class:
public class Receiver implements Runnable {
private List<IReceiverListener> listeners;
private final static int PACKETSIZE = 48;
private int port;
public Receiver() {
listeners = new ArrayList();
}
public void addReceiverListener(IReceiverListener listener) {
listeners.add(listener);
}
private void update(String data, String adress) {
for (IReceiverListener listener : listeners) {
listener.receiveConsoleData(data, adress);
if (data.indexOf("active") > -1) {
listener.incrementWatchDog();
}
}
}
#Override
public void run() {
try {
// Convert the argument to ensure that is it valid
// Construct the socket
while (true) {
//System.out.println("Listen on Port:" + this.port);
DatagramSocket socket = new DatagramSocket(this.port);
// Create a packet
DatagramPacket packet = new DatagramPacket(new byte[PACKETSIZE], PACKETSIZE);
// Receive a packet (blocking)
socket.receive(packet);
// Print the packet
update(new String(packet.getData()), packet.getAddress().toString());
//logger.addLogData(new String(packet.getData())+" "+packet.getAddress().toString());
// Return the packet to the sender
socket.close();
}
} catch (IOException e) {
}
}
public void setPort(int port) {
this.port = port;
}
public int getPort() {
return port;
}
}
and here my port updateFunction
#Override
public void updatePort(int port) {
receiverThread.interrupt();
receiverThread = null;
receiver.setPort(port);
receiverThread = new Thread(receiver);
receiverThread.start();
}
and the sending thread does this, when it sends data:
listener.updatePort(dsocket.getLocalPort());
This is actually not a threading problem. The problem is, that the receiver thread is stuck in the receive method, so it cannot react to the changed port. However, calling the method DatagramSocket#close from another thread releases the blocking receiver thread with a SocketException.
Thus, you can solve this by closing the currently receiving socket when the receiving port was changed. The receiving thread can now catch the SocketException and create a new DatagramSocket that listens on the new port.
There is no need to kill and recreate threads.
First you put the socket into a field. This allows you to access it from another thread, so you can call the socket.close() method. Second, you put another try-catch block into the while(true) loop, which only catches SocketException.
Something like this might work fine:
public class Receiver implements Runnable {
private static final int PACKETSIZE = 48;
private final ConcurrentLinkedQueue<IReceiverListener> listeners = new ConcurrentLinkedQueue<>();
private volatile DatagramSocket socket;
private volatile int port;
public Receiver(int port) {
this.port = port;
}
public void addReceiverListener(IReceiverListener listener) {
listeners.add(listener);
}
public void updatePort(int port) {
this.port = port;
DatagramSocket socket = this.socket;
if (socket != null) {
socket.close();
}
}
#Override
public void run() {
try {
while (true) {
receiveLoop(new DatagramSocket(port));
}
} catch (IOException e) {
// handle error
}
}
private void receiveLoop(DatagramSocket newSocket) throws IOException {
try (DatagramSocket socket = newSocket) {
this.socket = newSocket;
while (true) {
DatagramPacket packet = new DatagramPacket(new byte[PACKETSIZE], PACKETSIZE);
socket.receive(packet);
process(packet);
}
} catch (SocketException e) {
// port was changed -> return and restart with a new socket
} finally {
this.socket = null;
}
}
private void process(DatagramPacket packet) {
update(new String(packet.getData()), packet.getAddress().toString());
}
private void update(String data, String adress) {
for (IReceiverListener listener : listeners) {
listener.receiveConsoleData(data, adress);
if (data.indexOf("active") > -1) {
listener.incrementWatchDog();
}
}
}
}
Please note, that this might still contains some bugs. It is only supposed to give you a rough idea of how to solve this.
As you are using DatagramSocket, you can change the used port by Binding the socket to a new port rather than the used one:
socket.bind(new InetSocketAddress(new_port));
But remember that bind() method won't work unless the socket is already opened and a port assigned to it, so at the first time you have to create the socket regularly, then when you try to change the port, just bind it.
And the following is a complete visualization of the process:
public void video_udp_server(int port) throws Exception
{
byte[] receiveData = new byte[Integer.MAX_VALUE/100];
for(int i = 0; i < receiveData.length; i++){
receiveData[i] = ' ';
}
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
DatagramSocket socket = null;
try{
socket = new DatagramSocket(port);
}catch(Exception ex){
socket.bind(new InetSocketAddress(port));
}
socket.setReuseAddress(true);
socket.receive(receivePacket);
System.out.println(new String(receivePacket.getData()));
}
I'm writing a chat program in java and I have been stuck for hours with this problem. This is my class that waits for clients to connect to the server.
Every time a new client connects I create a new ChatClient(String name, DatagramSocket serverSocket,InetAddress IPAddress, int port) object.
My idea was that every ChatClient object listens on the socket and when a package is sent from the same IP as the ChatClient, it will handle it, otherwise do nothing.
As it is now, when I only have one client connected; the client gets every 2 packages, then run() in WaitForConnection() gets the rest.
So my question, is it possible to have multiple threads listening on the same DatagramSocket without loss (everyone gets everything send). If there is a solution, how?
private ArrayList<ChatClient> clients;
private DatagramSocket serverSocket;
private boolean running;
public WaitForConnection() {
running = true;
clients = new ArrayList<ChatClient>();
try {
serverSocket = new DatagramSocket(ChatServer.port);
} catch (SocketException e) {
System.out
.println("Couldn't open socket. Port might alreadybe in use");
e.printStackTrace();
}
try {
serverSocket.setReuseAddress(true);
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run() {
while (running) {
for (ChatClient ch : clients) {
System.out.println(ch.toString());
}
byte[] handShake = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(handShake,
handShake.length);
try {
serverSocket.receive(receivePacket);
} catch (IOException e) {
System.out.println("Waiting for connections error");
e.printStackTrace();
}
String connect = new String(receivePacket.getData());
System.out.println(connect);
InetAddress IPAddress = receivePacket.getAddress();
// if connect == "OPEN_CONNECTION" -> new client want to connect.
if (connect.contains("openconnection")) {
int port = receivePacket.getPort();
try {
ChatClient chatClient = new ChatClient(
IPAddress.getHostName(), serverSocket, IPAddress,
port);
// Don't want double clients.
for (int i = 0; i < clients.size(); i++) {
if (clients.get(i).equals(chatClient)) {
clients.remove(i);
}
}
clients.add(chatClient);
} catch (IOException e) {
System.out.println("Couldn't connect to client");
e.printStackTrace();
}
}
}
}
}
Code for ChatClient if you need to look at it.
public class ChatClient extends Thread {
private InetAddress IPAddress;
private DatagramSocket serverSocket;
private int port;
private String name;
public ChatClient(String name, DatagramSocket serverSocket,
InetAddress IPAddress, int port) throws IOException {
super(name);
this.name = name;
this.IPAddress = IPAddress;
this.serverSocket = serverSocket;
this.port = port;
byte[] confirmConnection = new byte[1024];
String connected = "Connection to server established";
confirmConnection = connected.getBytes();
serverSocket.send(new DatagramPacket(confirmConnection,
confirmConnection.length, IPAddress, port));
start();
}
public void run() {
while (true) {
byte[] message = new byte[1024];
DatagramPacket receivedPacket = new DatagramPacket(message,
message.length);
try {
serverSocket.receive(receivedPacket);
} catch (IOException e) {
System.out
.println("Something went wrong receiving data in ChatClient");
}
if (receivedPacket.getAddress().equals(IPAddress)) {
String connect = new String(receivedPacket.getData());
connect = connect.toUpperCase();
System.out.println(connect + "client side");
message = connect.getBytes();
try {
serverSocket.send(new DatagramPacket(message,
message.length, IPAddress, port));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
It is possible for multiple threads to receive from the same DatagramSocket, but only one of them will get each datagram.
I don't see why you think you need this.
It is technically not possible, because the network hardware receives the packet only once. But then you can always duplicate it in memory once it is read. In your code basically just do Arrays.copyOf(receivePacket)
For a more sophisticated version, you could use the NIO package and work with a Selector. This would allow you to have all network connections run through a single thread, that reads and distributes the data to processing threads. That saves you the extra threading-overhead if you have multiple connections from many clients.
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.
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" />
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.