Shifting from blocking to non-blocking I/O with javanio - java

i adapt this code How to send and receive serialized object in socket channel my real time simulation to send objects but i am running into exceptions one after another is it because this code blocking in nature how this code can be converted in to non blocking with javanio
/*
* Writer
*/
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class CleanSender implements Runnable {
private SimManager SM;
private BallState ballState = new BallState(10, 5);
private ServerSocketChannel ssChannel;
private Thread tRunSer = new Thread(this, "ServerSelectThread");
public static void main(String[] args) throws IOException {
CleanSender server = new CleanSender();
server.startServer();
}
private void startServer() throws IOException {
ssChannel = ServerSocketChannel.open();
ssChannel.configureBlocking(true);
int port = 2345;
ssChannel.socket().bind(new InetSocketAddress(port));
// SM = new SimManager(this, BS);
// SM.start(); // GameEngine thread starting here
tRunSer.start();
}
public void run() {
try {
SocketChannel sChannel = ssChannel.accept();
while (true) {
ObjectOutputStream oos = new ObjectOutputStream(sChannel
.socket().getOutputStream());
oos.writeObject(ballState);
System.out.println("Sending String is: '" + ballState.X + "'" + ballState.Y);
oos.close();
System.out.println("Sender Start");
System.out.println("Connection ended");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client: which is continously looking for reply from server
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
public class CleanReceiver implements Runnable {
private SocketChannel sChannel;
private Thread receiverThread = new Thread(this, "receiverThread");
private synchronized void startServer() throws IOException {
sChannel = SocketChannel.open();
sChannel.configureBlocking(true);
if (sChannel.connect(new InetSocketAddress("localhost", 2345))) {
receiverThread.start();
}
}
public void run() {
while (true) {
try {
ObjectInputStream ois = new ObjectInputStream(sChannel.socket()
.getInputStream());
BallState s = (BallState) ois.readObject();
System.out.println("String is: '" + s.X + "'" + s.Y);
ois.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("End Receiver");
}
}
public static void main(String[] args)
throws IOException, ClassNotFoundException {
CleanReceiver rc=new CleanReceiver();
rc.startServer();
System.out.println("End Receiver");
}
}
Will this design work in the scenario when server has to keep connect the client and simultaneous send the simulation state to already connected client?, i m looking for experts glance.
thanks,
jibbylala

If you are using ObjectInputStream or ObjectOutputStream I suggest you stick with blocking IO. Using non-blocking IO with these libraries is 10x harder for no real benifit.
Have you considered using ServerSocket and Socket instead of NIO. These will be easier to use and what the object streams were originall designed to use,

Your code have two main problems:
You close streams after handling every single object, that causes closing of the associated sockets, so they are no longer valid and cannot be used for processing the following objects. At the receiving side you don't need close() inside a loop at all, at the sending side use flush() instead of close() to ensure that buffers are flushed.
When implementing blocking IO you (usually) need to start a new thread on the server for each client. It would allow you to communicate with multiple clients simultaneously. Beware of thread synchronization problems in this case!
If having a thread per client is not acceptable for you, you can implement server in a non-blocking way, but, as already said by Peter Lawrey, it's more complex, so I suggest you to get it working with blocking IO first.

Related

Transmitting data from another thread, slow serial link with Java and RXTX library

Alright, I'll try to be as clear as possible with my problem.
I'm transmitting serial data over a veeeeeeery slow radio link (using the UART-controller on the raspberry pi and a home-built radio). It's for a very specific project where the requirement is spelled long range and speed is of less importance.
The program (Radio.java) is running two threads. One thread (Receiver) receives telemetry data from another program using a TCP-socket (which is very high speed, actually 100mbit). This thread continuously saves the data it receives on the TCP-socket in an ArrayBlockingQueue (with size = 1) so that the other thread (Transmitter) can reach this data. The rate at which the Receiver-thread receives data is pretty high. Now, I want Transmitter-thread to transmit the data and when it's finished I want it to again get the latest data from Receiver-thread and transmit it again over the slow-radio-link.
So in transmitter-thread I want it to work like this:
Get latest data from Receiver-thread
Transmit data over radio link (using the serialport)
Don't do ANYTHING until the data is actually transmitted.
repeat.
Now, when I'm running the program everything regarding the Receiver-thread is working just fine. But inside the transmitter-thread the line "this.out.write(output.getBytes());" just puts everything inside the OutputStream in a couple of milliseconds, and then again does the same thing. The data has no chance in being transmitted!
I've tried the example (only using the "SerialWriter"-thread) here:
http://rxtx.qbang.org/wiki/index.php/Two_way_communcation_with_the_serial_port
And using a long "Lirum Ipsum"-text everything worked just fine transmitting in 50baud. So basically, I want the same behaviour in my program as using System.in.read > -1... (which I guess is blocking, the reason it works???).
What should I do?
2015-01-01 edit BEGIN
I've found the problem! SRobertz lead me into the right direction! The problem is actually not the writespeed to the UART-buffer. The difference between running the "TwoWayComm"-example and my own code is that I'm running a GPS connected to UART-RX-port of the Raspberry Pi. To read data from the GPS is use the "GPSD"-software (which outputs data in a JSON-format). The GPSD-software connects to the GPS with 9600baud (specifically for this GPS-unit), while I switch to 50 baud on the same port (without closing the open connection that GPSD is running)! Trying to open UART with two different baud-rates is what is messing everything up.
I've rewritten the code so that I:
Open UART on 9600 baud
Read GPS data
Close the UART
Open UART on 50 baud
Transmit telemetry data to UART
Close UART
Repeat
And now everything works like a charm...
2015-01-01 edit END
So ... here is the code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
public class RADIO {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(1);
void connect(String portName) throws Exception {
CommPortIdentifier portIdentifier = CommPortIdentifier
.getPortIdentifier(portName);
if (portIdentifier.isCurrentlyOwned()) {
System.out.println("Error: Port is currently in use");
} else {
int timeout = 2000;
CommPort commPort = portIdentifier.open(this.getClass().getName(),
timeout);
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort;
serialPort.setSerialPortParams(50, SerialPort.DATABITS_7,
SerialPort.STOPBITS_2, SerialPort.PARITY_NONE);
// Open outputstream to write to the serial port
OutputStream out = serialPort.getOutputStream();
(new Thread(new Receiver(queue))).start();
(new Thread(new Transmitter(out, queue))).start();
} else {
System.err.println("Error: Not serial port.");
}
}
}
public static class Receiver implements Runnable {
OutputStream out;
protected ArrayBlockingQueue<String> queue = null;
public Receiver(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
public void run() {
// Open TCP-connection
try {
ServerSocket serverSocket = new ServerSocket(1002);
Socket clientSocket = serverSocket.accept(); // Wait for the client to start up
BufferedReader in = new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
String inputLine, outputLine;
while ((inputLine = in.readLine()) != null) {
queue.clear();
queue.put(inputLine);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static class Transmitter implements Runnable {
OutputStream out;
protected ArrayBlockingQueue<String> queue = null;
String output = "";
public Transmitter(OutputStream out, ArrayBlockingQueue<String> queue) {
this.out = out;
this.queue = queue;
}
public void run() {
try {
while (true) {
output = queue.take();
this.out.write(output.getBytes());
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
(new RADIO()).connect("/dev/ttyAMA0");
} catch (Exception e) {
e.printStackTrace();
}
}
}
(a small caveat: I haven't used gnu.io on the raspberry pi)
First, to isolate the problem, I would put a fairly long sleep in the transmitter thread, after this.out.write... to verify that the problem is not waiting for the serial port to
finish the transmission.
If that works, then you can try waiting for OUTPUT_BUFFER_EMPTY, by adding a
SerialPortEventListener and setting notifyOnOutputEmpty(true), making your
SerialPortEventListener a monitor along the lines of
class ExampleMonitor implements SerialPortEventListener {
boolean condition;
public synchronized serialEvent(SerialPortEvent ev) {
condition = true;
notifyAll();
}
public synchronized void awaitCondition() throws InterruptedException {
while(!condition) wait();
condition = false;
}
and then do
myExampleMonitor.awaitCondition() instead of the sleep in the transmit thread.
See http://rxtx.qbang.org/wiki/index.php/Event_based_two_way_Communication for the inverse use of events (note that there, there is no monitor and no waiting; instead, the work is done in the listener/callback.)

stop thread with udp server

I've got an UDP server class which implements Runnable interface. I start it in the thread.
The problem is that I can't stop it. Even in Debug it stops on pt.join() method.
Here is my server class
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class Network implements Runnable {
final int port = 6789;
DatagramSocket socket;
byte[] input = new byte[1024];
byte[] output = new byte[1024];
public Network() throws SocketException{
socket = new DatagramSocket(6789);
}
#Override
public void run() {
while(true){
DatagramPacket pack = new DatagramPacket(input,input.length);
try {
socket.receive(pack);
} catch (IOException e) {
e.printStackTrace();
}
input = pack.getData();
System.out.println(new String(input));
output = "Server answer".getBytes();
DatagramPacket sendpack = new DatagramPacket(output,output.length,pack.getAddress(),pack.getPort());
try {
socket.send(sendpack);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
This is the main class
public class Main {
static Network network = null;
public static void main(String[] args) throws IOException{
network = new Network();
System.out.println("Try to start server");
Thread pt = new Thread(network);
pt.start();
pt.interrupt();
try {
pt.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Stop server");
}
}
How to stop server?
java.net reads are non-interruptible. You would have to either close the DatagramSocket or have it read with a timeout (setSoTimeout()), and when you get the resulting SocketTimeoutException check the interrupt status: if set, exit the thread.
Calling interrupt doesn't actually stop the thread, it just sets a flag.
Inside your loop, check for isInterrupted(). e.g., a quick and dirty way would be change
while(true)
to
while (!Thread.currentThread().isInterrupted())
But you should consult some more documentation if you get more serious about this project.
As mentioned by #EJP, if you are hanging in the Socket IO, you'll need to close the Socket or have a timeout.
In addition to what EJP said, you probably should have a local boolean called running (or whatever), and set it to true before you enter your while loop. Have your while loop be conditioned on this local boolean. And provide methods (stopServer() and isRunning()) to set and check the status of the boolean. You also might want to remove the try-catch from inside the while loop and put the entire while loop within a try-catch-finally and in the finally statement perform clean-up (set running=false; close the connection, etc)

NullPointerException in Thread's run method

I would really appreciate help with my program. It is some sort of chat server with multiple clients.
Here's the server code:
package com.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static int PORT;
private ServerSocket server;
private Socket socket;
public Server(int port) throws IOException {
PORT = port;
server = new ServerSocket(PORT);
System.out.println("server started");
try {
while (true) {
socket = server.accept();
try {
new ServeClient(socket);
} catch (IOException e) {
socket.close();
}
}
} finally {
server.close();
}
}
public static void main(String[] args) throws IOException {
int port = Integer.parseInt(args[0]);
Server server = new Server(port);
}
}
I start the server and then create a Client. The server receives connection socket from socket
and creates a ServeClient Thread.
Here's ServeClient code:
package com.server;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Vector;
import com.gui.WindowManager;
public class ServeClient extends Thread {
private final Socket socket;
private BufferedReader in;
private PrintWriter out;
private String msg;
public static final String ENDSTRING = "END";
public static Vector clients = new Vector();
public ServeClient(final Socket socket) throws IOException {
this.socket = socket;
System.out.println("socket " + socket);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream())), true);
start();
}
public void run() {
try {
clients.add(this);
while (true) {
msg = in.readLine();
if (msg == ENDSTRING)
break;
broadcast(msg);
}
System.out.println("closing...");
} catch (IOException e) {
System.err.println("IO EXCEPTION");
} finally {
try {
socket.close();
} catch (IOException e) {
System.err.println("SOCKET NOT CLOSED");
}
}
}
#SuppressWarnings("deprecation")
public void broadcast(String msg) {
synchronized (clients) {
Enumeration<ServeClient> e = clients.elements();
while (e.hasMoreElements()) {
ServeClient serveClient = e.nextElement();
try {
synchronized (serveClient.out) {
serveClient.out.println(msg);
}
} catch (Exception eee) {
serveClient.stop();
}
}
}
}
}
What i get is a NullPointerException when ServeClient invokes run() method
server started
socket Socket[addr=/127.0.0.1,port=51438,localport=8888]
Exception in thread "Thread-0" java.lang.NullPointerException
at com.server.ServeClient.run(ServeClient.java:33)
line 33 is the line with first "try" statement in ServeClient run() method
com.server.ServeClient.run(ServeClient.java:33)
I don't believe that it's happening at the try.
Open up an IDE, turn on debugging, and step through until you can see what's happening. That's the fastest way to figure out what you've missed.
There's an object that you're assuming is fine that is not. Find it.
Here's an example of how to do this properly:
http://www.kodejava.org/examples/216.html
Your problem is with the order in which static instance variables are initialised. Try doing something like:
...
private static Vector clients = null;
...
if (clients==null) {
clients = new Vector(); // consider putting this in a synchronized block
}
before you add the client to the vector.
Sorry for necroing such an old issue but it seemed like this problem wasn't resolved, so I'll give a bit of input from my end.
I've had a similar problem and the compiler also kept telling me that the problem was at the start() method. However, when I commented out the thread part and just ran the code on the same thread as the UI, the compiler directed me to the real source of the problem: the code inside the thread.
After making sure the code didn't give an error, I enclosed the code with the original thread code, and it stopped giving me the NullPointerException error.
Hope this helps someone along the way.
Remove the duplicate class declaration in JPanel.
I was trying to run a timer thread that updated a clock in the main application window.
I had created the JFrame with Eclipse/WindowBuilder and had followed a tutorial on how to make a timer. I had copied the declaration of the textfield into the class declaration to make it available for the entire class, but forgot to remove the Class Id in front of the widget definition. So it still initialized the local instance and not the global one. Thus when I accessed the global one it was still null.

Java: Multithreading & UDP Socket Programming

I am new to multithreading & socket programming in Java. I would like to know what is the best way to implement 2 threads - one for receiving a socket and one for sending a socket. If what I am trying to do sounds absurd, pls let me know why! The code is largely inspired from Sun's tutorials online.I want to use Multicast sockets so that I can work with a multicast group.
class Server extends Thread
{
static protected MulticastSocket socket = null;
protected BufferedReader in = null;
public InetAddress group;
private static class Receive implements Runnable
{
public void run()
{
try
{
byte[] buf = new byte[256];
DatagramPacket pkt = new DatagramPacket(buf,buf.length);
socket.receive(pkt);
String received = new String(pkt.getData(),0,pkt.getLength());
System.out.println("From server#" + received);
Thread.sleep(1000);
}
catch (IOException e)
{
System.out.println("Error:"+e);
}
catch (InterruptedException e)
{
System.out.println("Error:"+e);
}
}
}
public Server() throws IOException
{
super("server");
socket = new MulticastSocket(4446);
group = InetAddress.getByName("239.231.12.3");
socket.joinGroup(group);
}
public void run()
{
while(1>0)
{
try
{
byte[] buf = new byte[256];
DatagramPacket pkt = new DatagramPacket(buf,buf.length);
//String msg = reader.readLine();
String pid = ManagementFactory.getRuntimeMXBean().getName();
buf = pid.getBytes();
pkt = new DatagramPacket(buf,buf.length,group,4446);
socket.send(pkt);
Thread t = new Thread(new Receive());
t.start();
while(t.isAlive())
{
t.join(1000);
}
sleep(1);
}
catch (IOException e)
{
System.out.println("Error:"+e);
}
catch (InterruptedException e)
{
System.out.println("Error:"+e);
}
}
//socket.close();
}
public static void main(String[] args) throws IOException
{
new Server().start();
//System.out.println("Hello");
}
}
First thing is first: your classes should start with a capital letter per the Java Naming Conventions:
Class names should be nouns, in mixed case with the first letter of
each internal word capitalized. Try to
keep your class names simple and
descriptive. Use whole words-avoid
acronyms and abbreviations (unless the
abbreviation is much more widely used
than the long form, such as URL or
HTML).
Second:
Try to break down the code into coherent sections and organize them around some common feature that you're dealing with... perhaps around the functionality or the model you're programming.
The (basic) model for the server is that the only thing it does is receive socket connections... the server relies on a handler to handle those connections and that's it. If you try to build that model it would look something like this:
class Server{
private final ServerSocket serverSocket;
private final ExecutorService pool;
public Server(int port, int poolSize) throws IOException {
serverSocket = new ServerSocket(port);
pool = Executors.newFixedThreadPool(poolSize);
}
public void serve() {
try {
while(true) {
pool.execute(new Handler(serverSocket.accept()));
}
} catch (IOException ex) {
pool.shutdown();
}
}
}
class Handler implements Runnable {
private final Socket socket;
Handler(Socket socket) { this.socket = socket; }
public void run() {
// receive the datagram packets
}
}
Third: I would recommend that you look at some existing examples.
Multi-threaded Client/Server Applications:
http://www.ase.md/~aursu/ClientServerThreads.html
Doug Lea:
http://www.developer.com/java/ent/article.php/3645111/Java-5s-BlockingQueue.htm (thanks to John)
http://gee.cs.oswego.edu/dl/cpj/index.html (still can't find the exact example, but it's there somewhere... if you feel brave look over his allcode.java file).
Concurrency in Practice examples:
http://www.javaconcurrencyinpractice.com/listings.html
Java Concurrency Tutorials:
http://java.sun.com/docs/books/tutorial/essential/concurrency/
Updated per comments:
OK Ravi, there are some big issues with your code and some minor issues with it:
I assume that the Receive class is your client... you should pull that out as a separate program (with its own main class) and run your server and multiple clients at the same time. Spawning a new "client thread" from your server for every new UDP package you send is a disturbing idea (big issue).
When you make your client application, you should make it run the receiving code in its own while loop (minor issue), e.g.:
public class Client extends Thread
{
public Client(/*..*/)
{
// initialize your client
}
public void run()
{
while(true)
{
// receive UDP packets
// process the UDP packets
}
}
public static void main(String[] args) throws IOException
{
// start your client
new Client().start();
}
}
You should only need just one thread per client and one thread per server (you technically don't even a separate thread in there since main has its own thread), so you might not find the ExecutorService that useful.
Otherwise your approach is correct... but I would still recommend that you check out some of examples.
Wanting to create threads in an application is not absurd! You won't need exactly 2 threads, but I think you're talking about 2 classes that implement the Runnable interface.
The threading API has gotten better since Java 1.5 and you don't need to mess with java.lang.Thread anymore. You can simply create a java.util.concurrent.Executor and submit Runnable instances to it.
The book Java Concurrency in Practice uses that exact problem - creating a threaded socket server - and walks through several iterations of the code to show the best way to do it. Check out the free sample chapter, which is great. I won't copy/paste the code here, but look specifically at listing 6.8.
It's a good thing Eclipse's history works even for a day back :) Thanks to that, I am able to give both Ravi a working example and Lirik his answer on leakage.
Let me first start of by stating that I have no clue what is causing this leak, but if I leave it long enough, it will fail on a OutOfMemoryError.
Second, I left the working code commented out for Ravi for a working basic example of my UDP server. The timeout was there to test how long my firewall would kill the receivers end (30 seconds). Just remove anything with the pool, and you're good to go.
So here is, a working but leaking version of my example threaded UDP server.
public class TestServer {
private static Integer TIMEOUT = 30;
private final static int MAX_BUFFER_SIZE = 8192;
private final static int MAX_LISTENER_THREADS = 5;
private final static SimpleDateFormat DateFormat = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss.SSSZ");
private int mPort;
private DatagramSocket mSocket;
// You can remove this for a working version
private ExecutorService mPool;
public TestServer(int port) {
mPort = port;
try {
mSocket = new DatagramSocket(mPort);
mSocket.setReceiveBufferSize(MAX_BUFFER_SIZE);
mSocket.setSendBufferSize(MAX_BUFFER_SIZE);
mSocket.setSoTimeout(0);
// You can uncomment this for a working version
//for (int i = 0; i < MAX_LISTENER_THREADS; i++) {
// new Thread(new Listener(mSocket)).start();
//}
// You can remove this for a working version
mPool = Executors.newFixedThreadPool(MAX_LISTENER_THREADS);
} catch (IOException e) {
e.printStackTrace();
}
}
// You can remove this for a working version
public void start() {
try {
try {
while (true) {
mPool.execute(new Listener(mSocket));
}
} catch (Exception e) {
e.printStackTrace();
}
} finally {
mPool.shutdown();
}
}
private class Listener implements Runnable {
private final DatagramSocket socket;
public Listener(DatagramSocket serverSocket) {
socket = serverSocket;
}
private String readLn(DatagramPacket packet) throws IOException {
socket.receive(packet);
return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(packet.getData())), MAX_BUFFER_SIZE).readLine();
}
private void writeLn(DatagramPacket packet, String string) throws IOException {
packet.setData(string.concat("\r\n").getBytes());
socket.send(packet);
}
#Override
public void run() {
DatagramPacket packet = new DatagramPacket(new byte[MAX_BUFFER_SIZE], MAX_BUFFER_SIZE);
String s;
while (true) {
try {
packet = new DatagramPacket(new byte[MAX_BUFFER_SIZE], MAX_BUFFER_SIZE);
s = readLn(packet);
System.out.println(DateFormat.format(new Date()) + " Received: " + s);
Thread.sleep(TIMEOUT * 1000);
writeLn(packet, s);
System.out.println(DateFormat.format(new Date()) + " Sent: " + s);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
if (args.length == 1) {
try {
TIMEOUT = Integer.parseInt(args[0]);
} catch (Exception e) {
TIMEOUT = 30;
}
}
System.out.println(DateFormat.format(new Date()) + " Timeout: " + TIMEOUT);
//new TestServer(4444);
new TestServer(4444).start();
}
}
btw. #Lirik, I witnessed this behavior first in Eclipse, after which I tested it from the command line. And again, I have NO clue what is causing it ;) sorry...
2 threads is fine. One reader another writer. Remember that with UDP you should not spawn new handler threads (unless what you're doing takes a long time), I recommend throwing the incoming messages into a processing Queue. The same for the send, have a send thread that blocks on an incoming Queue for UDP send.

Using Threads to Handle Sockets

I am working on a java program that is essentially a chat room. This is an assignment for class so no code please, I am just having some issues determining the most feasible way to handle what I need to do. I have a server program already setup for a single client using threads to get the data input stream and a thread to handle sending on the data output stream. What I need to do now is create a new thread for each incoming request.
My thought is to create a linked list to contain either the client sockets, or possibly the thread. Where I am stumbling is figuring out how to handle sending the messages out to all the clients. If I have a thread for each incoming message how can I then turn around and send that out to each client socket.
I'm thinking that if I had a linkedlist of the clientsockets I could then traverse the list and send it out to each one, but then I would have to create a dataoutputstream each time. Could I create a linkedlist of dataoutputstreams? Sorry if it sounds like I'm rambling but I don't want to just start coding this, it could get messy without a good plan. Thanks!
EDIT
I decided to post the code I have so far. I haven't had a chance to test it yet so any comments would be great. Thanks!
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.ServerSocket;
import java.util.LinkedList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class prog4_server {
// A Queue of Strings used to hold out bound Messages
// It blocks till on is available
static BlockingQueue<String> outboundMessages = new LinkedBlockingQueue<String>();
// A linked list of data output streams
// to all the clients
static LinkedList<DataOutputStream> outputstreams;
// public variables to track the number of clients
// and the state of the server
static Boolean serverstate = true;
static int clients = 0;
public static void main(String[] args) throws IOException{
//create a server socket and a clientSocket
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(6789);
} catch (IOException e) {
System.out.println("Could not listen on port: 6789");
System.exit(-1);
}// try{...}catch(IOException e){...}
Socket clientSocket;
// start the output thread which waits for elements
// in the message queue
OutputThread out = new OutputThread();
out.start();
while(serverstate){
try {
// wait and accept a new client
// pass the socket to a new Input Thread
clientSocket = serverSocket.accept();
DataOutputStream ServerOut = new DataOutputStream(clientSocket.getOutputStream());
InputThread in = new InputThread(clientSocket, clients);
in.start();
outputstreams.add(ServerOut);
} catch (IOException e) {
System.out.println("Accept failed: 6789");
System.exit(-1);
}// try{...}catch{..}
// increment the number of clients and report
clients = clients++;
System.out.println("Client #" + clients + "Accepted");
}//while(serverstate){...
}//public static void main
public static class OutputThread extends Thread {
//OutputThread Class Constructor
OutputThread() {
}//OutputThread(...){...
public void run() {
//string variable to contain the message
String msg = null;
while(!this.interrupted()) {
try {
msg = outboundMessages.take();
for(int i=0;i<outputstreams.size();i++){
outputstreams.get(i).writeBytes(msg + '\n');
}// for(...){...
} catch (IOException e) {
System.out.println(e);
} catch (InterruptedException e){
System.out.println(e);
}//try{...}catch{...}
}//while(...){
}//public void run(){...
}// public OutputThread(){...
public static class InputThread extends Thread {
Boolean threadstate = true;
BufferedReader ServerIn;
String user;
int threadID;
//SocketThread Class Constructor
InputThread(Socket clientSocket, int ID) {
threadID = ID;
try{
ServerIn = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
user = ServerIn.readLine();
}
catch(IOException e){
System.out.println(e);
}
}// InputThread(...){...
public void run() {
String msg = null;
while (threadstate) {
try {
msg = ServerIn.readLine();
if(msg.equals("EXITEXIT")){
// if the client is exiting close the thread
// close the output stream with the same ID
// and decrement the number of clients
threadstate = false;
outputstreams.get(threadID).close();
outputstreams.remove(threadID);
clients = clients--;
if(clients == 0){
// if the number of clients has dropped to zero
// close the server
serverstate = false;
ServerIn.close();
}// if(clients == 0){...
}else{
// add a message to the message queue
outboundMessages.add(user + ": " + msg);
}//if..else...
} catch (IOException e) {
System.out.println(e);
}// try { ... } catch { ...}
}// while
}// public void run() { ...
}
public static class ServerThread extends Thread {
//public variable declaration
BufferedReader UserIn =
new BufferedReader(new InputStreamReader(System.in));
//OutputThread Class Constructor
ServerThread() {
}//OutputThread(...){...
public void run() {
//string variable to contain the message
String msg = null;
try {
//while loop will continue until
//exit command is received
//then send the exit command to all clients
msg = UserIn.readLine();
while (!msg.equals("EXITEXIT")) {
System.out.println("Enter Message: ");
msg = UserIn.readLine();
}//while(...){
outboundMessages.add(msg);
serverstate = false;
UserIn.close();
} catch (IOException e) {
System.out.println(e);
}//try{...}catch{...}
}//public void run(){...
}// public serverThread(){...
}// public class prog4_server
I have solved this problem in the past by defining a "MessageHandler" class per client connection, responsible for inbound / outbound message traffic. Internally the handler uses a BlockingQueue implementation onto which outbound messages are placed (by internal worker threads). The I/O sender thread continually attempts to read from the queue (blocking if required) and sends each message retrieved to the client.
Here's some skeleton example code (untested):
/**
* Our Message definition. A message is capable of writing itself to
* a DataOutputStream.
*/
public interface Message {
void writeTo(DataOutputStream daos) throws IOException;
}
/**
* Handler definition. The handler contains two threads: One for sending
* and one for receiving messages. It is initialised with an open socket.
*/
public class MessageHandler {
private final DataOutputStream daos;
private final DataInputStream dais;
private final Thread sender;
private final Thread receiver;
private final BlockingQueue<Message> outboundMessages = new LinkedBlockingQueue<Message>();
public MessageHandler(Socket skt) throws IOException {
this.daos = new DataOutputStream(skt.getOutputStream());
this.dais = new DataInputStream(skt.getInputStream());
// Create sender and receiver threads responsible for performing the I/O.
this.sender = new Thread(new Runnable() {
public void run() {
while (!Thread.interrupted()) {
Message msg = outboundMessages.take(); // Will block until a message is available.
try {
msg.writeTo(daos);
} catch(IOException ex) {
// TODO: Handle exception
}
}
}
}, String.format("SenderThread-%s", skt.getRemoteSocketAddress()));
this.receiver = new Thread(new Runnable() {
public void run() {
// TODO: Read from DataInputStream and create inbound message.
}
}, String.format("ReceiverThread-%s", skt.getRemoteSocketAddress()));
sender.start();
receiver.start();
}
/**
* Submits a message to the outbound queue, ready for sending.
*/
public void sendOutboundMessage(Message msg) {
outboundMessages.add(msg);
}
public void destroy() {
// TODO: Interrupt and join with threads. Close streams and socket.
}
}
Note that Nikolai is correct in that blocking I/O using 1 (or 2) threads per connection is not a scalable solution and typically applications might be written using Java NIO to get round this. However, in reality unless you're writing an enterprise server which thousands of clients connect to simultaneously then this isn't really an issue. Writing bug-free scalable applications using Java NIO is difficult and certainly not something I'd recommend.

Categories

Resources