I am trying to run a multi-user chat client java programme as part of another java programme.
How do I implement it in such a way that I can open up the chat client from the main programme? I have attempted to start it using ProcessBuilder but it causes the whole programme to crash.
The class to start the chat client and the client client itself is shown below respectively
--------------------- Class to start chat client ---------------------
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
public class ChatCommand extends Command {
public static final String COMMAND_WORD = "chat";
public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Opens up a separate chat programme\n\t"
+ "Example: " + COMMAND_WORD;
public static final String MESSAGE_SUCCESS = "Initialising chat!";
public static void main(String[] args) {
new ChatCommand();
}
public ChatCommand() {
try {
int result = compile("seedu.addressbook.communications.ChatClient");
System.out.println("javac returned " + result);
result = run("seedu.addressbook.communications.ChatClient");
} catch (IOException | InterruptedException ex) {
ex.printStackTrace();
}
}
public int run(String clazz) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("java", clazz);
pb.redirectError();
pb.directory(new File("src"));
Process p = pb.start();
InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
consumer.start();
int result = p.waitFor();
consumer.join();
System.out.println(consumer.getOutput());
return result;
}
public int compile(String file) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("javac", file);
pb.redirectError();
pb.directory(new File("src"));
Process p = pb.start();
InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
consumer.start();
int result = p.waitFor();
consumer.join();
System.out.println(consumer.getOutput());
return result;
}
public class InputStreamConsumer extends Thread {
private InputStream is;
private IOException exp;
private StringBuilder output;
public InputStreamConsumer(InputStream is) {
this.is = is;
}
#Override
public void run() {
int in = -1;
output = new StringBuilder(64);
try {
while ((in = is.read()) != -1) {
output.append((char) in);
}
} catch (IOException ex) {
ex.printStackTrace();
exp = ex;
}
}
public StringBuilder getOutput() {
return output;
}
public IOException getException() {
return exp;
}
}
public CommandResult execute() {
ChatClient cc = new ChatClient();
try {
cc.main(new String[]{"a", "b"});
} catch (Exception e) {
System.out.println("aaa");
}
commandHistory.addHistory(COMMAND_WORD);
return new CommandResult(MESSAGE_SUCCESS);
}
}
--------------------------- Chat Client --------------------------
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/* A simple Swing-based client for the chat server. Graphically
* it is a frame with a text field for entering messages and a
* textarea to see the whole dialog.
*
* The client follows the Chat Protocol which is as follows.
* When the server sends "SUBMITNAME" the client replies with the
* desired screen name. The server will keep sending "SUBMITNAME"
* requests as long as the client submits screen names that are
* already in use. When the server sends a line beginning
* with "NAMEACCEPTED" the client is now allowed to start
* sending the server arbitrary strings to be broadcast to all
* chatters connected to the server. When the server sends a
* line beginning with "MESSAGE " then all characters following
* this string should be displayed in its message area.
*/
public class ChatClient {
private BufferedReader in;
private PrintWriter out;
private JFrame frame = new JFrame("MediChat");
private JTextField textField = new JTextField(40);
private JTextArea messageArea = new JTextArea(8, 40);
/* Constructs the client by laying out the GUI and registering a
* listener with the textfield so that pressing Return in the
* listener sends the textfield contents to the server. Note
* however that the textfield is initially NOT editable, and
* only becomes editable AFTER the client receives the NAMEACCEPTED
* message from the server.
*/
public ChatClient() {
// Layout GUI
textField.setEditable(false);
messageArea.setEditable(false);
frame.getContentPane().add(textField, "North");
frame.getContentPane().add(new JScrollPane(messageArea), "Center");
frame.pack();
// Add Listeners
textField.addActionListener(new ActionListener() {
/* Responds to pressing the enter key in the textfield by sending
* the contents of the text field to the server. Then clear
* the text area in preparation for the next message.
*/
public void actionPerformed(ActionEvent e) {
out.println(textField.getText());
textField.setText("");
}
});
}
/* Prompt for and return the address of the server.
*/
private String getServerAddress() {
return JOptionPane.showInputDialog(
frame,
"Enter IP Address of the Server:",
"Welcome to MediChat!",
JOptionPane.QUESTION_MESSAGE);
}
/* Prompt for and return the desired screen name.
*/
private String getName() {
return JOptionPane.showInputDialog(
frame,
"Choose a screen name:",
"Screen name selection",
JOptionPane.PLAIN_MESSAGE);
}
/* Connects to the server then enters the processing loop.
*/
private void run() throws IOException {
// Make connection and initialize streams
String serverAddress = getServerAddress();
Socket socket = new Socket(serverAddress, 9001);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Process all messages from server, according to the protocol.
while (true) {
String line = in.readLine();
if (line.startsWith("SUBMITNAME")) {
out.println(getName());
} else if (line.startsWith("NAMEACCEPTED")) {
textField.setEditable(true);
} else if (line.startsWith("MESSAGE")) {
messageArea.append(line.substring(8) + "\n");
}
}
}
/**
* Runs the client as an application with a closeable frame.
*/
public static void main(String[] args) throws Exception {
ChatClient client = new ChatClient();
client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.frame.setVisible(true);
client.run();
}
}
You are seriously overcomplicating things:
don't make compiling your client code part of any Java class. Define a project setup in your IDE, or on the command line using gradle for example. Then use that to separately compile your classes whenever you change something. Running javac in your classes manually is seriously wrong!
and then, just make sure that all compiled class files are available on the classpath of your jvm. Don't bother to use reflection or anything else that is based on class names as raw strings.
most importantly: you use other classes by instantiating objects directly. The main method should only be used when you want to run a class standalone from the command line!
so instead of using class names as string, simply normally import the classes to use, and use new to create objects of them.
beyond that, separate your concerns. The client is the client, the server is the server. It is absolutely not a good idea to have the server start client instances. Meaning: rather create a third class, maybe called SetupTestEnvironment that first starts the server and a few clients for testing purposes.
Related
I have two doubts
FIRST.
I am creating a desktop application in netbeans, I want to open a port entered by user.I have created two files in same package, getting port number from user its in one file and processing on it is second file.
I have created object of that class for getting user's entered port number its not showing any error but port is not opening on that number i have checked using tcp view
here is code of first file
Server.java
package server;
import java.io.*;
import java.net.*;
import javax.swing.JFrame;
public class Server extends Thread {
public static int SERVERPORT;
private boolean running = false;
public volatile boolean stop = false;
public Socket client = null;
public static void main(String[] args) {
mainFrame frame = new mainFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
SERVERPORT = frame.portnum;//here i am getting port number from 2nd file
}
#Override
public void run() {
super.run();
running = true;
try {
System.out.println("Server Has Started........ \n Waiting for client........");
ServerSocket serverSocket = new ServerSocket(SERVERPORT);
try {
while (!stop && running) {
client = serverSocket.accept();
System.out.println("Connection Accepted......");
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
String usercmnd = in.readLine();
if (usercmnd != null) {
Runtime runtime = Runtime.getRuntime();
Process p = runtime.exec(usercmnd);
}
}
here is 2nd file where user have to enter a port number
mainFrame.java
package server;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.net.*;
//import server.Server;
/**
*
* #author admin
*/
public class mainFrame extends JFrame {
private Server mServer;
public int portnum;
public mainFrame() {
super("mainFrame");
mServer = new Server();
initComponents();
setIcon();labInfo.setText("Not Connected");
try{ ipAdd.setText(String.valueOf(InetAddress.getLocalHost().getHostAddress()));
}catch(Exception e){labInfo.setText(e.getMessage());}
}
private void connActionPerformed(java.awt.event.ActionEvent evt) {
if(port.getText().equals(""))
{
labInfo.setText("Port number cannot be empty!!");
}else{
portnum=Integer.parseInt(port.getText());//here i am getting user's entered port number
conn.setEnabled(false);port.setEditable(false);
labInfo.setText("Waiting for Connection.....");
mServer.start();
}
}
private void disconnActionPerformed(java.awt.event.ActionEvent evt) {
mServer.requestStop();
labInfo.setText("Not Connected");
port.setEditable(true);
conn.setEnabled(true);
}
SECOND.
Is there any way to check entered port number is currently using so then we can alert the user to use another port number...???
I would rather suggest you run the mainFrame class first from which you can call the Server class at the click of the Button to retrieve the port number entered in the TextField. I can't seem to help with the full code but you can change your connActionPerformed() method to
private void connActionPerformed(java.awt.event.ActionEvent evt) {
boolean success = false;
do {
try {
if (port.getText().equals("")) {
labInfo.setText("Port number cannot be empty!!");
} else {
portnum = Integer.parseInt(port.getText());//here i am getting user's entered port number
ServerSocket serverSocket = new ServerSocket(portnum);
System.out.println("Connected to Server");
mServer = new Server(serverSocket);//Which means you need to implement a constructor with a Server argument.
//conn.setEnabled(false);
//port.setEditable(false);
//abInfo.setText("Waiting for Connection.....");
mServer.start();
success = true;
}
} catch (BindException ex) {
System.out.println("Port in use");
}
} while (success);
}
And then you can implement a constructor like this:
ServerSocket serverSocket;
public Server(ServerSocket serverSoket){
this.serverSocket = serverSoket;
}
NOTE: since this code is not complete, manipulate to suit your needs.
Hope this would be helpful, thank you.
I have a multi threaded server in which server is waiting for all possible clients to be connected.As soon as client is connected it sends an int(12345) to server and server reads and display it now server uses a specific IPaddress of a client using hash map architecture sends a message to that client to which ipaddress is matched.but my code is stuck in while loop and it isn't go to the function messagetospecificclient() and if it goes it displays null. Sorry for my bad English
My code is
Server
public class ServerStart implements Runnable
{
#Override
public void run()
{
try
{
HandleMultipleClients hmc=new HandleMultipleClients();
hmc.connect();
hmc.messagetospecificclients("172.20.3.122");
}
}
HandleMultipleClients
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package zeeshannisar210;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
*
* #author Zeeshan Nisar
*/
public class HandleMultipleClients{
Map<Integer, java.net.Socket> clients = new HashMap<Integer, java.net.Socket> ();
Socket sock;
ServerSocket serverSock;
DataOutputStream dos;
DataInputStream dis;
String ip;
public HandleMultipleClients()
{
}
public void connect()
{
try
{
serverSock = new ServerSocket(2101);
while (true)
{
sock = serverSock.accept();
clients.put(sock.getPort(), sock);
dis=new DataInputStream(sock.getInputStream());
int s=dis.readInt();
System.out.print(s);
messagetospecificclients(ip);
}
}
catch(Exception e)
{
}
}
public void messagetospecificclients(String ipaddress) throws IOException
{
System.out.print(ipaddress);
for (Iterator<Integer> iter = clients.keySet().iterator(); iter.hasNext(); )
{
int key = iter.next();
System.out.print("ok1");
ip=ipaddress;
System.out.print(ip);
System.out.print("ok2");
java.net.Socket client = clients.get(key);
InetAddress zee = client.getInetAddress();
String s = zee.getHostAddress();
System.out.print("ok3");
System.out.print(s);
if (s.equals(ipaddress))
{
System.out.print("ok4");
dos =new DataOutputStream(client.getOutputStream());
dos.writeUTF("Some message");
}
}
}
public static void main(String[] args) {
new HandleMultipleClients();
}
}
Client code is
public class messagefromserver implements Runnable
{
#Override
public void run()
{
try
{
sock = new Socket("localhost",2101);
System.out.println("Success");
dos=new DataOutputStream(sock.getOutputStream());
dos.writeInt(12345);
// Basicinfosend bis=new Basicinfosend(sock);
// Thread t1=new Thread(bis);
// t1.start();
// Thread.sleep(1000);
// Systeminfosend sis=new Systeminfosend(sock);
// Thread t2=new Thread(sis);
// t2.start();
// Thread.sleep(1000);
// Thread p = new Thread(new Process());
// p.start();
while(true)
{
String s=dis.readUTF();
System.out.print(s);
}
}
I found this Client-Server chat code online but would like to modified it to show the list of clients connected to to each client this way users know who is connected and who has left the chat.
client class:
package edu.lmu.cs.networking;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
*
* #author PUTA
* The client follows the Chat Protocol which is as follows.
* When the server sends "SUBMITNAME" the client replies with the
* desired screen name. The server will keep sending "SUBMITNAME"
* requests as long as the client submits screen names that are
* already in use. When the server sends a line beginning
* with "NAMEACCEPTED" the client is now allowed to start
* sending the server arbitrary strings to be broadcast to all
* chatters connected to the server. When the server sends a
* line beginning with "MESSAGE " then all characters following
* this string should be displayed in its message area.
*/
public class ChatClient {
BufferedReader in;
PrintWriter out;
JFrame frame = new JFrame("InstantMessenger");
JTextField textField = new JTextField(40);
JTextArea messageArea = new JTextArea(8, 40);
/**
* Constructs the client by laying out the GUI and registering a
* listener with the textfield so that pressing Return in the
* listener sends the textfield contents to the server. Note
* however that the textfield is initially NOT editable, and
* only becomes editable AFTER the client receives the NAMEACCEPTED
* message from the server.
*/
public ChatClient() {
// Layout GUI
textField.setEditable(false);
messageArea.setEditable(false);
frame.getContentPane().add(textField, "North");
frame.getContentPane().add(new JScrollPane(messageArea), "Center");
frame.pack();
// Add Listeners
textField.addActionListener(new ActionListener() {
/**
* Responds to pressing the enter key in the textfield by sending
* the contents of the text field to the server. Then clear
* the text area in preparation for the next message.
*/
public void actionPerformed(ActionEvent e) {
out.println(textField.getText());
textField.setText("");
}
});
}
/**
* Prompt for and return the address of the server.
*/
private String getServerAddress() {
return JOptionPane.showInputDialog(
frame,
"Enter IP Address of the Server:",
"Welcome to the Chatter",
JOptionPane.QUESTION_MESSAGE);
}
/**
* Prompt for and return the desired screen name.
*/
private String getName() {
return JOptionPane.showInputDialog(
frame,
"Choose a screen name:",
"Screen name selection",
JOptionPane.PLAIN_MESSAGE);
}
/**
* Connects to the server then enters the processing loop.
*/
private void run() throws IOException {
// Make connection and initialize streams
String serverAddress = getServerAddress();
Socket socket = new Socket(serverAddress, 9001);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Process all messages from server, according to the protocol.
while (true) {
String line = in.readLine();
if (line.startsWith("SUBMITNAME")) {
out.println(getName());
} else if (line.startsWith("NAMEACCEPTED")) {
textField.setEditable(true);
} else if (line.startsWith("MESSAGE")) {
messageArea.append(line.substring(8) + "\n");
}
}
}
/**
* Runs the client as an application with a closeable frame.
*/
public static void main(String[] args) throws Exception {
ChatClient client = new ChatClient();
client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.frame.setVisible(true);
client.run();
}
}
server class:
package edu.lmu.cs.networking;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
/**
*
*
*/
public class ChatServer {
/**
* The port that the server listens on.
*/
private static final int PORT = 9001;
/**
* The set of all names of clients in the chat room. Maintained
* so that we can check that new clients are not registering name
* already in use.
*/
private static HashSet<String> names = new HashSet<String>();
/**
* The set of all the print writers for all the clients. This
* set is kept so we can easily broadcast messages.
*/
private static HashSet<PrintWriter> writers = new HashSet<PrintWriter>();
/**
* The appplication main method, which just listens on a port and
* spawns handler threads.
*/
public static void main(String[] args) throws Exception {
System.out.println("The chat server is running.");
ServerSocket listener = new ServerSocket(PORT);
try {
while (true) {
new Handler(listener.accept()).start();
}
} finally {
listener.close();
}
}
/**
* A handler thread class. Handlers are spawned from the listening
* loop and are responsible for a dealing with a single client
* and broadcasting its messages.
*/
private static class Handler extends Thread {
private String name;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
/**
* Constructs a handler thread, squirreling away the socket.
* All the interesting work is done in the run method.
*/
public Handler(Socket socket) {
this.socket = socket;
}
/**
* Services this thread's client by repeatedly requesting a
* screen name until a unique one has been submitted, then
* acknowledges the name and registers the output stream for
* the client in a global set, then repeatedly gets inputs and
* broadcasts them.
*/
public void run() {
try {
// Create character streams for the socket.
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Request a name from this client. Keep requesting until
// a name is submitted that is not already used. Note that
// checking for the existence of a name and adding the name
// must be done while locking the set of names.
while (true) {
out.println("SUBMITNAME");
name = in.readLine();
if (name == null) {
return;
}
synchronized (names) {
if (!names.contains(name)) {
names.add(name);
break;
}
}
}
// Now that a successful name has been chosen, add the
// socket's print writer to the set of all writers so
// this client can receive broadcast messages.
out.println("NAMEACCEPTED");
writers.add(out);
// Accept messages from this client and broadcast them.
// Ignore other clients that cannot be broadcasted to.
while (true) {
String input = in.readLine();
if (input == null) {
return;
}
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
} catch (IOException e) {
System.out.println(e);
} finally {
// This client is going down! Remove its name and its print
// writer from the sets, and close its socket.
if (name != null) {
names.remove(name);
}
if (out != null) {
writers.remove(out);
}
try {
socket.close();
} catch (IOException e) {
}
}
}
}
}
My approach will be, to introduce a command like :WHO. When client send :WHO to the server, the server will respond list of user's connected.
Something like (on the server class) :
String input = in.readLine();
if (input == null) {
return;
} else if(input.equals(":WHO")) {
out.println(names.toString()); // ... or format it nicer.
} else {
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
This way, the client can ask "who's login/online" any time. Or you can send list of names when the client first established a connection, and send only names who joins or parts the chat.
I have two programs that I'll post below, one is a Server and the other is a Client. The ChatServer runs once and many clients can run ChatClient from separate computers and talk together in their GUI's. The the client program comes with two buttons, one that's simulates the sending of a message to a single user ; "Send Message To User", and one that just generally sends a message ; "Send Message To All". Although now the Clients on the server seem to be able to selectively send messages to an individual by giving the recipient name, all the clients can see that message, which is not exact what I am aiming for. What I am going for is having the "Send Message To User" button click to send a message to the named user without all other users seeing the message.
The Server Program Code
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
/**
* A multithreaded chat room server. When a client connects the
* server requests a screen name by sending the client the
* text "SUBMITNAME", and keeps requesting a name until
* a unique one is received. After a client submits a unique
* name, the server acknowledges with "NAMEACCEPTED". Then
* all messages from that client will be broadcast to all other
* clients that have submitted a unique screen name. The
* broadcast messages are prefixed with "MESSAGE ".
*
*/
public class ChatServer {
/**
* The port that the server listens on.
*/
private static final int PORT = 9001;
/**
* The set of all names of clients in the chat room. Maintained
* so that we can check that new clients are not registering name
* already in use.
*/
private static ArrayList<String> names = new ArrayList<String>();
/**
* The set of all the print writers for all the clients. This
* set is kept so we can easily broadcast messages.
*/
private static ArrayList<PrintWriter> writers = new ArrayList<PrintWriter>();
/**
* The appplication main method, which just listens on a port and
* spawns handler threads.
*/
public static void main(String[] args) throws Exception {
System.out.println("The chat server is running.");
ServerSocket listener = new ServerSocket(PORT);
try {
while (true) {
new Handler(listener.accept()).start();
}
} finally {
listener.close();
}
}
/**
* A handler thread class. Handlers are spawned from the listening
* loop and are responsible for a dealing with a single client
* and broadcasting its messages.
*/
private static class Handler extends Thread {
private String name;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
/**
* Constructs a handler thread, squirreling away the socket.
* All the interesting work is done in the run method.
*/
public Handler(Socket socket) {
this.socket = socket;
}
/**
* Services this thread's client by repeatedly requesting a
* screen name until a unique one has been submitted, then
* acknowledges the name and registers the output stream for
* the client in a global set, then repeatedly gets inputs and
* broadcasts them.
*/
public void run() {
try {
// Create character streams for the socket.
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Request a name from this client. Keep requesting until
// a name is submitted that is not already used. Note that
// checking for the existence of a name and adding the name
// must be done while locking the set of names.
while (true) {
out.println("SUBMITNAME");
name = in.readLine();
if (name == null) {
return;
}
synchronized (names) {
if (!names.contains(name)) {
names.add(name);
break;
}
}
}
// Now that a successful name has been chosen, add the
// socket's print writer to the set of all writers so
// this client can receive broadcast messages.
out.println("NAMEACCEPTED");
writers.add(out);
// Accept messages from this client and broadcast them.
// Ignore other clients that cannot be broadcasted to.
while (true) {
String input = in.readLine();
if (input == null) {
return;
}
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
} catch (IOException e) {
System.out.println(e);
} finally {
// This client is going down! Remove its name and its print
// writer from the sets, and close its socket.
if (name != null) {
names.remove(name);
}
if (out != null) {
writers.remove(out);
}
try {
socket.close();
} catch (IOException e) {
}
}
}
}
}
The Client Program Code
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.*;
/**
* A simple Swing-based client for the chat server. Graphically
* it is a frame with a text field for entering messages and a
* textarea to see the whole dialog.
*
* The client follows the Chat Protocol which is as follows.
* When the server sends "SUBMITNAME" the client replies with the
* desired screen name. The server will keep sending "SUBMITNAME"
* requests as long as the client submits screen names that are
* already in use. When the server sends a line beginning
* with "NAMEACCEPTED" the client is now allowed to start
* sending the server arbitrary strings to be broadcast to all
* chatters connected to the server. When the server sends a
* line beginning with "MESSAGE " then all characters following
* this string should be displayed in its message area.
*/
public class ChatClient {
BufferedReader in;
PrintWriter out;
JFrame frame = new JFrame("Chatter");
JTextField textField = new JTextField(40);
JTextArea messageArea = new JTextArea(8, 40);
JButton sendall = new JButton("Send Message to All");
JButton senduser = new JButton("Send Message to User");
String otherGuy = "";
/**
* Constructs the client by laying out the GUI and registering a
* listener with the textfield so that pressing Return in the
* listener sends the textfield contents to the server. Note
* however that the textfield is initially NOT editable, and
* only becomes editable AFTER the client receives the NAMEACCEPTED
* message from the server.
*/
public ChatClient() {
// Layout GUI
textField.setEditable(false);
messageArea.setEditable(false);
frame.getContentPane().add(textField, "South");
frame.getContentPane().add(new JScrollPane(messageArea), "Center");
frame.getContentPane().add(sendall, "West");
frame.getContentPane().add(senduser, "East");
frame.pack();
// Add Listeners
// textField.addActionListener(new ActionListener() {
// /**
// * Responds to pressing the enter key in the textfield by sending
// * the contents of the text field to the server. Then clear
// * the text area in preparation for the next message.
// */
// public void actionPerformed(ActionEvent e) {
// out.println(textField.getText());
// textField.setText("");
// }
// });
sendall.addActionListener(new ActionListener() {
/**
* Responds to pressing the enter key in the textfield by sending
* the contents of the text field to the server. Then clear
* the text area in preparation for the next message.
*/
public void actionPerformed(ActionEvent e) {
out.println(textField.getText());
textField.setText("");
}
});
senduser.addActionListener(new ActionListener() {
/**
* Responds to pressing the enter key in the textfield by sending
* the contents of the text field to the server. Then clear
* the text area in preparation for the next message.
*/
// = sendTo();
public void actionPerformed(ActionEvent e) {
otherGuy = sendTo();
out.println(textField.getText() + " -> " + otherGuy);
textField.setText("");
}
});
}
/**
* Prompt for and return the address of the server.
*/
private String getServerAddress() {
return JOptionPane.showInputDialog(
frame,
"Enter IP Address of the Server:",
"Welcome to the Chatter",
JOptionPane.QUESTION_MESSAGE);
}
/**
* Prompt for and return the desired screen name.
*/
private String getName() {
return JOptionPane.showInputDialog(
frame,
"Choose a screen name:",
"Screen name selection",
JOptionPane.PLAIN_MESSAGE);
}
private String sendTo() {
return JOptionPane.showInputDialog(
frame,
"Who do you want to send a message to?",
"",
JOptionPane.PLAIN_MESSAGE);
}
/**
* Connects to the server then enters the processing loop.
*/
private void run() throws IOException {
// Make connection and initialize streams
String serverAddress = getServerAddress();
Socket socket = new Socket(serverAddress, 9001);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
// Process all messages from server, according to the protocol.
while (true) {
String line = in.readLine();
if (line.startsWith("SUBMITNAME")) {
out.println(getName() + otherGuy);
} else if (line.startsWith("NAMEACCEPTED")) {
textField.setEditable(true);
} else if (line.startsWith("MESSAGE")) {
messageArea.append(line.substring(8) + "\n");
}
}
}
/**
* Runs the client as an application with a closeable frame.
*/
public static void main(String[] args) throws Exception {
ChatClient client = new ChatClient();
client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
client.frame.setVisible(true);
client.run();
}
}
Now I have tried thing like having various input output streams and trying to connect those, but no luck. Tried fiddling with having the names arraylist directing the messages to one client versus all but that did not work out either. Do you guys have any idea how I what I would need to do to go about doing this?
while (true) {
String input = in.readLine();
if (input == null) {
return;
}
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
} catch (IOException e) {
System.out.println(e);
}
This specifies that right when the server receives data from a client, send it to all output streams right away. There is no filtering system. You need a way to specify which stream you want to send to if you only want the message going to 1 client.
Current protocol:
Client sends - server receives - server sends to all connected outputstreams
Protocol needed:
Client sends - server receives - server see who message is too - server sends message to specified outputstream(s).
org.apache.commons.net.io.Util uses InputStream which cannot be parsed live until the stream terminates. Is that correct or incorrect?
The IOUtil class is a blackbox for me. It uses org.apache.commons.net.io.Util but this is equally opaque.
Specifically, the line Util.copyStream(remoteInput, localOutput); of IOUtil is intriguing:
copyStream
public static final long copyStream(InputStream source,
OutputStream dest)
throws CopyStreamException
Same as copyStream(source, dest, DEFAULT_COPY_BUFFER_SIZE);
Throws:
CopyStreamException
How can I read either the original stream or its copy as it comes in? Live telnet connections will have an InputStream which does not terminate. I see no such functionality in the API.
Alternately, re-implementing Apache examples.util.IOUtil leads back to the original problem:
package weathertelnet;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
public class StreamReader {
private final static Logger LOG = Logger.getLogger(StreamReader.class.getName());
private StringBuilder stringBuilder = new StringBuilder();
private InputStream inputStream;
public StreamReader() {
}
public void setInputStream(InputStream inputStream) throws IOException {
this.inputStream = inputStream;
readWrite();
}
public void readWrite() throws IOException {
Thread reader = new Thread() {
#Override
public void run() {
do {
try {
char ch = (char) inputStream.read();
stringBuilder.append(ch);
} catch (IOException ex) {
}
} while (true); //never stop reading the stream..
}
};
Thread writer = new Thread() {
#Override
public void run() {
//Util.copyStream(remoteInput, localOutput);
//somehow write the *live* stream to file *as* it comes in
//or, use org.apache.commons.net.io.Util to "get the data"
}
};
}
}
Either I have a fundamental misunderstanding, or, without re-implementing (or using reflection, maybe) these API's do not allow processing of a live, unterminated InputStream.
I'm really not inclined to use reflection here, the next stage is, I think, to start breaking down what org.apache.commons.net.io.Util does and how it does it, but that's really going down the rabbit hole. Where does it end?
http://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/io/Util.html#copyStream%28java.io.InputStream,%20java.io.OutputStream%29
You can copy a Stream "live" but the InputStream will probably block when there is no more input.
You can see the code for org.apache.commons.net.io.Util#copyStream(...) here
output first:
thufir#dur:~$
thufir#dur:~$ java -jar NetBeansProjects/SSCCE/dist/SSCCE.jar
print..
makeString..
cannot remove java.util.NoSuchElementException
------------------------------------------------------------------------------
* Welcome to THE WEATHER UNDERGROUND telnet service! *
------------------------------------------------------------------------------
* *
* National Weather Service information provided by Alden Electronics, Inc. *
* and updated each minute as reports come in over our data feed. *
* *
* **Note: If you cannot get past this opening screen, you must use a *
* different version of the "telnet" program--some of the ones for IBM *
* compatible PC's have a bug that prevents proper connection. *
* *
* comments: jmasters#wunderground.com *
------------------------------------------------------------------------------
Press Return to continue:finally -- waiting for more data..
cannot remove java.util.NoSuchElementException
finally -- waiting for more data..
------------------------------------------------------------------------------
* Welcome to THE WEATHER UNDERGROUND telnet service! *
------------------------------------------------------------------------------
* *
* National Weather Service information provided by Alden Electronics, Inc. *
* and updated each minute as reports come in over our data feed. *
* *
* **Note: If you cannot get past this opening screen, you must use a *
* different version of the "telnet" program--some of the ones for IBM *
* compatible PC's have a bug that prevents proper connection. *
* *
* comments: jmasters#wunderground.com *
------------------------------------------------------------------------------
Press Return to continue:
cannot remove java.util.NoSuchElementException
^Cthufir#dur:~$
thufir#dur:~$
then code:
thufir#dur:~$ cat NetBeansProjects/SSCCE/src/weathertelnet/Telnet.java
package weathertelnet;
import static java.lang.System.out;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Logger;
import org.apache.commons.net.telnet.TelnetClient;
public final class Telnet {
private final static Logger LOG = Logger.getLogger(Telnet.class.getName());
private TelnetClient telnetClient = new TelnetClient();
public Telnet() throws SocketException, IOException {
InetAddress host = InetAddress.getByName("rainmaker.wunderground.com");
int port = 3000;
telnetClient.connect(host, port);
final InputStream inputStream = telnetClient.getInputStream();
final ConcurrentLinkedQueue<Character> clq = new ConcurrentLinkedQueue();
final StringBuilder sb = new StringBuilder();
Thread print = new Thread() {
#Override
public void run() {
out.println("print..");
try {
char ch = (char) inputStream.read();
while (255 > ch && ch >= 0) {
clq.add(ch);
out.print(ch);
ch = (char) inputStream.read();
}
} catch (IOException ex) {
out.println("cannot read inputStream:\t" + ex);
}
}
};
Thread makeString = new Thread() {
#Override
public void run() {
out.println("makeString..");
do {
try {
do {
char ch = clq.remove();
sb.append(ch);
// out.println("appended\t" + ch);
} while (true);
} catch (java.util.NoSuchElementException | ClassCastException e) {
out.println("cannot remove\t\t" + e);
try {
Thread.sleep(1000);
} catch (InterruptedException interruptedException) {
out.println("cannot sleep1\t\t" + interruptedException);
}
} finally {
out.println("finally -- waiting for more data..\n\n" + sb + "\n\n\n");
try {
Thread.sleep(1000);
} catch (InterruptedException interruptedException) {
out.println("cannot sleep1\t\t" + interruptedException);
}
}
} while (true);
}
};
print.start();
makeString.start();
}
private void cmd(String cmd) throws IOException {//haven't tested yet..
byte[] b = cmd.getBytes();
System.out.println("streamreader has\t\t" + cmd);
int l = b.length;
for (int i = 0; i < l; i++) {
telnetClient.sendCommand(b[i]);
}
}
public static void main(String[] args) throws SocketException, IOException {
new Telnet();
}
}thufir#dur:~$
thufir#dur:~$