Streams - ObjectOutputStream gives NULL - java

I have two apps that work in conjunction with one another. One is a "server" type app that does not have any GUI interface and handles queries to a database and processes requests from a client. The other is a "client" that is primarily a GUI and is for users to interact with database information in a structured manner.
ISSUE / TROUBLE / HELP NEEDED WITH
The problem that I am having is that I can send one Object (a String[]) to the server successfully and with no problems. Client app sends it, Server app receives it an processes it successfully.
If I try and send a second String[], the the client compiles the array and thinks it gets sent, but the server never receives is (gets only null) and produces a IOException.
This is even with Arrays that contain the exact same number of positions and the exact same text in the exact same format and positions.
The error produced by the printStackTrace() is:
Java.io.OptionalDataException
at java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1367)
at java.io.ObjectInputStream.readObject (ObjectInputStream.java:369)
at server.ConnectionThread.processClientRequests(ConnectionThread:204)
at server.ConnectionThread.processClientRequests(ConnectionThread:50)
at javalang.Thread.run(Thread.java:722)
The code at line 204 that is the point where the ObjectStream is being read from:
String[] addArray = (String[]) ois.readObject();
ois is an ObjectInputStream and is initialized as follows:
private ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
CLIENT CODE
The client code used to send these objects TO the server app is:
ObjectToServer.writeObject(String[] var);
ObjectToServer.flush();
ObjectToServer.reset();
COMMENTS
What does NOT make sense to me is that this exact same code format is used to successfully send a number of String[] over the objectOutputStream from the SERVER to the CLIENT app without ever sending a "null"
I have Google searched this and all to absolute no avail.
Someone please help if you can!!
ADDITIONAL CODE
// CONNECTION THREAD IS ON SERVER APP, SETS UP STREAMS AND WAITS FOR MESSAGES FROM CLIENT
// HANDLES COMMUNICATION FROM CLIENT AND REST OF SERVER
public class ConnectionThread implements Runnable
{
private Socket socket;
private SystemCore core;
//Streams for connections
private InputStream is;
private OutputStream os;
//Writers and readers for communication of Strings
private PrintWriter toClient;
private BufferedReader fromClient;
// Writers and readers for sending and receiving Objects between server and client.
private ObjectInputStream ois = null;
private ObjectOutputStream oos = null;
//Protocol
static final String CLIENT_QUITTING = "Exit";
public ConnectionThread(Socket s, SystemCore aSysCore)
{
socket = s;
// State of the SystemCore as taken from HelloServer
core = aSysCore;
}
public void run()
{
try
{
openStreams();
toClient.println(MESSAGE_TO_CLIENT);
processClientRequests();
closeStreams();
this.socket.close();
}
catch (OptionalDataException ode )
{
System.out.println("OptionalDataException: ");
System.out.println("length is: " + ode.length);
}
catch (IOException ioe)
{
System.out.println("IO trouble with a connection in ConnectionThread run() " + ioe.getMessage());
ioe.printStackTrace();
}
catch (ClassNotFoundException cnf)
{
System.out.println("Class trouble with a connection in ConnectionThread run() " + cnf.getMessage());
cnf.printStackTrace();
}
catch(ParseException pe)
{
System.out.println("Parse trouble with a connection in ConnectionThread run() " + pe.getMessage());
pe.printStackTrace();
}
}
/**
* Opens streams between the server and the client.
*
* #throws IOException
*/
private void openStreams() throws IOException
{
final boolean AUTO_FLUSH = true;
this.is = this.socket.getInputStream();
this.fromClient = new BufferedReader(new InputStreamReader(is));
this.os = this.socket.getOutputStream();
this.toClient = new PrintWriter(os, AUTO_FLUSH);
//Object streams.
oos = new ObjectOutputStream(socket.getOutputStream());
ois = new ObjectInputStream(socket.getInputStream());
System.out.println("...Streams set up");
}
/**
* Private method that accepts arguments from a client and executes the related
* commands in the systemcore as long as the command passed from the client
* is not CLIENT_QUITTING.
*
* #throws IOException
* #throws ClassNotFoundException
*/
private void processClientRequests() throws IOException, ClassNotFoundException, ParseException
{
String commandFromClient;
commandFromClient = fromClient.readLine();
while (!(commandFromClient.equals(CLIENT_QUITTING)))
{
if (commandFromClient.equals("addProjectPrepare"))
{
String[] addArray = (String[]) ois.readObject();
core.addProjectPrepare(addArray);
}
if (commandFromClient.equals("editProjectPrepareDetails"))
{
String[] editArray = (String[]) ois.readObject();
recruit.editProjectPrepareDetails(editArray);
}
}
commandFromClient = fromClient.readLine();
}
**// CLIENT SIDE (User GUI) CODE THAT SENDS STRING[] TO THE SERVER**
public void saveAction()
{
// TEST TO SEE IF THE DATE ENTERED IS CORRECT FORMAT, IF NOT NO SAVE OCCURRS
boolean parsedOk = false;
if (this.arrivalDateTextField.getText().isEmpty() == false)
{
try
{
// Check if date is correct format. Nothing will be done with
// the testDate object
MyDate testDate = new MyDate(
this.arrivalDateTextField.getText());
//Allow write to server to occur.
parsedOk = true;
//If date is okay, send form data to server.
}
catch (ParseException pe)
{
this.arrivalDateTextField.setText(""); // Set text field to blank
int messageIcon = javax.swing.JOptionPane.ERROR_MESSAGE;
JOptionPane.showMessageDialog(this, "Invalid date",
"Warning", messageIcon);
}
}
else
{
parsedOk = true; // No date entered so allow blank.
}
if (parsedOk == true)
{
// WRITE DATA TO SERVER OCCURS HERE:
try
{
**//getPersonDetails() returns a String[]**
ManageClientConnections.toServer.println("addNewData");
ManageClientConnections.objectToServer.writeObject(this.getPersonDetails());
ManageClientConnections.objectToServer.flush();
ManageClientConnections.objectToServer.reset();
}
catch (IOException ioe)
{
System.out.println(
"While writing new person to server, there was an error: " + ioe.getMessage());
}
// And dispose of the GUI, inside the parseok if clause
this.dispose();
}
}

You can't create multiple input/output streams over the same socket input/output streams. that doesn't work. you need to pick one type of stream and stick with it. Since you need to send structured data, you should use only the Object streams and ditch the Print streams. if you need to send different types of messages from client to server, then you should consider using a wrapping Serializable object type (e.g. Message) which can contain different types of messages.

Related

java readInt() on server doesn't get the good int

I'm implementing a "little" client/server app, like a Cloud.
The problem is: I'm new to Java.
So I've learned it a bit fast, and same for client/server communication, database, frames, threads.
I'm pretty sure my code won't be the best one, but the fact is: I have to use Java, and I have to do this fast. I won't try to optimize it, I just need it to work.
I have already implemented a lot, so I won't give all the code here, just explain what happens before my problem:
The main prog on client side opens a login frame. The client can register or login. Registration is working well, so let's say he logs in. If authentication works, that opens another frame, with "browse" and "upload" options. The idea is, he browse a file and then upload it. When he clicks on upload, it should call the upload function, which will send a byte[2] array to the server with a DataOutputStream object. (it sends first the size of the array, and then the array)
On the server side, when authentication works, it'll give the client socket to a "Cloud" class, which - until now - is just supposed to receive a byte array (it'll do more later, but for now I can't get this byte array)
But the server receives a size of 1970302063 instead of 2. I've checked the size before the writeInt on client size, it's 2. After the readInt on the server side, it's 1970302063.
I don't understand it. Can the server receive something else somewhere and my beautiful 2 be lost in a buffer?
Server side:
public class Server {
public static void main(String[] zero) throws IOException {
ServerSocket serverSocket ;
Socket clientSocket ;
int tmp = 0; //when everything works, tmp will disappear
DataBase DB = new DataBase();
System.out.println("ouverture du server");
serverSocket = new ServerSocket(2017);
while(tmp<1) { //limit the number of connections allowed.
try {
clientSocket = serverSocket.accept();
Thread t = new Thread(new Connect(clientSocket, DB));
t.start();
//clients.printAll(); //just to see the DB.
}catch (IOException e) {
e.printStackTrace();
}
tmp++;
}
serverSocket.close();
}
}
As you can see, I'm authorising just 1 connection, because I'm still testing it and I don't want to change the port number each time I got an error / change something. With that I'll just have to restart server after each test.
public class Connect implements Runnable{
private DataBase DB;
private Socket clientSocket;
public Connect(Socket socket, DataBase DB) {
this.clientSocket = socket;
this.DB = DB;
}
public void run() {
try {
BufferedReader in;
PrintWriter out;
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(clientSocket.getOutputStream());
String mode = in.readLine();
String clientID = in.readLine();
String clientPwd = in.readLine();
if (mode.equals("auth")) {
Authentification auth = new Authentification (clientID, clientPwd, DB);
out.write(auth.getMessage()+"\r\n");
out.flush();
if (auth.getMessage().equals("Login Successfull. Welcome in SecureCloud!"))
new Cloud(clientSocket, DB); //launch Cloud.
}
else if (mode.equals("reg")) {
Registration reg= new Registration(clientID, clientPwd, DB);
out.write(reg.getMessage()+"\r\n");
out.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
this.clientSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Now, here: authentication and registration will set up a connection, and the client socket is closed at the end. BUT, if the authentication is successful, then it create a new Cloud:
public class Cloud {
public Cloud(Socket clientSocket, DataBase DB) {
try {
DataInputStream dIn = new DataInputStream(clientSocket.getInputStream());
int length = dIn.readInt(); // read length of incoming message
System.out.println("byte array size: "+length);
byte[] shorthash;
if(length!=2) {
System.err.println("Incorrect size for shorthash!");
}
else {
shorthash = new byte[length];
dIn.readFully(shorthash, 0, shorthash.length); // read the message
System.out.println(shorthash);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
As you can see, for now it doesn't do much. I know I'll have to add a while(true) loop so that the client can upload more files, but for now I'll be happy if I can send just this byte array.
Now, client side. I'll skip the main, it just open the login frame. I'll also skip most of the login Frame, it's just a frame...here is what happens when client click on "login":
public void actionPerformed(ActionEvent e)
{
if (e.getSource()==connectButton) {
Connection con = new Connection();
con.auth(username.getText(), password.getText());
if(con.getServerAnswer().equals("Login Successfull. Welcome in SecureCloud!")) {
this.setVisible(false);
con.setConnection(true);
con.setUsername(username.getText());
new Load_Frame(con.getUsername(),con.getSocket());
}
else System.out.println("erreur: " + con.getServerAnswer());
}
else if (e.getSource()==registerButton) {
this.setVisible(false);
new Registration_Frame();
}
}
So, it creates the connection and launch the authentification process (con.auth) with username and password. If it's successfull, it'll open the Load Frame with the username and socket used for this connection.
I'll skip again most of Load Frame, here are the actions:
public void actionPerformed(ActionEvent e)
{
if (e.getSource()== uploadButton) {
this.filename = uploadField.getText();
File file = new File(filename);
//TODO: change the upload function:
try {
client.upload(file);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
else if (e.getSource()== downloadButton) {
this.filename = downloadField.getText();
//TODO: change the download function
//download(filename);
}
else if (e.getSource()== browseButton) {
JFileChooser jc = new JFileChooser();
if(jc.showOpenDialog(this) != 1)
uploadField.setText(jc.getSelectedFile().getAbsolutePath());
}
}
And, last but not least, the "upload" function, called by client.upload(file):
public void upload(File originalFile) throws NoSuchAlgorithmException, NoSuchPaddingException, IOException {
//create the "FileData" object, which make the shorthash of the file.
FileData myfile = new FileData(originalFile, fileID);
//say to the server that we want to upload:
PrintWriter mode;
mode = new PrintWriter(socket.getOutputStream());
mode.println("upload");
mode.flush();
//Send shorthash to the server:
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
System.out.println("byte array size: "+myfile.shortHash.length);
out.writeInt(myfile.shortHash.length); // send the length of the byte array
//out.flush();
out.write(myfile.shortHash); //send the array
}
I've tried with and without the out.flush(), it doesn't make any difference.
With all that, here are the answers I get:
From client:
Asking for authentification...
Login Successfull. Welcome in SecureCloud!
No file uploaded
byte array size: 2
From server:
Connection to SQLite has been established.
ouverture du server
byte array size: 1970302063
Incorrect size for shorthash!
Of course, since I know the size of the byte array, I could easily tell the server "hey, the size is 2!" (I mean, directly initialize my array with new byte[2] )
But I would like to understand what happens here. Plus, if I'm not receiving the good size, maybe I won't receive the good array.
So, thanks to the people who find the problem really fast :)
It was a bit of stupid, the kind of problem you don't see even if you reread your code 5 times, but then someone else read it he see that in a minute.
The solution is to delete this part in the upload function:
//say to the server that we want to upload:
PrintWriter mode;
mode = new PrintWriter(socket.getOutputStream());
mode.println("upload");
mode.flush();
it was usefull until i changed the code on server side, and now it's just annoying ^^

Sending objects through sockets java TCP server to android app

I'm writing a client-server pair where the server is a java TCP server running on Linux and the client is an Android app developed in Android Studio.
I've successfully made a client-server pair that sends Message objects to each other, but when I try to implement similar functionality in my Android app nothing seems to happen.
The Android app works while just sending Strings with the readLine() and println() methods from the BufferedReader and Printwriter classes, but not with the readObject() and writeObject() from ObjectOutput / InputStream classes.
Have also tried writeUnshared() / readUnshared() methods without luck.
//Message.java
package Message;
import java.io.*;
public class Message implements Serializable {
String msg;
String tag;
String username;
private static final long serialVersionUID = 4L;
// Methods.
}
// Reading MessageObjects in Server.java.
#Override
public void run() {
Message message = null;
try {
while ((message = (Message)reader.readObject()) != null) {
// Processing message.
}
}
}
// Sending Message Objects in android App.
public void onClick(View v) {
if(!msgBox.getText().toString().equals("")) {
final String msg;
try {
msg = msgBox.getText().toString();
writer.writeObject(new Message(msg, CLIENT, username));
msgBox.setText("");
writer.flush();
// Updating ui etc.
}
catch (IOException e) {
e.printStackTrace();
}
textBox.smoothScrollBy(textBox.getMaxScrollAmount(), 100);
}
}
// Connection-method in android app, initalizes streams.
private boolean connect(String username, String address, int port) {
boolean connected = false;
try {
server = new Socket(address, port);
InputStreamReader(server.getInputStream());
reader = new ObjectInputStream(server.getInputStream());
writer = new ObjectOutputStream(server.getOutputStream());
writer.writeObject(new Message("!newUser",AUTOMATED,username));
writer.flush();
connected = true;
System.out.println("Connected!");
}
catch (Exception ex) {
ex.printStackTrace();
System.out.println("Cannot Connect!");
connected = false;
// UI-things.
}
if(connected){
// Thread that listens for replies.
listenThread();
}
return connected;
}
You need to create the ObjectOutputStream before the ObjectInputStream, at both ends. Otherwise you can get a deadlock.
Your read loop is incorrect. readObject() doesn't return null at end of stream, so using null as a loop condition doesn't make sense. It can return null any time you send a null. The loop should terminate when EOFException is caught.

Java Socket (TCP) Client Callback Functions

I am writing a simple Java Socket Client that is able to connect to a socket and pass a certain request to the server. Pretty much what I am trying to do:
Java Client connects to socket sends request to server
Server processes request and sends back "starting"
Java Client waits for next progress update
Server sends "progress 10%"
Java Client receives the progress update and processes accordingly
Server sends progress update 20%... so on so forth
I do not want to constantly check if there is something in the input stream but rather have the client retrieve the data as soon as there is something pending and process it (on a background thread). I am fairly new to working with java and networking in general so I don't know much about implementing this.
Server code (python, running on a raspberry pi):
import SocketServer
from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler
import sys
import time
import socket
# We mix with ThreadingMixIn to allow several simultaneous
# clients. Otherwise, a slow client may block everyone.
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
pass
# StreamRequestHandler provides us with the rfile and wfile attributes
class RequestHandler(StreamRequestHandler):
def handle(self):
print str(self.client_address) + 'connected'
data = "foo"
while data != "":
data = self.rfile.readline()
print data
try:
self.wfile.write("foo\n")
except socket.error: # Client went away, do not take that data into account
data = ""
print 'Handler Exiting'
self.request.close()
def finish(self):
print 'Client Disconnected'
if __name__ == '__main__':
PORT = 80
ThreadingTCPServer.allow_reuse_address = True
server = ThreadingTCPServer(("", PORT), RequestHandler)
try:
server.serve_forever()
except KeyboardInterrupt:
print "\nServer Terminated"
Basically at the end of prototyping I will be implementing this into an Android app. If you were wondering I am planning on using this to run some home automation and some other things.
This is the current client code I am working with (from an example online and I modified it a little):
import java.lang.*;
import java.io.*;
import java.net.*;
public class ClientTest {
private Socket socket = null;
private BufferedReader reader = null;
private BufferedWriter writer = null;
public ClientTest(InetAddress address, int port) throws IOException {
socket = new Socket(address, port);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
}
public void send(String msg) throws IOException {
writer.write(msg, 0, msg.length());
writer.flush();
}
public String recv() throws IOException {
return reader.readLine();
}
public static void main(String[] args) {
try {
InetAddress host = InetAddress.getByName("192.168.1.15");
ClientTest client = new ClientTest(host, 80);
//ClientTest client2 = new ClientTest(host, 9999);
for (int i = 1; i <= 1000; i++) {
client.send("bar " + i + "\n");
String response = client.recv();
System.out.println("" + i + ": " + response);
try {
Thread.sleep(15);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//client2.send("Client2\n");
}
client.socket.close();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//client2.socket.close();
} catch (IOException e) {
System.out.println("Caught Exception: " + e.toString());
}
}
}
Instead of String response = client.recv(); I want to have this function automatically called whenever the server sends back a request and have the message processed in the background. (I think it is somewhere along the lines of a callback function)
All answers appreciated!

Java ObjectOutputStream's Method flush()

I am currently learning networking, specifically client-server classes.
I have done much research and implemented various test programs but I can't figure out why/when one would need to use the flush() method.
How can there be data mistakenly left in the output stream if it is always read in by the input stream? As dictated by the client-server code.
I tried to test my basic echo client server program by omitting the flush() but I could not break it.
When testing the flush() by writing twice from the client side and only reading once for the server's reply all that happened was a backlog (I assume the stream acts like a queue?) in the server's replies.
Then I took the same code and added flush() before and after the second write and it made no difference. It's as if the flush() doesn't actually clear the stream.
So can someone please explain in what scenario with regards to client/server stream interactions would flush() be required?
Server:
public class ServerApp
{
private ServerSocket listener;
private Socket clientCon;
public ServerApp()
{
try
{
listener = new ServerSocket(1234, 10);
} catch (IOException e)
{
e.printStackTrace();
}
}
public void listen()
{
try
{
System.out.println("Server is listening!");
clientCon = listener.accept();
System.out.println("Server: Connection made with Client");
processClient();
} catch (IOException e)
{
e.printStackTrace();
}
}
public void processClient()
{
try(ObjectOutputStream out = new ObjectOutputStream(clientCon.getOutputStream()); ObjectInputStream in = new ObjectInputStream(clientCon.getInputStream()))
{
String msg;
while(!(msg = (String)in.readObject()).equalsIgnoreCase("Shutdown"))
{
out.writeObject("Server: " + msg);
out.flush();
}
out.writeObject("Server is powering down...");
out.close();
in.close();
} catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
}
}
public static void main (String args[])
{
ServerApp sa = new ServerApp();
sa.listen();
}
}
Client:
public class ClientApp
{
private Socket serverCon;
public ClientApp()
{
try
{
serverCon = new Socket("127.0.0.1", 1234);
} catch (IOException e)
{
e.printStackTrace();
}
}
public void communicate()
{
try (ObjectOutputStream out = new ObjectOutputStream(serverCon.getOutputStream()); ObjectInputStream in = new ObjectInputStream(serverCon.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)))
{
String response = null;
do
{
System.out.println("Enter your message for server: ");
out.writeObject(br.readLine());
out.flush();
out.writeObject("Flush not working");
out.flush();
response = (String) in.readObject();
System.out.println(response);
response = (String) in.readObject();
System.out.println(response);
} while (!response.equalsIgnoreCase("Server is powering down..."));
} catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
}
}
public static void main(String args[])
{
ClientApp ca = new ClientApp();
ca.communicate();
}
}
The method flush() is used to flush out any internal buffers that may be in use. For example using a BufferedOutputStream the contents are written in chunks to improve performance (it's slower to write each byte as they come).
Depending on usage, you might never have to call flush(). However let's say you send a small String (converted to byte[]) and it fits nicely in the internal buffer. The contents of the buffer won't be sent until the buffer is full or flush() is called.
Now let's say you're writing over the network, and you expect the other side to answer something to your small String. Since it's still in the buffer, the other side won't receive it and it can result in both sides waiting forever.
Object streams are another beast, and I'm a little disappointed that so many beginners are using them. There should be a warning in the class saying "Objects may be more difficult to send/receive than they appear".
ObjectOutputStream delegates the flush() call to its internal BlockDataOutputStream which has 3 buffers sized 1024, 5 and 256 for "blockdata", header data and characters respectively.
Try it with new ObjectOutputStream(new BufferedOutputStream(clientCon.getOutputStream())) and you'll see a difference with and without flush(). It causes flushing of the underlying buffered output stream. Without a buffered stream there is no buffer to flush so it does nothing.

URL Parsing with Java server using Runnable class

How can I parse URL queries with a system like this.
For Example something like get these URL arguments in variables.
http://localhost?format=json&apikey=838439873473kjdhfkhdf
http://tutorials.jenkov.com/java-multithreaded-servers/multithreaded-server.html
I made these files
WorkerRunnable.java
package servers;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.net.Socket;
/**
*/
public class WorkerRunnable implements Runnable{
protected Socket clientSocket = null;
protected String serverText = null;
public WorkerRunnable(Socket clientSocket, String serverText) {
this.clientSocket = clientSocket;
this.serverText = serverText;
}
public void run() {
try {
InputStream input = clientSocket.getInputStream();
OutputStream output = clientSocket.getOutputStream();
long time = System.currentTimeMillis();
output.write(("HTTP/1.1 200 OK\n\nWorkerRunnable: " +
this.serverText + " - " +
time +
"").getBytes());
output.close();
input.close();
System.out.println("Request processed: " + time);
} catch (IOException e) {
//report exception somewhere.
e.printStackTrace();
}
}
}
MultiThreadedServer.java
package servers;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
public class MultiThreadedServer implements Runnable{
protected int serverPort = 8080;
protected ServerSocket serverSocket = null;
protected boolean isStopped = false;
protected Thread runningThread= null;
public MultiThreadedServer(int port){
this.serverPort = port;
}
public void run(){
synchronized(this){
this.runningThread = Thread.currentThread();
}
openServerSocket();
while(! isStopped()){
Socket clientSocket = null;
try {
clientSocket = this.serverSocket.accept();
} catch (IOException e) {
if(isStopped()) {
System.out.println("Server Stopped.") ;
return;
}
throw new RuntimeException(
"Error accepting client connection", e);
}
new Thread(
new WorkerRunnable(
clientSocket, "Multithreaded Server")
).start();
}
System.out.println("Server Stopped.") ;
}
private synchronized boolean isStopped() {
return this.isStopped;
}
public synchronized void stop(){
this.isStopped = true;
try {
this.serverSocket.close();
} catch (IOException e) {
throw new RuntimeException("Error closing server", e);
}
}
private void openServerSocket() {
try {
this.serverSocket = new ServerSocket(this.serverPort);
} catch (IOException e) {
throw new RuntimeException("Cannot open port 8080", e);
}
}
}
Dispatch.java
package servers;
public class Dispatch {
/**
* #param args
*/
public static void main(String[] args) {
MultiThreadedServer server = new MultiThreadedServer(9000);
new Thread(server).start();
try {
Thread.sleep(20 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Stopping Server");
server.stop();
}
}
You're doing fine so far.
Read the data off of the InputStream (BufferedReader might help) one line at a time.
Read and learn the HTTP Protocol (see Request Message section here: http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol).
The first line that the client sends is going to follow that format: GET /foo.html?x=y&a=b HTTP/1.1 followed by \n\n that's the Method, URL (with query parameters) and Protocol. Split that line (on the spaces...) and then break the URL up according to the specs.
Everything you need can be found in the String class for parsing the data.
You have forgotten to read what the clients sends. In http the clients opens the connection and than sends the request and waits for the server to reply.
To read the request you have two options. Use a BufferedReader or read it byte by byte.
The BufferedReader is easier. You get a String for every line and can easily split it or replace characters, or whatever ;)
Reading every byte is a little bit faster, but it will only be relevant if you need to serve a huge amount of request per seconds. Than this can really make a difference. I just put this information just so you know ;)
I have included the necessary part for reading in your WorkerRunnable.java.
This reads and prints out the whole client request.
Start your server, open your browser and type: http://127.0.0.1:9000/hello?one=1&two=2&three=3
The First line on the Console will read: GET /hello?one=1&two=2&three=3 HTTP/1.1
Before closing an OutputStream, be sure to call the flush() method. This will force any buffered bytes to be written out. If you don't do it, than there might be some bytes/characters missing and you might be spending a long time looking for the error.
try {
InputStream input = clientSocket.getInputStream();
// Reading line by line with a BufferedReader
java.io.BufferedReader in = new java.io.BufferedReader(
new java.io.InputStreamReader(input));
String line;
while ( !(line=in.readLine()).equals("") ){
System.out.println(line);
}
OutputStream output = clientSocket.getOutputStream();
long time = System.currentTimeMillis();
output.write(("HTTP/1.1 200 OK\n\nWorkerRunnable: " +
this.serverText + " - " +
time +
"").getBytes());
output.flush();
//Flushes this output stream and forces any buffered output bytes to be written out.
output.close();
input.close();
System.out.println("Request processed: " + time);
I don't know exactly what you are doing there. You just told us you need to parse the URL, but maybe a better way is to use the simpleframework (http://www.simpleframework.org)
It is like an embedded HTTP-Server, you can look at the tutorial. It will give you a request object, from there you can easily fetch the parameters in the url.
Technically speaking, you can, but it would leave you with implementing the http protocol on your own.
A much better option would be to use the Java Http Server from Oracle. See the following article for tips http://alistairisrael.wordpress.com/2009/09/02/functional-http-testing-with-sun-java-6-httpserver/

Categories

Resources