OOP Lab Task 5
De la WikiLabs
Jump to navigationJump to searchRequired Tutorials
- The Object Oriented Paradigm; Classes and Objects
- Notions About the Java Language
- Writing and Executing a Java Program
- Java Syntax; A Program's Lexical Structure
- Coding Conventions
- Advanced Notions About Object Oriented Programming
- Java Application Programming Interface (API) (EN)
- Exception Handling
- Input/Output Streams
- Serialization
- Network Sockets
Requirements
- For this task you need the previously designed class Message, from OOP Lab Task 1. All your classes must be in the labutil package.
- Add a class called
ClientPeer
with the following features:- Only one constructor, that takes as arguments a string (the user/sender name) and a reference to a Socket object, that is supposed to be already instantiated and connected, and saves them in private fields.
- A method
void sendMessage(String message)
, that creates a Message object with the content taken from the method's argument and the sender's name read from the class' fields, and sends the newly created object through the available Socket.- In order to be able to send a Message object through a stream (to serialize that object), that object must be serializable.
- The object is sent through the output stream of the Socket. You get the reference to that stream using the Socket's method
getOutputStream
, but to be able to send objects through this stream, which is only a basic stream of bytes, you need to enclose it in a filter stream of typeObjectOutputStream
(this object does the serialization for you, that is it converts the Message object into a stream of bytes).
- A method named
close
, without arguments and return values, that symply closes the output stream. - The output stream is created at ClientPeer instantiation, used by sendMessage method which writes Message objects into, and closed by close method.
- The constructor and both methods employ a socket and a stream, whose methods may throw various exceptions of type IOException when called. You do not catch them, but, because they are checked exceptions, you must declare them to be thrown by the constructor and the methods.
- All fields must be private.
- Add to the labutil package another class, named
Client
, which is executable (has a main method). This class has- A constructor, with two arguments, a string for an internet address, and an integer for a port number. This constructor creates a Socket object and connects it to the desired remote socket, whose address and port are given as arguments.
- The Socket reference must be an instance field so that it may be used by the other methods of this class.
- A method named
run
, without arguments, which does the following:- First, it instantiates a ClientPeer object with your name as an argument (you are the sender!) and the reference to the Socket object as the other argument.
- After that, open an input stream of characters to read from the keyboard. The input stream from the keyboard is already enclosed in the object System.in, which is opened and available by default. However, this stream is a byte stream, therefore you need to filter it properly, so that the bytes are transformed to characters. To do that, you enclose the System.in stream into an
InputStreamReader
object. The Reader object (InputStreamReader is derived from Reader) delivers the stream character by character. To read a whole line from the keyboard without calling the read method for each character, you enclose further the InputStreamReader into aBufferedReader
object, whose readLine method fits this purpose. The readLine method returns (with a string) only after you type the Enter key (but the newline character will not be part of the returned string). - Then, read a string of characters from the keyboard (from the previously opened input stream) and call the sendMessage method of the ClientPeer object. Your typed messages will thus be sent to the destination.
- You may send messages in a continuous loop (type a line, press Enter, type another line, press Enter a.s.o).
- To break the loop, you test for a special typed line, QUIT (it will act like a command, not a message to be sent). Remember how to check that two strings are equal!
- After you finish with sending messages don't forget to close the output stream of the ClientPeer object, the input stream and the socket! To be sure that they are closed in any circumstances, put the statements that closes them in a finally block, atached to a try block that encloses the other part of the main method. Be careful, the references used inside the finally block must be declared outside the try block and initialized with some value (the initialization value may be null).!
- Because the run method operates with sockets and streams it may also raise checked exceptions of type IOException. You do not catch them; simply declare that the method run throws them out.
- The main method instantiates a Client object with some destination IP address (try first with 127.0.0.1), and port 9000 (that connects the client's socket to the destination's socket, the localhost (127.0.0.1) on port 9000), and calls the method run for the newly created Client object.
- Because both the Client constructor and the run method may generate checked exceptions, throw them further outside the main method.
- You may run the Client class, to see that it compiles, builds and runs, but the execution will end abruptly with an exception because there is still no server running, to which your client might connect (Connection refused). The following steps are devoted to the other part of the connection, the receiver of messages, which also acts as the server.
- Add to the labutil package a class called
ServerPeer
with the following features:- The constructor has as its sole argument a reference to a pre-initialized Socket object, that it stores in a private field.
- The method
void run()
opens an input stream for this socket, reads messages from it, and displays them on the screen, formatted as specified in OOP Lab Task 1.- You get the reference to that stream using the Socket's method
getInputStream
, but to be able to receive objects through this stream, which is only a basic stream of bytes, you need to enclose it in a filter stream of typeObjectInputStream
(this object does the deserialization for you, that is it converts the stream of bytes into a Message object). However, you need to explicitly downcast the return type of ObjectInputStream, which is Object, to your expected Message type, if you want to call methods that are specific only to Message objects. - The formatted message is printed on the screen with the help of Message methods.
- Both the constructor and the method run employ a socket and a stream, whose methods may throw various checked exceptions of type IOException when called. You do not catch them; simply declare that they throw them out.
- In addition, the downcast may throw a ClassNotFoundException, that you also simply propagate out of the method (you do not expect other types than Message to receive from the stream, but this exception is checked, so it must be explicitly handled somehow).
- Do these reads in a continuous loop (read a Message object from the stream, display the formatted message, read another object, display the new message, a.s.o.).
- As long as the connection is open, the readObject method waits for a complete object to be received from the stream and then returns with a reference to it. If the connection is closed (by the other side for example) the method aborts and throws the EOFException. Use this exception to neatly end the read loop. For this purpose, enclose the continuous loop in a try block and attach a catch for this particular exception, whithout statements. This catch will simply serve to continue the program, instead of aborting it with an exception. If you want a more realistic output, you may print on the screen, after the EOFException was caught, the message The client has disconnected. or something like that.
- Don't forget to close everything after the connection is closed (and you exited from the loop). You have just added a try block, so you need only to attach to it the finally block with statements to close the input stream and the socket. Be careful, the references used inside the finally block must be declared ouside the try block!
- You get the reference to that stream using the Socket's method
- The last to be added to your package is a class called
Server
, which is executable (has a main method). It encloses:- One constructor, with a single argument which is the public port number of your server. This constructor instantiates a ServerSocket object connected the given port.
- The ServerSocket reference must be an instance field, so that it may be used by the other methods of this class.
- A method, named
void run()
, without arguments, which:- calls the ServerSocket's method accept, to listen for clients. This method returns after a client request is accepted and a connection with that client is established. The method returns a reference to a local Socket object created specifically for this connection. You use this Socket reference to instantiate a ServerPeer object.
- calls next the run method for this newly created ServerPeer object.
- both the accept method and the run method of the ServerPeer may generate exceptions. You do not handle them, but they must be declared as thrown by the method.
- it must return only after the connection is closed (remember, you just added the proper try-catch construction to handle the EOFException for the run method of the ServerPeer. It closes only the Socket object. However, the ServerSocket is up and you may call again its accept method to connect to another client, a.s.o.
- Don't forget to close the SeverSocket object after you decide to no longer accept clients and to exit your program. Use the try/finally blocks to be sure that the ServerSocket will be always closed.
- If it still has compilation errors it's because you forget about that annoying exceptions. The run method uses various sockets and streams, that may throw exceptions ... already a classic story. Don't bother with them, simply append the necessary declaration to the main method signature. In addition to IOExceptions, a ClassNotFoundException may be thrown from run, so it needs to be declared too, or you declare their base class of exceptions to be thrown.
- The main method instantiates a Server object attached to some port (for example 9000), and calls its run method.
- One constructor, with a single argument which is the public port number of your server. This constructor instantiates a ServerSocket object connected the given port.
- Run the Server class (with the Server.java opened in the editor window right-click and select Run File, or click Run->Run File from the general Menu). If there are no errors the program runs, but nothing is output, and it appears to run without end. It's because there are no clients running around and it listens continuously waiting for some client to appear (the accept method returns only after it catches a client, or something bad happens).
- How to test your classes? Actually you have two separate applications, a server and a client. You have just started the server, but it runs without end waiting for a client to send messages. You need to start separately a client. To do that, switch the editor window to Client.java and run that file too (right-click and select Run File, or bla bla). A second output tab appears. You have now two programs running at the same time, with their outputs displayed on separate tabs in the output window at the bottom of the NetBeans environment.
- Type something in the output window of the Client and press Enter. Look to the other tab and see that the Sever outputs the message you have just typed. Try with other messages. To close the Client, type QUIT and press Enter. Depending on how you have written the run method of the Server class, the server will end too, or it will continue, listening for other clients to connect (in which case you run again the Client.java, type something a.s.o)
- If you want more fun, and know the IP address of the computer of your colleague, ask him to open a server on some port, change your Client class to open a Socket with that IP instead of the localhost IP 127.0.0.1, and the desired port, then run locally your client and type messages for your colleague. Please, send him only peacefull messages!
Submitting
- The assignment will be evaluated automatically by the Web-CAT platform.
- You could access the Web-CAT platform using the username and the password with which you acces the electronica.curs.pub.ro intranet.
- Select the OOP Lab Task 5 assignment.
- Submit your work as a single .zip archive (give it whatever name you choose) containing only the Java source code files.
- Attention Any deviation from these instructions may lead to the loss of the entire amount of points.