multi-client program without multi-threading - java

Is there any way to write a multi-client program using sockets in Java without multi-threading?
I heard about socket mapping but how does it work?
I know multi-threading, but for now I should not use it.

Well I just threw this together. This is just an example to programming with java.nio. I think this code is fairly straight forward. It binds a ServerSocket to ports 3000-3009. Whenever a client sends data it will echo it back to them. Itcan handle as many connections as possible. You should implement more error handling of course but it will be harder to read then for reference. Handling data in this way is a bit more difficult than using thread per client approach, if you want it's possible to have a hybrid type server where inactive connections go into non blocking mode and when you start receiving data you can jump back into blocking mode with a cached thread pool.
To test it out just open a shell and use telnet localhost 3000
Selector selector = Selector.open();
for(int port=3000;port<3010;port++){
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(port));
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Bound to " + server);
}
ByteBuffer buffer = ByteBuffer.allocate(0x4000);
while(selector.isOpen()){
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while(keys.hasNext()){
SelectionKey key = keys.next();
if(!key.isValid()) continue;
if(key.isReadable()){
buffer.clear();
SocketChannel socket = (SocketChannel)key.channel();
if(socket.read(buffer) == -1){ //-1 is end of stream
System.out.println("Client Disconnected " + socket);
socket.close();
continue;
}else{
buffer.flip();
socket.write(buffer); //echo data back to client
}
}else if(key.isAcceptable()){
ServerSocketChannel serverChannel = (ServerSocketChannel)key.channel();
SocketChannel socket = serverChannel.accept();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
System.out.println("Client Connected " + socket);
}
}
selector.selectedKeys().clear();
}

Related

Is JAVA NIO wasting CPU cycles by looping continuously?

Code for an echo server using a single threaded java I/O
public static void main(String[] args) throws Exception {
// create socket
int port = 4444;
ServerSocket serverSocket = new ServerSocket(port);
System.err.println("Started server on port " + port);
try {
// repeatedly wait for connections, and process
while (true) {
// a "blocking" call which waits until a connection is requested
Socket clientSocket = serverSocket.accept();
System.err.println("Accepted connection from client");
// open up IO streams
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintStream out = new PrintStream(clientSocket.getOutputStream());
// waits for data and reads it in until connection dies
// readLine() blocks until the server receives a new line from client
String s;
while ((s = in.readLine()) != null) {
out.print(s);
}
// close IO streams, then socket
System.err.println("Closing connection with client");
out.close();
in.close();
clientSocket.close();
}
} finally {
serverSocket.close();
}
}
code for the same using NIO
public static void main(String[] args) throws IOException {
ServerSocketChannel server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(PORT_NUMBER));
server.socket().setReuseAddress(true);
server.configureBlocking(false);
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
while (true) {
int channelCount = selector.select();
if (channelCount > 0) {
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ, client.socket().getPort());
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
System.out.println("port: " + key.attachment());
if (client.read(buffer) < 0) {
key.cancel();
client.close();
} else {
buffer.flip(); // read from the buffer
/*
* byte[] received = new byte[buffer.remaining()];
* buffer.get(received); buffer.clear(); // write into the buffer
* buffer.put(received); buffer.flip(); // read from the buffer
*/
client.write(buffer);
buffer.clear(); // write into the buffer
}
}
}
}
}
}
Here using normal I/O the main thread comes and wait at the socket.accept() call. But NIO doesn't do that since socketChannel.accept() is not a blocking call.
So wont the NIO program be continuously running the loop ? And result in waste of CPU cycles ? Can I write the program in a better way. Sorry I am very new to JAVA NIO and asynchronous programming.
In normal IO the thread is blocked on serverSocket.accept().
With NIO the thread is blocked on selector.select().
From the JavaDoc of Selector#select():
This method performs a blocking selection operation.
Why is this called "non blocking IO"?
Actually, your first example (with normal IO) has two blocking calls: server.accept() and in.readLine().
Now consider the case with a badly behaving client: it opens a connection to the server, but never sends any data. With normal IO the server thread waits in in.readLine() for data to arrive and cannot serve any other client until the first client closes its connection.
With NIO the picture is different: if a client opens a connection, the server thread wakes up, server.accept()s the connection and registers the SocketChannel with the same selector. Then the server thread waits on the selector once more through selector.select(). Now there are two possibilities to wake up the server thread: either another client connecting, or the first client sending some data.
So the term "non blocking IO" does not mean that the server thread is never blocked - it means that a non-behaving client cannot block the server thread forever.

How java-nio is Non Blocking confused

I am new to NIO i understand the concept of Asynchronous Socket but i am confused on Non Blocking part.
I am using java NIO Selector . My Code for Server is
public class EcoNonBlockingIOServer_7 {
public static int PORT_NUMBER = 5555;
public static void main(String[] argv) throws Exception {
new EcoNonBlockingIOServer_7().go(argv);
}
public void go(String[] argv) throws Exception {
int port = PORT_NUMBER;
if (argv.length > 0) { // Override default listen port
port = Integer.parseInt(argv[0]);
}
System.out.println("Listening on port " + port);
// Allocate an unbound server socket channel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
// Get the associated ServerSocket to bind it with
ServerSocket serverSocket = serverChannel.socket();
// Create a new Selector for use below
Selector selector = Selector.open();
// Set the port the server channel will listen to
serverSocket.bind(new InetSocketAddress(port));
// Set nonblocking mode for the listening socket
serverChannel.configureBlocking(false);
// Register the ServerSocketChannel with the Selector
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// This may block for a long time. Upon returning, the
// selected set contains keys of the ready channels.
int n = selector.select();
if (n == 0) {
continue; // nothing to do
}
// Get an iterator over the set of selected keys
Iterator it = selector.selectedKeys().iterator();
// Look at each key in the selected set
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
// Is a new connection coming in?
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
registerChannel(selector, channel, SelectionKey.OP_READ);
sayHello(channel);
}
// Is there data to read on this channel?
if (key.isReadable()) {
readDataFromSocket(key);
}
// Remove key from selected set; it's been handled
it.remove();
}
}
}
Now My Queries are:
If we register a channel with selector on any operation it always get blocked on selector.select() then how it is non blocking.
If we admit it uses OP_ACCEPT as key and maps channel accordingly but again In key is acceptable i am modifying this channel selector to OP_READ since it already has been accepted. Again It blocks on selector.select() for read event
*Please correct my understanding if i am wrong *
If we register a channel with selector on any operation it always get blocked on selector.select() then how it is non blocking.
select() is blocking. Every operation on a non-blocking channel itself is non-blocking, i.e. read() and write().
If we admit it uses OP_ACCEPT as key and maps channel accordingly but again In key is acceptable i am modifying this channel selector to OP_READ since it already has been accepted.
Very confused. The channel whose interest-ops == OP_ACCEPT is the listening socket. The channel you accepted from the listening socket is a connected socket, and it is this socket that you put into non-blocking mode, register with OP_ACCEPT, etc.
Again It blocks on selector.select() for read event
Correct, but it doesn't block in read() or write() or accept() or finishConnect(). Using a selector is actually called multiplexed I/O: you wait for multiple channels and multiple events at the same time in a single operation.

Muitiplexed socket communication in Java

I am writing a server program that can accept communication from multiple (but fixed) number of clients. I want to keep the program single-threaded. To do so, I am using non-blocking socket to iterate over each client, but each client's channel uses blocking mode. Here's my server code:
class server {
public static void main(String args[])
throws Exception {
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new java.net.InetSocketAddress("localhost", 8005));
System.out.println("Server attivo porta 8005");
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_ACCEPT);
for(;;) {
selector.select();
Set keys = selector.selectedKeys();
Iterator i = keys.iterator();
while(i.hasNext()) {
SelectionKey key = (SelectionKey) i.next();
i.remove();
if (key.isAcceptable()) {
SocketChannel client = channel.accept();
client.configureBlocking(true);
ObjectInputStream ois = new ObjectInputStream(
client.socket().getInputStream());
String s = (String)ois.readObject();
System.out.println(s);
}
}
}
}
}
The client uses simple blocking I/O, as shown here:
class client {
public static void main(String args[]) throws Exception {
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(true);
channel.connect(new java.net.InetSocketAddress("localhost", 8005));
ObjectOutputStream oos = new ObjectOutputStream
(channel.socket().getOutputStream());
for (int i = 0; i < 100; i++) {
oos.writeObject(new String("Hello " + i));
System.out.println(i);
}
}
}
The problem is that although the client wants to write 100 times, the server is reading the message just once. Neither the server nor the client is giving any exception, but I am only getting the output "Hello 0" from the server. Is there any problem in what I am doing here? If so, what alternatives do I have?
Thanks.
Update: Closing the ObjectInputStream within the server's loop gives a BrokenPipeException by the client (the server behaves in the same way).
The issue is that you're just checking for new connections with key.isAcceptable(). You also need to check for reads with key.isReadble(). You should only be doing connection setup from key.isAcceptable().
See Java ServerSocketChannel SocketChannel (Callback)
The problem is that the server is not waiting for the client to send all it's data. In client server programs, what you need to do is to establish a clear protocol between both so that they are in sync when data is transmitted/received. This is usually done by signaling an end of transmission by either side by sending a designated symbol or closing the connection when they are done

Asynchronous UDP how to get ip and port from client in java?

This is my code:
channel = DatagramChannel.open();
socket = channel.socket();
channel.configureBlocking(false);
socket.bind(new InetSocketAddress(3000));
selector = Selector.open();
channel.register(selector, SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.allocate(65536);
while(true)
{
if(selector.select()>0)
{
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator iterator = selectionKeys.iterator();
while(iterator.hasNext())
{
SelectionKey key = (SelectionKey)iterator.next();
iterator.remove();
InetSocketAddress isa = (InetSocketAddress) channel.getRemoteAddress();
if(key.isReadable())
{
System.out.print(isa.getAddress().getHostAddress()+":"+isa.getPort());
}
}
}
}
the isa is null.I want to get the DatagramPack SocketAddress like socket.receive(DatagramPack); but i dont know channel how to get it. Use Channel.getSocketAddress() retun Null.
UDP is a connectionless protocol, so you will not be able to find the remote address of the channel, since there is no such thing. Once you open a UDP port for listening, everybody can send you messages, without establishing a direct connection. Every message you receive can potentially come from a different sender.
What you can do is to retrieve the remote address of the message. Check the DatagramChannel.receive() method: it will fill the buffer with the message, and return the address of the sender of that particular message.

IP address of clients in socket using NIO

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

Categories

Resources