This is my first post, so correct me if I am doing something wrong.
I am trying to make a multithreaded client/server application. I have no problem making a basic 1 to 1 client/server application without threading, but I want every client to talk individually to the server and each client only to recieve the messages that belongs to the particular client in the console. This is where I can't make it work.
I have looked at some tutorials online and I tried to debug in Eclipse without any luck.
What I want the application to do:
If I run the server and 2 clients I want to be able to write a message on a client and make the server return the message and the message number without skipping a number.
Client 1 :
Message no: 0. You said: Hello!
Message no: 1. You said: Whats up!
Client 2 :
Message no: 0. You said: Hey Server!
Message no: 1. You said: What are you doing now?
It works with the first Client that I run and perfectly increment the messagenumber, but when I create the second one it freezes, so I guess my problem is either with the socket declaration or with the way I am threading. (Maybe a socket.close() is missing somewhere?)
I know there is some other flaws with the application, but keep in mind that it is just a school assignment for working with threads.
Thanks in advance if someone is willing to help. :)
Server Code:
public class Server extends Thread
{
DataInputStream in;
DataOutputStream out;
ServerSocket listener;
public Server() throws IOException
{
try
{
while (true)
{
listener = new ServerSocket(9090);
Socket socket = listener.accept();
System.out.println("Test");
ThreadServer ts = new ThreadServer(socket);
Thread t1 = new Thread(ts);
t1.start();
}
}
finally
{
listener.close();
}
}
public static void main(String[] args) throws IOException
{
new Server();
}
}
Client code:
public class Client extends JFrame
{
DataOutputStream dos;
BufferedReader input;
String answer = null;
String textString;
Socket s = new Socket("localhost", 9090);
public Client() throws IOException
{
JButton btn = new JButton("run");
final JTextField txf = new JTextField(10);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
try
{
dos = new DataOutputStream(s.getOutputStream());
textString = txf.getText();
dos.writeUTF(textString);
dos.flush();
input = new BufferedReader(new InputStreamReader(s.getInputStream()));
answer = input.readLine();
}
catch (Exception e1)
{
e1.printStackTrace();
}
System.out.println(answer);
}
});
setLayout(new GridLayout(2,2));
add(btn);
add(txf);
setVisible(true);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) throws IOException
{
new Client();
}
}
ThreadServer code:
public class ThreadServer implements Runnable
{
DataInputStream in;
DataOutputStream out;
public ThreadServer (Socket socket) throws IOException
{
int i = 0;
try
{
while (true)
{
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
try
{
String message = in.readUTF();
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("Message no: " + i + ". You said: " + message);
out.println("Message no: " + i);
}
catch(Exception e)
{
e.printStackTrace();
}
i++;
}
}
finally
{
socket.close();
}
}
#Override
public void run() {
// TODO Auto-generated method stub
}
}
Let me try to solve it.
Create only one ServerSocket. Move below line from while loop. Put it before while loop.
listener = new ServerSocket(9090);
Move the code of ThreadServer's constructor to run() method because it implements Runnable but doesn't behave like a Runnable class.
public ThreadServer(Socket socket) throws IOException {
this.socket=socket;
}
#Override
public void run() {
...
//code from constructor
}
In Server class you are writing two lines to Client but client is reading just one. Remove extra line as shown below
out.println("Message no: " + i + ". You said: " + message);
//out.println("Message no: " + i);
One last suggestion:
Always check for inputs before calling readUTF() as shown below:
if (in.available() > 0) {
try {
String message = in.readUTF();
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("Message no: " + i + ". You said: " + message);
//out.println("Message no: " + i);
} catch (Exception e) {
e.printStackTrace();
}
i++;
}
Your ThreadServer is looping infinitely in the constructor, instead of the run method.
This is very bad. The run method is where you should put that code.
You should also follow the advice in the comment and move the socket creation.
-edit-
same is true for your ThreadServer, don't do the socket init in the loop. just the reading.
You might also discover later that you need an extra thread for the client. As it is now
it seems each client can only receive messages when the action performed button is clicked,
and that is probably not what you want. This will expose some synchronization problems
that you will have to deal with, but most you gui library might actually help you there.
It was a long time since I dealt with swing.
-edit again-
In addition, read up on exception handling. You should decide where you want
to catch the IO exceptions, rather than randomly nesting try/catch and declaring methods
as throwing them.
Related
I am new at stackoverflow and I am sorry if this kind of a question is asked before but did a quick search and I could not find any title like mine. I am working on a multi-client chat application on Java. I was following the tutorials and I can send messages that every user in the application can see. But I wonder how to create and send a private message to a spesific user into the chat.
import java.io.*;
import java.net.*;
import java.util.HashSet;
import java.util.Set;
public class ChatServer {
private int port;
private Set<String> userNames = new HashSet<>();
private Set<UserThread> userThreads = new HashSet<>();
public ChatServer(int port) {
this.port = port;
}
public static void main(String[] args) {
new ChatServer(9999).execute();
}
private void execute() {
try {
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("Server is running");
while (true) {
Socket socket = serverSocket.accept();
System.out.println("New user connected");
UserThread newUser = new UserThread(socket, this);
userThreads.add(newUser);
newUser.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void addUserName(String s) {
this.userNames.add(s);
}
public void broadcast(String serverMessage, UserThread excludeUser) {
for (UserThread aUser : userThreads) {
if (aUser != excludeUser)
aUser.sendMessage(serverMessage);
}
}
}
The code above is my server code.
public void run() {
Console console = System.console();
String userName = console.readLine("Enter your username : ");
writer.println(userName);
String text;
do {
text = console.readLine("[" + userName + "]: ");
if (text.startsWith("[")) {
isTargeted = true;
this.aimUserName = text.substring(text.indexOf("[") + 1, text.indexOf("]"));
//System.out.println("Private Message to: " + aimUserName);
} else {
isTargeted = false;
}
writer.println(text);
} while (!text.equals("bye"));
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
and this code above is a part of my write thread class. As you can see, if a message starts with '[name]' part, the "name" means the user that we want to send a private message. By doing this, I can get the name of the user that I want to send a private message but I could not figure out how to broadcast this message just to that spesific user. I believe I need to configure my broadcast function in ChatServer class but I don't really know how to do. What steps should I follow?
--Edit--
I've been working on my question and I did some additions to solve my problem. First of all, I think I should share everything I have to you. I shared my ChatServer class previously. Other classes I have are:
import java.io.IOException;
import java.net.Socket;
public class ChatClient {
public static void main(String[] args) {
new ChatClient().execute();
}
private void execute() {
try {
Socket socket = new Socket("localhost", 3);
System.out.println("Connected to chat server");
new ReadThread(socket, this).start();
new WriteThread(socket, this).start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
import java.net.*;
import java.io.*;
public class ReadThread extends Thread{
private BufferedReader reader;
private Socket socket;
private ChatClient client;
public ReadThread(Socket socket, ChatClient client) {
this.socket = socket;
this.client = client;
InputStream input;
try {
input = this.socket.getInputStream();
this.reader = new BufferedReader(new InputStreamReader(input));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
while(true) {
try {
String response = this.reader.readLine();
System.out.println("\n" + response);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
}
}
}
import java.net.Socket;
import java.io.*;
public class UserThread extends Thread {
private Socket socket;
private ChatServer server;
PrintWriter writer = null;
public String userName;
public UserThread(Socket socket, ChatServer chatServer) {
this.socket = socket;
this.server = chatServer;
}
public void run() {
try {
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
OutputStream output = socket.getOutputStream();
writer = new PrintWriter(output,true);
String userName = reader.readLine();
this.userName = userName;
server.addUserName(userName);
String serverMessage = "New user connected: " + userName;
server.broadcast(serverMessage,this);
String clientMessage;
do {
clientMessage = reader.readLine();
serverMessage = "[" + userName + "] : " + clientMessage;
server.broadcast(serverMessage, this);
}while(!clientMessage.equals("bye"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void sendMessage(String serverMessage) {
writer.println(serverMessage);
}
}
import java.net.*;
import java.io.*;
public class WriteThread extends Thread {
private Socket socket;
private ChatClient client;
private PrintWriter writer;
public WriteThread(Socket socket, ChatClient client) {
this.socket = socket;
this.client = client;
OutputStream output;
try {
output = socket.getOutputStream();
this.writer = new PrintWriter(output, true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
Console console = System.console();
String userName = console.readLine("Enter your username : ");
writer.println(userName);
String text;
do {
text = console.readLine("[" + userName + "]: ");
if(text.startsWith("[")){
String aimUserName = text.substring(text.indexOf("[")+1,text.indexOf("]"));
System.out.println("Private Message to: " + aimUserName);}
writer.println(text);
}while(!text.equals("bye"));
/*do {
text = console.readLine("[" + userName + "]: ");
writer.println(text);
}while(!text.equals("bye"));*/
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
These codes work properly and I can multi-chat very clean. But while working on private chat stuff, I added to the ChatServer the line of:
public void privatebr(String serverMessage, String targetUserName){
for(UserThread aUser: userThreads){
if(aUser.userName == targetUserName)
aUser.sendMessage(serverMessage);
}
to the UserThread, I edited the part as:
String clientMessage;
do {
clientMessage = reader.readLine();
serverMessage = "[" + userName + "] : " + clientMessage;
if(clientMessage.startsWith("[")){
String targetUserName = clientMessage.substring(clientMessage.indexOf("[")+1,clientMessage.indexOf("]"));
serverMessage = "[" + userName + "] : " + clientMessage;
server.privatebr(serverMessage, targetUserName);
}else{
server.broadcast(serverMessage, this);
}
}while(!clientMessage.equals("bye"));
But when I did all these edits, the normal multi-chat progress became broken where is my fault? Why everything has broken?
Good question! To answer the question you asked is that you should maintain a Map of Users to their Socket connections, so that way with DMs you can just select the user(s) that you want to message. You will also need a messaging protocol for that (see below)
...But I have to tell you that using Sockets and SocketServer classes in today's day and age is like re-inventing the wheel. The place to start in doing a chat server is using the web sockets protocol. Even under this, you will probably want to define a message protocol (like I did - I created a messaging protocol using JSON and message types, where the string message in the websocket event onMessage first gets parsed into an object)
There are implementations for supporting WS on all platforms: java, .net, python, php etc. This should be your starting point.
--- Update ---
I understand where you are coming from. To help you along in understanding Sockets / ServerSockets, here are a couple of pointers & resources
DatagramSockets (aka UDP): This is a different transmission protocol than the regular TCP, used by Shockwave and then Flash, and is the fundamental reason that Flash is problematic. I strongly recommend against this
Data & Object Input/OutputStreams: "Data" streams are Java only (can't connect to technologgy built on other platforms). Object streams are similar, except you are transporting actual whole objects through the stream (also Java only) No one* (almost no one) uses these anymore.
SocketException: Using java.net.[Server]Socket(s), you are likely to encounter this exception. It happens when you are waiting for more data (through a read / readLine call) on a socket, and the socket closes. It took me a long time to figure this out, but THIS EXCEPTION IS YOUR FRIEND! You get it when the connection has closed (either on the client or server side). It allows the thread that was waiting on the socket to wake up, and allows you to do whatever clean-up you need to do. SocketException is a subclass of IOException, so you may not even realize what this is. But now at least I have warned you
Streams vs. Writers and Readers: Writers and Readers are for interpreting raw bytes as Java characters and Strings. This is necessary, as there are multiple text formats (i.e. ascii, windows-xx, utf-8, utf-16). Readers and Writers help you read and write text in different text formats (and also interpreting Images from image formats).
Buffered Writers and Streams: These are for INEFFICIENT reading and writing. For writing, this means enabling you to write part of a message and not send it until you are ready. For reading, this means reading streams line by line for example rather than reading everything at one go.
TUS: tjacobs/io - https://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/io/ this is an old collection of Java libraries I put on SourceForge years ago, but a lot of the classes here pertain to dealing with Sockets. In particular, see SocketServerEx, DataFetcher, Main/App, Timeout, and maybe IOUtils. And of everything, really look at DataFetcher which is a lightweight threadableframework for Callback I/O listening.
Good luck and have fun!
I am writing a Java client/server GUI application using sockets and here is the problem:
I have a button to start listening for a specified port:
button actionPerformed method
private void listenButtonActionPerformed(java.awt.event.ActionEvent evt) {
int port = Integer.parseInt(portTextfield.getText(), 10);
try {
socket.listen(port);
} catch (IOException ex) {
}
}
Here is the socket.listen method
public static void listen() throws IOException {
ServerSocket ss = new ServerSocket(port);
while (true)
new socket(ss.accept());
}
"socket" class extends "Thread"
So after ss.accept() returns a value it creates new socket instance in separate thread.
After clicking the button the GUI freezes because inside the socket.listen method there is an infinite loop. How can I avoid that?
You have two pitfalls in your design:
ss.accept() is a blocking call so your UI will freeze until there is an incoming connection
Never run while(true) loops in the EDT.
Instead do the following:
When the button is clicked create a thread that will start listening for incoming connections.
Whenever you have an incoming connection, create another thread that will take the incoming client connection and deal with it.
as long as your
new socket(ss.accept());
returns immediately, you only need to change your
while (true)
this puts the EDT (Event Dispatch Thread) into an infinite loop and your GUI becomes irresponsive. So, delete this line.
If you can't then use the SwingWorker class ( http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html#process(java.util.List)
Create a nested class that extents SwingWorker. Just call a swingWoker.execute(); (after you have created its object) in your listenButtonActionPerformed(java.awt.event.ActionEvent evt) method.
See the tutorial: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Never create a new thread and run it from from the Swing EDT
Check this out: http://javarevisited.blogspot.ro/2012/02/what-is-blocking-methods-in-java-and.html
1) If you are writing GUI application may be in Swing never call
blocking method in Event dispatcher thread or in the event handler.
for example if you are reading a file or opening a network connection
when a button is clicked don't do that on actionPerformed() method,
instead just create another worker thread to do that job and return
from actionPerformed(). this will keep your GUI responsive, but again
it depends upon design if the operation is something which requires
user to wait than consider using invokeAndWait() for synchronous
update.
Using multiple threads: http://javarevisited.blogspot.ro/2011/02/how-to-implement-thread-in-java.html
You will need to use Multi-Threading. If I where you, I would separate the GUI code and the server code and when the button is pressed, I simply launch the Server code as a new Thread.
Your code is freezing the GUI basically because all events are executed on the Event Dispatcher Thread (EDT) which is the thread which takes care of all your GUI stuff and respective events. If you either block it, stop it or throw in loops it will affect on its performance.
Try these...
1. During getting the initial connection delay can occur, so first create and empty socket,then try to connect to the server.
`Socket s = new Socket();`
`s.connect(new InetSocketAddress("ip_addr",port_nos),1000);`
2. And Secondly always keep the Non-UI work out of Your UI thread..
Here is my Example of Server - Client Communication..
Client side code:
public class ClientWala {
public static void main(String[] args) throws Exception{
Boolean b = true;
Socket s = new Socket();
s.connect(new InetSocketAddress("127.0.0.1", 4444),1000);
System.out.println("connected: "+s.isConnected());
OutputStream output = s.getOutputStream();
PrintWriter pw = new PrintWriter(output,true);
// to write data to server
while(b){
if (!b){
System.exit(0);
}
else {
pw.write(new Scanner(System.in).nextLine());
}
}
// to read data from server
InputStream input = s.getInputStream();
InputStreamReader isr = new InputStreamReader(input);
BufferedReader br = new BufferedReader(isr);
String data = null;
while ((data = br.readLine())!=null){
// Print it using sysout, or do whatever you want with the incoming data from server
}
}
}
Server side code:
import java.io.*
import java.net.*;
public class ServerTest {
ServerSocket s;
public void go() {
try {
s = new ServerSocket(44457);
while (true) {
Socket incoming = s.accept();
Thread t = new Thread(new MyCon(incoming));
t.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
class MyCon implements Runnable {
Socket incoming;
public MyCon(Socket incoming) {
this.incoming = incoming;
}
#Override
public void run() {
try {
PrintWriter pw = new PrintWriter(incoming.getOutputStream(),
true);
InputStreamReader isr = new InputStreamReader(
incoming.getInputStream());
BufferedReader br = new BufferedReader(isr);
String inp = null;
boolean isDone = true;
System.out.println("TYPE : BYE");
System.out.println();
while (isDone && ((inp = br.readLine()) != null)) {
System.out.println(inp);
if (inp.trim().equals("BYE")) {
System.out
.println("THANKS FOR CONNECTING...Bye for now");
isDone = false;
s.close();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
try {
s.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new ServerTest().go();
}
}
I am looking at the following documentation in hopes of understanding uniData. The problem that I am experiencing is that I am unable to make a "simulated real-like version" of a backend server inorder to test my unidata queries.
Here is what I have so far:
I have a main entry into the server, to listen:
public static void main(final String[] args) throws UnknownHostException, IOException {
int clientNumber = 0;
final ServerSocket listener = new ServerSocket(31438);
try {
while (true) {
new MySimulatedConnection(listener.accept(), clientNumber++).start();
}
} finally {
listener.close();
}
}
The SimulatedConnection receives requests, and attempts to read some data from them. (Purely using sockets)
#Override
public void run() {
try {
final BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
final PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
while (true) {
final String input = in.readLine();
if (input == null || input.equals(".")) {
break;
}
out.print("## received message " + input);
}
} catch (final IOException e) {
System.out.println("Error handling client# " + requestNumber + ": " + e.getMessage());
} finally {
try {
socket.close();
} catch (final IOException e) {
System.out.println("## cant close socket " + e.getMessage());
}
}
}
On my client, I am establishing a uniData session with something like this:
public static void main(final String[] args) throws UnknownHostException, IOException, UniSubroutineException,
UniSessionException {
final FakeClient faker = new FakeClient();
faker.createConnection();
}
private void createConnection() throws UnknownHostException, IOException, UniSubroutineException,
UniSessionException {
System.out.println("Connecting to unidata...");
UniSession uSession = new UniSession();
final UniJava uj = new UniJava();
uSession = uj.openSession();
uSession.setHostName("127.0.0.1");
uSession.setUserName("sample_username");
uSession.setPassword("sample_password");
uSession.setAccountPath("some_account");
uSession.setTimeout(5000);
final int port = uSession.connection.getPort();
System.out.println("Testing connection to " + SERVER_ADDRESS + " on port " + port + "...");
final Socket testSocket = new Socket(SERVER_ADDRESS, port);
if (testSocket.isConnected()) {
System.out.println("Connection successful");
uSession.connect();
System.out.println("## we performed the connect");
final UniSubroutine sub = uSession.subroutine("UNIJAVA.SETUP", 3);
sub.setArg(0, "unknwon host");
sub.call();
System.out.println("## sub called");
} else {
System.out.println("### no connection possbile!");
}
}
Why am I doing it the way I am?
Well, I am experimenting to see how I can create a server and how I can query for some data out of it. Obviously, my first choice is to use socketing as this seems to be the basic principle of communication here.
What is working, and what is not?
So far, the client is able to connect to the tcp socket, and read this line:
System.out.println("Connection successful");
However, connecting using uniApi, yeilds no success and a long timeout (since that is the way i configured it in the uSession.setTimeout(5000); value :
uSession.connect();
In addition, my buffered reader reads no data on the server, as I am guessing the subroutine paramaters I am passing are actually not being called as strings.
With that in mind, what am I trying to do?
I want to update my server to accept uniAPI requests, and act as an actual server that can perform some actions. However, it is obvious that I am missing in implementing possibly some interface, or some connection management, inorder to get this to work. Any idea as to what that may be?
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.
I've written a simple application in Java where there are two nodes, each with a ServerSocket open to a port listening for incoming connections. The nodes run two threads each, sending 1000 messages to the other node through a persistent TCP socket created when sending the first message. However, the nodes do not receive all 1000 messages. One may receive 850 while the other only receives 650. This number tends to stay constant over multiple runs.
The sending code is as follows:
public void SendMsg(String dest, Message myMsg) {
Socket sendsock = null;
PrintWriter printwr = null;
try {
if(printwr == null) {
sendsock = new Socket(dest, Main.rcvport);
printwr = new PrintWriter(sendsock.getOutputStream(), true);
}
String msgtosend = myMsg.msgtype.toString() + "=" + Main.myaddy + "=" + myMsg.content + "\n";
printwr.print(msgtosend);
} catch (UnknownHostException ex) {
System.out.println(ex);
//DO: Terminate or restart
} catch (IOException ex) {
System.out.println(ex);
//DO: Terminate or restart
}
}
Performance seems to improve if I use
buffwr = new BufferedWriter(printwr)
as well and use buffwr.write(...) instead of printwr.print(...), though it doesn't seem to be a complete solution for the data loss. There are no exceptions to show that packets weren't delivered, so according to the sender, they were all sent successfully.
On the receiving end, the accepted connection is treated as follows:
BufferedReader inbuff = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
while(running) {
String rcvedln = inbuff.readLine();
if(rcvedln != null) {
count++;
System.out.println(count);
}
}
Is there an problem with how the readers and writers have been used that could be causing the problem? Thanks.
SendMsg() is creating a new socket every call, so you aren't using a persistent TCP connection. The method isn't closing the socket, either, so you have a lot of open collections. You may be reaching a limit to the number of connections the process can make (the sockets may not be closed when the objects are garbage collected).
Finally, as kd304 pointed out, the Javadoc for PrintWriter states this about the autoFlush parameter of the PrintWriter constructor: "if true, the println, printf, or format methods will flush the output buffer". Your code wasn't calling a method that did a flush.
Try this:
public class MessageSender implements Closeable {
private final Socket socket;
private final PrintWriter writer;
public MessageSender(String dest, int port) {
socket = new Socket(dest, port);
writer = new PrintWriter(socket.getOutputStream(), true);
}
public void sendMessage(Message message) {
try {
writer.println(message.toString());
} catch (UnknownHostException ex) {
System.out.println(ex);
//DO: Terminate or restart
} catch (IOException ex) {
System.out.println(ex);
//DO: Terminate or restart
}
}
#Override
public void close() throws IOException {
writer.close();
socket.close();
}
Note I modified the code so that sendMessage() calls Message.toString() to get the formatted message. It doesn't seem right for sendMessage() to reference fields in Message in order to format the message. Instead of using toString() you could create a method in Message specifically for this purpose.
Here's the server side code:
public class Server implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService executor;
private volatile boolean running = true;
public Server(int port, ExecutorService executor) throws IOException {
serverSocket = new ServerSocket(port);
this.executor = executor;
}
#Override
public void run() throws IOExeption {
while (running) {
Socket socket = serverSocket.accept();
executor.execute(new ConnectionHandler(socket));
}
}
public boolean stop(long timeout, TimeUnit unit) {
running = false;
executor.shutdown();
return executor.awaitTermination(timeout, unit);
}
}
You can use Executors to create an ExecutorService to run the tasks. Note that ConnectionHandler needs to close the socket it is given.
Are you closing out the PrintWriter to flush the stream?
} finally {
printwr.close();
sendsock.close();
}
Ah, sorry. I accidentally removed the commenting from the code. It's actually like this:
public void SendMsg(String dest, Message myMsg) {
Socket sendsock = null;
try {
if(printwr == null) {
sendsock = new Socket(dest, Main.rcvport);
printwr = new PrintWriter(sendsock.getOutputStream(), true);
}
String msgtosend = myMsg.msgtype.toString() + "=" + Main.myaddy + "=" + myMsg.content + "\n";
printwr.print(msgtosend);
} catch (UnknownHostException ex) {
System.out.println(ex);
//DO: Terminate or restart
} catch (IOException ex) {
System.out.println(ex);
//DO: Terminate or restart
}
}
printrw is declared and stored outside the function, so once it's set up, there is no need for sendsock or for reinitializing printrw. In the actual application, I'm storing the PrintWriter for every connection in a HashMap and retrieving it at the start of the SendMsg(...) function.
Since the connections are persistent, every time one is accepted, a new thread is lunch that runs a while loop to check it continuously for data. These threads and connections are only closed once the application is terminated. In addition to my previous question, is there a more efficient way of doing this?
Earlier, I'd implemented this code without the "\n" and using println(...) instead and I still had the issue of some messages not being received, so I'm not sure what is causing the problem. The messages are sent like so:
public class SendPortal2 implements Runnable {
String dest = null;
SendPortal2 (String dest) {
this.dest = dest;
}
public void run() {
for(int i=1; i<1000; i+=2) {
Message myMsg = new Message("Message", Main.myaddy + " " + String.valueOf(i));
Main.myCommMgr.SendMsg(dest, myMsg);
}
}
}
There are two such threads running. When I ran the code again just now, one side got 999 packets whereas the other one only got 500, leading me to believe sometimes the data from an entire thread could be blocked out. Is that likely?
Thanks for the replies!
If I put a Thread.sleep(2) inside the for loop where the SendMsg function is called, more messages are received properly, but it's not always 1000. Could it be possible that the system's resources are being hogged by two threads running while loops continuously?