I'm setting up a basic client server program. Right now the client is simply sending an object with a command string in it to the server and the server is acknowledging it. This is done using a JFrame with buttons on it. It seems to work ok, except the client seems to be sending multiple requests instead of just one.
Inner class within my Client code:
private class CommandHandler implements ActionListener{
FTPCommand c;
ObjectOutputStream oos;
#Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd == "DIR"){
c = new FTPCommand("DIR");
}
if (cmd == "CHDIR"){
String newDirectory = JOptionPane.showInputDialog("Please enter directory to change to:");
c = new FTPCommand("CHDIR", newDirectory);
}
try {
oos = new ObjectOutputStream(sock.getOutputStream());
oos.writeObject(c);
oos.flush();
//System.out.println("Command: " + c.getCommand() + ", sent successfully");
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
Server:
public void run(){
ObjectInputStream in;
try {
in = new ObjectInputStream(sock.getInputStream());
FTPCommand cmd = (FTPCommand) in.readObject();
System.out.println("Received command: " + cmd.getCommand() + " at " + System.currentTimeMillis());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
So if I click on the DIR button just once, this is the output I get, although sometimes it might just be one or two lines:
Received command: DIR at 1390572358017
Received command: DIR at 1390572365578
Received command: DIR at 1390572377229
Why is it sending multiple requests?
Check inside the constructor of FTPCommand; that how many time... it has been called... If thrice .... trace the calling methods.
It turns out that this was the result of the client holding onto previous connections, and sending requests through all open sockets.
Related
Hello Im writing an app in which client sends name of room to server, server creates it and then sends back whole list of rooms. I have problem with receiving this object from server also whats interesting when I close clients' app and open again I have list of rooms just like it should be. I refresh room list in client app but its always empty only reopening helps that's pretty weird and I don't know an issue of this.
On client side:
getIs() method is returning is object
getOs() method returning os object
this.os = new ObjectOutputStream(clientSocket.getOutputStream());
this.is = new ObjectInputStream(clientSocket.getInputStream());
private void createRoom(ActionEvent event) {
String roomName = "CreateRoom ";
roomName += setRoomName();
String response = null;
try {
client.getOs().writeObject(roomName);
response = (String) client.getIs().readObject();
System.out.println(response);
} catch (IOException | ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void refreshRooms() {
String response = null;
try {
client.getOs().writeObject("RefreshRooms");
response = (String) client.getIs().readObject();
System.out.println(response);
rooms = (Rooms) client.getIs().readObject();
System.out.println("Print in client: ");
rooms.printAllRooms();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Server:
this.os = new ObjectOutputStream(connection.getOutputStream());
this.is = new ObjectInputStream(connection.getInputStream());
public void run() {
String inputRequest = null;
try {
while((inputRequest = (String) ois.readObject()) != null) {
System.out.println(inputRequest);
handleRequest(inputRequest);
}
} catch (IOException | ClassNotFoundException e) {
System.out.println("Client has disconnected.");
e.printStackTrace();
}
}
private void handleRequest(String request) {
String response = null;
String[] msg = request.split(" ");
if(msg[0].equals("CreateRoom")) {
try {
oos.writeObject("You want create a room.");
Room newRoom = new Room(msg[1]);
rooms.addRoom(newRoom);
System.out.println("Created room: " + newRoom.getName());
System.out.println("\n Print after creation: ");
rooms.printAllRooms();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else if (msg[0].equals("RefreshRooms")) {
try {
oos.writeObject("You want list of rooms.");
System.out.println("Print before send.");
rooms.printAllRooms();
oos.writeObject(rooms);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
///EDIT:
So I removed PrintWriter and BufferedReader objects and now Im using only Object Streams. What doesn't work now is:
I create some rooms one after another and then refresh rooms list on clients app - in that case I get all rooms
But when I create one room refresh then create another and refresh I get only 1 room after 2nd refresh, so basically when I refresh server sends me always the same object from 1st send and I don't know how to change it.
Also Im printing these rooms on server side and always get all rooms so room creation is OK.
You could try to flush the buffered streams:
os.flush()
This will force the stream to actually send the bytes of the serialized object. Without that, the BufferedOutputStream might just wait around and buffer data, as the name says. This is done so that the size of the sent packets does not become too small, which would result in a lot of overhead if you want to send multiple objects.
If you are done, you should close the stream anyway.
I know what you are going to say "use .flush()"....I am. What I have done is build a program that will synchronize 3 machines based on the messages sent from the client.
Here is the algorithm I am using:
Connect all client machines to 1 server machine
Each client machine gets to the point where it is ready to execute (this is the "syncing" part" so it sends a "ready" flag to the server)
Once all machines have sent the "ready" flag via socket, the server sends back "execute" via PrintWriter and the client machines and they execute and finish that iteration
After the client has finished executing it sends a "reset" flag back to the server and the server gets ready to start all over (the server doesn't care how many times this will happen)
Each client then moves on to the next iteration to do the same exact thing, gets "ready", and then when all are "ready", server sends "execute" again <- this is were the problem is. My Printwriter.println("execute") is supposedly sent but is NEVER received on any of the client machines.
Just to reiteration and be clear, the first time this proccess is carried out, everything go's as expected, on the second iteration, the "execute" command is never sent to the client even though it executes the out.println("execute) line.
Am I doing something wrong? I have also tried PrintStream and see the same problem ever time. Am I handling the BufferedReader wrong? For the life of me I cannot understand why everything works as expected the first time around but then after the second time "execute" is never sent to the client machines. Any help will be much appreciated.
Server snipit:
StartServerResponseThread() Is called after server has accepted the client.
// Init the nuc client
public void NucClientInit(Socket s) {
new Thread() {
public void run() {
try {
socket = s;
bufOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
// socket.getOutputStream(), "UTF-8")), true);
brffReadIn = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
// Start the reponse thread
StartServerResponseThread();
// Confirm connection to client
ConfirmConnection();
} catch (IOException ex) {
Logger.Log("Nuc:NucClientInit:IOException:ex: " + ex);
}
}
}.start();
}
// Sends connect and waits for reponse back from server
private void ConfirmConnection() {
Logger.Log("Sending connect to: " + ip_address);
try {
bufOut.write("connect");
bufOut.newLine();
bufOut.flush();
} catch (IOException ex) {
Logger.Log("Nuc:ConfirmConnection():bufOut:ex " + ex);
}
Logger.Log("Sent connect: " + ip_address);
while (!connected) {
}
Logger.Log(ip_address + " Client is successfully Connected!");
}
// Handles any message from server
private void HandleClientMessage(String message) {
switch (message) {
case "connect":
// Flag connection to confirm client connected (see ConfirmConnection())
connected = true;
break;
case "ready":
Logger.Log("Nuc:" + ip_address + ":received:ready");
// Nuc is ready for sync now
SetNucIsReady(true);
break;
case "reset":
// Nuc has finished test (probably interleave)
SetNucIsReady(false);
break;
default:
Logger.Log("UNHANDLED CLIENT RSPONSE!!");
}
}
// Will always run in a separate thread waiting for any response
// from the server
private void StartServerResponseThread() {
new Thread() {
public void run() {
try {
while ((fromClient = brffReadIn.readLine()) != null) {
HandleClientMessage(fromClient);
}
} catch (IOException ex) {
Logger.Log("Nuc:StartServerResponseThread():IOException:ex: " + ex);
}
} // Close run()
}.start();
}
// This method is used to send the execute command to the Nuc machine tied
// to this object.
public void Execute() {
try {
bufOut.write("execute");
bufOut.newLine();
bufOut.flush();
Logger.Log(("Class:Nuc:method:Execute:" + ip_address + " sent "
+ "execute command"));
} catch (IOException ex) {
Logger.Log("Nuc:Execute():IOException:ex: " + ex);
}
}
Client:
// Tell Server ready to execute
static void SendReady(){
try {
bufOut.write("ready");
bufOut.newLine();
bufOut.flush();
} catch (IOException ex) {
log("KPI_Socket:SendReady():bufOut:ex " + ex);
}
log("Sent to Server 'ready'");
}
// Wait until execute is received from server
static void WaitForExecute(){
log("Waiting for execute command from server");
while(!execute){
}
log("Execute received from Server!");
execute = false;
}
// Tell server nuc needs to reset its isReady boolean
static void SendServerReset(){
try {
bufOut.write("reset");
bufOut.newLine();
bufOut.flush();
} catch (IOException ex) {
log("KPI_Socket:SendServerReset():bufOut:ex " + ex);
}
log("Sent to Server 'reset'");
}
// Inform user connection is good
static void HandleConnect(){
connected = true;
log("Client Now Connected To Server");
}
// Handles any response from the server
static void HandleServerResponse(String serverResponse){
switch (serverResponse){
case "connect":
HandleConnect();
break;
case "execute":
execute = true;
HandleExecute();
default:
log("UHANDLED RSPONSE FROM SERVER!!")
}
}
// Will always run in a separate thread waiting for any response
// from the server
static void StartServerResponseThread(){
new Thread(){
public void run(){
while ((fromServer = brffReadIn.readLine()) != null) {
log("Message from Server: '" + fromServer + "'");
HandleServerResponse(fromServer);
}
log("StartServerResponseThread() CLOSED!!!!!!!!");
} // Close run()
}.start();
}
// Init all components, connect to server and start server response thread
static void ConnectToServer(String host, int port){
hostname = host;
portNumber = port;
try {
log("Attempting to connect to " + hostname + "\n");
socket = new Socket(hostname, portNumber);
log("Connected to " + hostname);
bufOut = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
// socket.getOutputStream(), "UTF-8")), true);
brffReadIn = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
// Start the server response thread to run in background
StartServerResponseThread();
// Wait for response from server on connect message
while(!connected){
log("Waiting for greeting message from server");
wait_for(1);
}
try {
bufOut.write("connect");
bufOut.newLine();
bufOut.flush();
} catch (IOException ex) {
log("KPI_Socket:ConnectToServer():bufOut:ex " + ex);
}
} catch (UnknownHostException e) {
log("KPI_Socket:ConnectToServer():UnknownHostException" + e);
} catch (IOException e) {
log("KPI_Socket:ConnectToServer():IOException: " + e);
}
} // Close ConnectToServer();
I know what you are going to say "use .flush()"...
No I'm not. I'm going to say 'don't use PrintWriter'. It swallows exceptions. Use BufferedWriter, write(), newline(), and flush(). That way if there are any exceptions, you'll see them. With PrintWriter, you can't.
I have a chat program. The problem is that I am trying to disallow dupe names. Essentially, whenever a name change request is sent to the server, it is checked against the list of names currently in use and if it is already taken, the person is added to my shitlist (not allowed to post) and they are sent a message that they need to change their name.
I commented the crap out of the code since there is a lot so you can understand it easily.
The problem is that the wrong person is being sent the message that the name is already in use! I have spent the last 8 hours trying to find it and It's bloody driving me mad!
The server side code is long; I'll post the relevant bits and any further will be provided on request. I'll also link to the complete program. (Not the source, the JAR.)
JAR: https://www.mediafire.com/?4t2shjdjf7blpg2
//...Irrelevant bits ommitted...//
public class Server
{
// The server object reference
static Server server;
// Declarations:
private ArrayList<ObjectOutputStream> clientOutputStreams; // out streams
private ArrayList<String> takenNames = new ArrayList<>(); // taken names
private InetAddress ip;
private final int serverPort; // the port the server is running on
private static ObjectOutputStream changer; // the last person to change names
private ArrayList<ObjectOutputStream> shitList = new ArrayList<>();
private HashMap <InetAddress, ObjectOutputStream> ipMap =
new HashMap<>(); // <ip, outputstream>
//...Irrelevant bits ommited...//
// Don't mind this non-indentation, it is supposed to be.
public void tellEveryone(Message message, InetAddress senderIP)
{
// First check some special conditions..
if(message.getType() == Message.TYPE.IN_USE)
{
try
{
changer.writeObject(message);
}
catch (IOException e)
{
e.printStackTrace();
}
}
// If someone is on my shitlist,
if(shitList.contains(ipMap.get(senderIP)))
{
// Warn them of their sins...
Message nopeMessage = new Message(Message.TYPE.SERVER,
"You may not send any messages until you change your name!",
"Server");
try
{
ipMap.get(senderIP).writeObject(nopeMessage);
}
catch(IOException e)
{
e.printStackTrace();
}
}
else
{
// Send message normally to everyone...
// Sync, just to be safe
synchronized(clientOutputStreams)
{
for(ObjectOutputStream oo : clientOutputStreams) // while more clients...
{
try
{
oo.writeObject(message);
oo.flush();
}
catch(IOException e)
{
System.out.println("IOException caught during tellEveryone()");
e.printStackTrace();
}
}
}
System.out.println(getTimeStamp() + ": Message Sent by:".
concat(" " + senderIP + "/ " + message.getSenderName()));
}
}
The server handler inner class...
public class ServerHandler implements Runnable
{
#Override
public void run()
{
// Create a list of client out streams to send stuff...
clientOutputStreams = new ArrayList<>();
try // To establish a connection with clients
{
// Create server socket...
ServerSocket serverSocket = new ServerSocket(serverPort);
while(true) // Will always run! Blocks!
{
// Assign a client socket to any new socket connections...
// (The var used here is temp, but will be passed off soon.)
Socket clientSocket = serverSocket.accept();
// Get's the ip of the client that connected...
ip = clientSocket.getInetAddress();
System.out.println(ip + " " + "connected.");
// Create ooStream to send messages to client...
ObjectOutputStream ooStream =
new ObjectOutputStream(
clientSocket.getOutputStream());
// Add the client oo stream to the list of outputs...
clientOutputStreams.add(ooStream);
// Add user IP data to map of ip's
ipMap.putIfAbsent(ip, ooStream);
// Create new thread to run inner class ClientHandler...
Thread t = new Thread(new ClientHandler(clientSocket));
// Running the thread makes it safe to overwrite the...
// ...clientsocket variable.
t.start();
}
}
catch (IOException e)
{
System.out.println("Exception in server.run()");
// TODO: Revise
e.printStackTrace();
}
}
}
The client handler inner class
public class ClientHandler implements Runnable
{
private ObjectInputStream oInStream; // The client's input stream.
private Socket socket; // Socket to the client
public ClientHandler(Socket clientSocket)
{
try // to create an input stream...
{
socket = clientSocket; // <-- The one passed in to the method
// Potential error from previous version... REMOVE WHEN TESTED
oInStream = new ObjectInputStream(socket.getInputStream());
}
catch(IOException e)
{
System.out.println("Error establishing input stream");
}
}
#Override
public void run()
{
Message message;
try // To process incoming messages...
{
while(socket.isClosed() == false) // If the socket is open...
{
// While there are more messages...
// Also assigns to the message var.
while((message = (Message)oInStream.readObject()) != null)
{
// Passes on the message and sender info.
if(message.getType() == Message.TYPE.NAME_REQUEST)
{
changer = ipMap.get(socket.getInetAddress());
System.out.println(socket.getInetAddress());
System.out.println(changer.toString());
handleNameRequests(message);
}
else
{
tellEveryone(message, ip); // TEST CHANGE- DELETED IF TEST
}
}
// TEST TEST TEST
synchronized(clientOutputStreams)
{
int index =
clientOutputStreams.indexOf(
socket.getOutputStream());
clientOutputStreams.remove(index);
System.out.println("Removed the client in sync");
}
}
// TEST TEST TEST
socket.close(); // TEST CLOSING SOCKET WHEN DONE.
System.out.println("Sock closed after while loop in ch run()");
}
catch(IOException e)
{
System.out.println("IOException caught when "
+ "reading message.");
}
catch (ClassNotFoundException e)
{
System.out.println("Some poor sap is going to have to debug"
+ "this!");
}
finally
{
// THIS WHOLE BLOCK: TEST TEST TEST
try
{
oInStream.close();
System.out.println("just closed oinStream");
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
I FINALLY FOUND IT!
For any future people encountering a similar problem, the problem was that I was assigning the ip variable in the wrong place! This essentially resulted in the list of ip's being all the same! Another bug confounded that problem, in that when I disabled the sending ability of messages when on my shitlist (aren't programmers the darndest?), I disabled ALL types of messages, including those from the server, ect! Oops!
The lesson? Bugs hide in the darndest places. Walk through everything, and doubt what you know to be true. Assume nothing, verify everything. There are never enough print statements when debugging!
I am working on a small project where I have to communicate to an Android app on my phone and with Arduino.
Now, I have the connection between Android and laptop (used as server, I have a small amount of data stored here), and I can change the contents of text files when I send certain instructions from Android app.
This is how I do it:
I have a ServerSide class that listens on port 3000 and I read the text I stream from phone, then I make certain changes in text files for different messages.
The code:
public class ServerSide {
public static void main(String[] args) throws IOException {
while (true) {
ServerSocket serverSocket = null;
// check if client is trying to connect
try {
serverSocket = new ServerSocket(3000);
} catch (IOException e) {
System.err.println("Cannot communicate on this port");
System.exit(1);
}
Socket clientSocket = null;
// move to another socket
try {
clientSocket = serverSocket.accept();
} catch (IOException e) {
System.err.println("Accept failed");
System.exit(1);
}
// stream that will be sent to client. "true" is for creating from
// existing
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),
true);
// stream that comes from the client
BufferedReader in = new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
String recivedData, sendData;
ServerProtocol communicationProtocol = new ServerProtocol();
while ((recivedData = in.readLine()) != null) {
sendData = communicationProtocol.process(recivedData);
out.println(sendData);
System.out.println("The text should now be written in file");
System.out.println(sendData);
}
in.close();
out.close();
clientSocket.close();
serverSocket.close();
}
}
}
ServerProtocol.process() is the method that updates the files
By the way, this is a good version of a program that implies connection via sockets (if anyone should need information about this, at a future time).
Everything works great, I can see my updates immediatly after I send them, the server is up and running, waiting for messages.
I forgot to mention, I am new to java and a novice in programming, in general.
Now, I want this code I managed to write to be part of a bigger "server". By "server", I understand a program that "serves", performs a service. When it runs on my laptop, it takes information that comes from the Internet on the port I specify, change things in files according to my messages, keeps theese files updated and in the same time it uses theese files to "interpert" data I send from phone, and then sends according messages to Arduino Shield. (THIS IS WHAT I WANT TO ACHIVE)
I guess that what I miss, is the following:
How do i make this code I have written untill now, part of a bigger project, that does all that?
I managed to split the project in 3 parts:
Communication laptop - Android
Constant data updates
Communication laptop - Arduino
I've done some research, and I came across threads. So I thought about having the communication with Android on a separate thread of a MainServer. I clearly got it wrong, because it doesn't do what I expect it to do, so here is the code:
I create the ServerSide class that extends Thread, and has a run() method that should be called when I start the thread. It behaves just like the one above, but the executing code lays inside a run() method:
public class ServerSide extends Thread {
#Override
public void run() {
while (true) {
ServerSocket serverSocket = null;
// check if client is trying to connect
try {
serverSocket = new ServerSocket(3000);
} catch (IOException e) {
System.err.println("Cannot communicate on this port");
System.exit(1);
}
Socket clientSocket = null;
// move to another socket
try {
clientSocket = serverSocket.accept();
} catch (IOException e) {
System.err.println("Accept failed");
System.exit(1);
}
// stream that will be sent to client. "true" is for creating from
// existing
PrintWriter out = null;
try {
out = new PrintWriter(clientSocket.getOutputStream(), true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// stream that comes from the client
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(
clientSocket.getInputStream()));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String recivedData, sendData;
recivedData = null;
sendData = null;
ServerProtocol communicationProtocol = new ServerProtocol();
try {
while ((recivedData = in.readLine()) != null) {
try {
sendData = communicationProtocol.process(recivedData);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
out.println(sendData);
System.out
.println("The text should now be written in file");
System.out.println(sendData);
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
out.close();
try {
clientSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Then, I have the MainServer:
public class MainServer {
public static void main(String[] args) throws IOException {
System.out.println("Started");
Thread myThread = new Thread(new ServerSide());
myThread.start();
System.out.println("Started2");
while (true);
}
}
It should do nothing, just start the new thread. I expect this new thread do act just like the old ServerSide above (the one with main() method).
Someone, please tell me where I got it wrong !?!
Well, two things seem a little unusual about the MainServer class. First, creating a thread with new Thread(new ServerSide()) will cause a compilation error. There are two ways to fix this: either you make ServerSide implement the Runnable interface instead of extending Thread, or you create the thread with new ServerSide(). Second, the infinite loop at the end of main is useless and can be removed. The main method runs in its own thread, and if it finishes, all other threads keep running, and there is no need to keep main alive. The program will indeed keep running when main finishes, which may seem strange, but that's what will happen.
Everything was OK here, my problem was actually my phone connection to wi-fi, I was a bit too far in the back yard :)
I'm new to network I/O programming, and I've run into a snag-- basically what I want to do is have a desktop app talk to the google maps javascript API. In order to facilitate this, I have built a java applet which will act as a bridge between the desktop app and the browser javascript app. When I run the desktop app and applet together in Eclipse they can communicate perfectly, and I am able to invoke applet functions by writing strings to a Socket bound to the same port the applet has established a ServerSocket connection with. For testing purposes in Eclipse, I send the string "sendJSAlertTest" to the socket's outputstream, then derive a Method instance using the java.lang.reflect API from the ServerSocket inputstream, and then finally invoke the resulting method in the applet. When the applet is running in a browser I write "sendJSAlert" to the socket instead since it leads to the actual invocation of javascript. The result in Eclipse using the appletviewer is that the desktop application context prints the output "awesome sendJSAlert" and the applet context prints the output from the sendJSAlertTest() method, "Hello Client, I'm a Server!". The result of passing "sendJSAlert" to the applet running in the browser is that the desktop application prints null, suggesting that for some reason the inputstream of the ServerSocket is empty, and the browser itself does nothing when it should generate a javascript alert box with the text "Hello Client, I'm a Server!". The browser I'm using is Google Chrome, and for the moment I am simply running everything on the local machine (e.g. no remote server involved yet)
Below is the relevant Java code and HTML:
SocketClient.java
import java.awt.event.*;
import java.io.*;
import java.net.*;
public class SocketClient {
Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
private InetAddress myAddress;
private String remoteFunction;
public SocketClient(){
}
public void listenSocket(int portNum){
//Create socket connection
try{
System.out.println("#Client Trying to create socket bound to port " + portNum);
socket = new Socket(<my hostname here as a string>, portNum);
System.out.println("the attached socket port is " + socket.getLocalPort());
System.out.flush();
out = new PrintWriter(socket.getOutputStream(), true);
out.println("sendJSAlertTest");
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = in.readLine();
System.out.println("#CLient side Text received from server: " + line);
} catch (UnknownHostException e) {
System.out.println("Unknown host: <my hostname here as a string>.eng");
System.exit(1);
} catch (IOException e) {
System.out.println("No I/O");
e.printStackTrace();
System.exit(1);
}
}
public void setRemoteFunction(String funcName){
remoteFunction = funcName;
}
public String getRemoteFunction(){
return remoteFunction;
}
}
SocketServer.java
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.*;
class SocketServer {
ServerSocket server = null;
Socket client = null;
BufferedReader in = null;
PrintWriter out = null;
String line;
private NetComm hNet;
private Method serverMethod;
SocketServer(NetComm netmain){
hNet = netmain;
}
public void listenSocket(int portNum){
try{
System.out.println("#server Trying to create socket bound to port " + portNum);
server = new ServerSocket(portNum);
System.out.println("the attached socket port is " + server.getLocalPort());
} catch (IOException e) {
System.out.println("Could not listen on port " + portNum);
System.exit(-1);
}
try{
client = server.accept();
System.out.println("Connection accepted!");
} catch (IOException e) {
System.out.println("Accept failed: " + portNum);
System.exit(-1);
}
try{
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.out.println("Accept failed: " + portNum);
System.exit(-1);
}
while(true){
try{
System.out.println("trying to read from inputstream...");
line = in.readLine();
System.out.println(line);
//Now that we have a method name, invoke it
try {
serverMethod = hNet.getClass().getDeclaredMethod(line,
String.class);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
serverMethod.invoke(hNet, "Hello Client, I'm a Server!");
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Send data back to client
out.println("awesome " + line);
} catch (IOException e) {
System.out.println("Read failed");
System.out.flush();
System.exit(-1);
}
}
}
protected void finalize(){
//Clean up
try{
in.close();
out.close();
server.close();
} catch (IOException e) {
System.out.println("Could not close.");
System.exit(-1);
}
}
public int getBoundLocalPort(){
return server.getLocalPort();
}
}
NetComm.java
import cresco.ai.att.ccm.core.CCMMain;
import cresco.ai.att.ccm.gui.DataPanel;
import java.io.*;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import javax.servlet.*;
import javax.servlet.http.*;
import java.applet.*;
public class NetComm extends JApplet{//HttpServlet{
private CCMMain hMain;
private DataPanel dpLocal;
private SocketServer sockserver;
private Method serverMethod;
String testStr;
Integer testInt; /*integer */
Character testChar; /*character*/
//Testing this...
ServerSocket server = null;
Socket client = null;
BufferedReader in = null;
PrintWriter out = null;
String line;
#Override
public void init(){
sockserver = new SocketServer(this);
//For offline debug (should be disabled in a release to the webapp):
//initSocketServer is commented out in the release version and
//invoked in the Eclipse testbed version. In the webapp,
//initSocketServer is invoked from javascript (see below js sockPuppet())
//////initSocketServer(0);
String msg = "Hello from Java (using javascript alert)";
try {
getAppletContext().showDocument(new URL("javascript:doAlert(\"" +
msg +"\")"));
}
catch (MalformedURLException me) { }
}
public void sendJSAlertTest(String message){
System.out.println("sendJSAlert remotely invoked, with message: " +
message);
}
public void sendJSAlert(String message){
try {
getAppletContext().showDocument(new URL("javascript:doAlert(\"" +
message +"\")"));
}
catch (MalformedURLException me) { }
}
public void initSocketServer(int portNum){
sockserver.listenSocket(portNum);
}
public void finalizeSocketServer(){
sockserver.finalize();
}
public int socket2Me(int portNum){
try {
socks.add(new ServerSocket(portNum));
return 0; //socket opened successfully
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return -1; //socket failed to open
}
}
public int getSocketServerPort(){
return sockserver.getBoundLocalPort();
}
public void showRectTest(){
try {
getAppletContext().showDocument(new
URL("javascript:overlayRect()"));
}
catch (MalformedURLException me) { }
}
public void setGUI(DataPanel d){
dpLocal = d;
}
}
MapViz.html
<html>
<head>
<title>Welcome to Geographic Midpoint Map Vizualization!</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta charset="UTF-8">
<link href="https://google-developers.appspot.com/maps/documentation/javascript
/examples/default.css"
rel="stylesheet" type="text/css">
...google maps stuff omitted...
<script type="text/javascript">
<script type="text/javascript">
function overlayRect(){
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillStyle="#FF0000";
ctx.fillRect(0,0,150,75);
}
function doAlert(s){
alert(s);
}
function testJava(){
document.ccmApplet.showRectTest();
}
function sockPuppet(){
var i = parseInt(document.getElementById("args").value,10);
alert("parsing the input args... got " + i);
if(i == NaN || i == null){
i = 0;
}
alert("passed NaN OR null block, i is " + i);
//i = 6672; //because $%*& you, that's why!
document.ccmApplet.initSocketServer(i);
//document.ccmApplet.listenSocket(i);
alert("inittializing socket server...");
//queryPort();
alert("querying port...");
document.ccmApplet.finalizeSocketServer();
//document.ccmApplet.finalize();
alert("finalizing socket server...");
}
function queryPort(){
var d = document.getElementById("debug");
var s1 = "Last port opened was: ";
//var s2 = document.ccmApplet.getLastBoundPort();
var s2 = document.ccmApplet.getSocketServerPort();
var sFinal = s1.concat(s2);
d.value = sFinal;
}
</script>
</head>
<body>
<applet width="500" height="50" name="ccmApplet" archive="CCM.jar"
code="cresco.ai.att.ccm.io.NetComm" MAYSCRIPT></applet>
<p></p>
<canvas id="myCanvas" width="200" height="100"></canvas>
<div id="map_canvas"></div>
<input id="args" type="textentry" value="" />
<button height="50" width="50" onClick="sockPuppet()">Test Socket
Creation</button>
<input id="debug" type="debugthingy" value="debug area..." />
<button height="50" width="50" onClick="testJava()">Test Java Callback</button>
</body>
</html>
In the webapp, I fill in the args input with a valid port number on the local machine and press the Test Socket Connection button which invokes the sockPuppet() javascript. This should create a ServerSocket and bind it to the specified port, which I then separately connect my desktop client app to via SocketClient.listenSocket. The result from Eclipse in the desktop app context is "awesome sendJSAlertTest" and in the appletviewer context is the output "sendJSAlert remotely invoked, with message: Hello Client, I'm a Server!". The webapp, invoking sendJSAlert(), should call the javascript alert function on the same message, creating a popup box with the message "sendJSAlert remotely invoked, with message: Hello Client, I'm a Server!" but instead nothing happens in the browser (nor the Chrome java or javascript debug consoles), and the desktop app output is null instead of "awesome sendJSAlert" as expected
So the question: What might be the cause of the different results? I know the browser's security sandbox could be an issue, but I've included a permissions file which should allow communication via sockets on any localhost port:
grant {
permission java.net.SocketPermission
"localhost:1024-",
"accept, connect, listen, resolve";
};
It's certainly possible though that I have not applied the permissions properly (I used the sun policytool gui); what exactly needs to be done in the applet code (if anything) to apply the permissions? Could a security problem result in the lack of response I'm seeing? I'd expect an exception to be reported in Chrome's java debug console, but there weren't any...
any help would be much appreciated, thanks!
-CCJ
UPDATE:
Okay, some new information: I ran the applet again in Chrome with the javascript console open (could have sworn I tried this before without effect, but evidently not) and received the following console output--
"Uncaught Error: java.security.AccessControlException: access denied
("java.net.SocketPermission" "<myipaddress>:4218" "accept,resolve") MapVizApp.html:154
sockPuppet MapVizApp.html:154 onclick MapVizApp.html:179 Uncaught Error: Error
calling method on NPObject. sockPuppet onclick "
So the question now is why am I tripping this security exception? The policy file with the permissions given above is in the same working directory as the html page and the jar file containing the applet, and I added the following to my system's JRE security policy file
//Grants my NetComm applet the ability to accept, connect, and listen on unpriv. ports
grant codeBase "file:${user.home}\Desktop\dev\invention\ATT\MapViz\CCM.jar" {
permission java.net.SocketPermission
"localhost:1024-",
"accept, connect, listen, resolve";
};
I haven't yet signed the applet, but it was my understanding that if the policy files are in order an applet doesn't need to be signed... if I'm wrong on that please let me know. Anyway, does anyone have any suggestions as to why this security exception is being thrown despite the policy files having the above granted permissions? Is there a naming convention for policy files in working directories that the JRE looks for? My working directory policy file for now is just named ccmPolFile, but I'm not clear on how the JRE is supposed to locate it; is there something I need to add to the applet code to point the JRE at the intended working directory policy file? Further, shouldn't the system policy file grant that I added be enough by itself to satisfy socket permissions for my applet inside CCM.jar?
UPDATE 2:
I signed the applet and added the line policy.url.3=file:${user.home}\Desktop\dev\invention\ATT\MapViz\ccmPolFile.policy to my java.security file in ${java.home}/lib/security (via http://docs.oracle.com/javase/tutorial/security/tour2/step4.html#Approach2 this is apparently how the JRE locates policy files to load)... sadly, the result is exactly the same security exception. The only thing left that I know of is
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
// perform the security-sensitive operation here
return null;
}
});
which should let me do almost anything since the applet is now signed. I wanted to keep signing out of the equation, but policy files aren't working for some reason. I'll be back shortly with how that works out
righto, so following my update 2 above, I change the listenSocket() method in SocketServer.java code to
public void listenSocket(int portNum){
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
int portNum = 4444;
try{
System.out.println("#server Trying to create socket bound to port " + portNum);
server = new ServerSocket(portNum);
System.out.println("the attached socket port is " + server.getLocalPort());
} catch (IOException e) {
System.out.println("Could not listen on port " + portNum);
System.exit(-1);
}
try{
client = server.accept();
System.out.println("Connection accepted!");
} catch (IOException e) {
System.out.println("Accept failed: " + portNum);
System.exit(-1);
}
try{
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.out.println("Accept failed: " + portNum);
System.exit(-1);
}
while(portNum==4444){
try{
System.out.println("trying to read from inputstream...");
line = in.readLine();
System.out.println(line);
//Now that we have a method name, invoke it
try {
serverMethod = hNet.getClass().getDeclaredMethod(line,
String.class);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
serverMethod.invoke(hNet, "Hello from Javascript invoked by the
desktop app!");
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Send data back to client
out.println("awesome " + line);
//System.out.println(line);
//System.out.flush();
} catch (IOException e) {
System.out.println("Read failed");
System.out.flush();
System.exit(-1);
}
}
return null;
}
});//end doPrivileged
}
obviously this is an unsafe kludge, but it does the trick-- I receive no security exception, and the desktop app prints "awesome sendJSAlert" so I know IO is working between the client and server contexts via sockets. The actual js alert function didn't fire, but I think that has something to do with the horrid infinite while loop in listenSocket() above...
Take home message: for some reason, to establish socket connections from an applet in google chrome I needed to sign the applet AND use AccessController.doPrivileged() to invoke my security sensitive code, despite having set my local policy and security files to grant my applet those permissions
googlers see refs:
http://www.coderanch.com/how-to/java/HowCanAnAppletReadFilesOnTheLocalFileSystem
http://docs.oracle.com/javase/7/docs/api/java/security/AccessController.html
http://www-personal.umich.edu/~lsiden/tutorials/signed-applet/signed-applet.html
http://docs.oracle.com/javase/tutorial/security/tour2/step4.html
UPDATE: Finally working 100% :D I changed the listenSocket() method above in SocketServer.java to this:
public void listenSocket(int portNum){
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
int portNum = 4444;
try{
System.out.println("#server Trying to create socket bound to port " + portNum);
server = new ServerSocket(portNum);
System.out.println("the attached socket port is " + server.getLocalPort());
} catch (IOException e) {
System.out.println("Could not listen on port " + portNum);
System.exit(-1);
}
try{
client = server.accept();
System.out.println("Connection accepted!");
} catch (IOException e) {
System.out.println("Accept failed: " + portNum);
System.exit(-1);
}
try{
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
System.out.println("Accept failed: " + portNum);
System.exit(-1);
}
try {
line = in.readLine();
System.out.println("line is " + line + " from the inputstream to the
serversocket");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if(line != null){
System.out.println("trying to read from non-null inputstream...");
//line = in.readLine();
System.out.println(line);
//Now that we have a method name, invoke that bitch!
try {
serverMethod = hNet.getClass().getDeclaredMethod(line, String.class);
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
serverMethod.invoke(hNet, "Hello From Javascript invoked by a desktop
app!");
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Send data back to client
out.println("awesome " + line);
//System.out.println(line);
//System.out.flush();
}
return null;
}
});//end doPrivileged
}
The server.accept() method blocks until a connection is made anyway, so for this scenario where I only want to pass one command at a time to the serversocket inputstream a while loop didn't make sense. The change to an if allowed the program to actually continue on to the java.reflect stuff which invokes a method in the applet which invokes javascript functions directly. Since the port is still hard-coded and the applet utilizes doPrivileged(...) this is still not a great solution, but it does satisfy the use case of invoking javascript in a web browser from a desktop java application via a java applet bridge so it makes for a good springboard into more robust implementations!