I'm already taking some questions from database with JDBC, resultsets, etc... then putting this into arraylists.. and show all via Java GUI.
However now i want to extend this using sockets, threads etc.. to learn this staff.
This time i want to use three tier architecture approach, where the database access is
sorted in the application layer which interacts the same old database.
Application layer is going to act like a server which accept incoming
requests of multiple clients and provide the interaction with the data level.
I try something like this;
Server side: taking questions(20 tf questions, 8 multiple), writing to arraylist and sends to the client.
I send only tf questions. How can i send also multiples ?
Is this true way to do this ?
If threads are necessary ? How can i implement threads to this ?
public class trainingServer {
static ArrayList<TrueFalse> truefalseList = new ArrayList<TrueFalse>();
static ArrayList<Multiple> multipleList = new ArrayList<Multiple>();
static ArrayList<String> clientAnswers = new ArrayList<String>();
public static void main(String[] args) throws IOException, SQLException, ClassNotFoundException {
Connection con;
Statement st;
ResultSet resultSet, resultSet2;
ServerSocket serverSocket = null;
Socket clientSocket = null;
ObjectOutputStream out = null;
ObjectInputStream in = null;
PrintWriter outScore = null;
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost/db", "root", "");
st = con.createStatement();
try {
serverSocket = new ServerSocket(9999); //listening on port 9999
} catch (Exception e) {
System.out.println("Port Error!");
}
System.out.println("Server is ready for connection..");
clientSocket = serverSocket.accept();
out = new ObjectOutputStream(clientSocket.getOutputStream());
in = new ObjectInputStream(clientSocket.getInputStream());
outScore = new PrintWriter(clientSocket.getOutputStream(), true);
resultSet = st.executeQuery("SELECT No, Question, Answer FROM truefalse");
//adding true/false questions and their answers to tfList from database
while (resultSet.next())
{
TrueFalse qa = new TrueFalse();
qa.number=resultSet.getInt(1);
qa.question=resultSet.getString(2);
qa.answer=resultSet.getString(3);
truefalseList.add(qa);
}
//writing true-false part to object output stream to send client
// for(TrueFalse tf : truefalseList)
// out.writeObject(tf);
resultSet2 = st.executeQuery("SELECT No, Question, Ans1, Ans2, Ans3, Ans4, Ans5, Explanation, Trueans FROM multiple");
//adding multiple questions and their answers to multList from database
while (resultSet2.next())
{
Multiple qm = new Multiple();
qm.num=resultSet2.getInt(1);
qm.question=resultSet2.getString(2);
qm.answer1=resultSet2.getString(3);
qm.answer2=resultSet2.getString(4);
qm.answer3=resultSet2.getString(5);
qm.answer4=resultSet2.getString(6);
qm.answer5=resultSet2.getString(7);
qm.explanation=resultSet2.getString(8);
qm.trueAns=resultSet2.getString(9);
multipleList.add(qm);
}
//writing mult. part to object output stream to send client
// for(Multiple mult : multipleList)
// out.writeObject(mult);
out.writeUTF("Server ready");
out.flush();
// If we are here, then connection was probably not a portscan
out.writeObject(truefalseList);
out.writeObject(multipleList);
out.flush();
clientAnswers = (ArrayList<String>) in.readObject();
// score = (Score)in.readObject();
// do something with score.
outScore.println(checkAnswers());
outScore.flush();
out.close();
in.close();
outScore.close();
clientSocket.close();
serverSocket.close();
}
static int checkAnswers(){
int score=0;
for(int i=0 ; i<clientAnswers.size() ; i++)
{
if(i<20)
{
if(clientAnswers.get(i).equalsIgnoreCase(truefalseList.get(i).answer))
{
score=score+3;
}
}
else
{
int count=0;
if(clientAnswers.get(i).equalsIgnoreCase(multipleList.get(count).trueAns))
{
score=score+5;
count++;
}
}
}
return score;
}
}
Client side: (except gui staff etc..)
Socket soket = null;
ObjectInputStream in = null;
ObjectOutputStream out = null;
BufferedReader inScore = null;
try {
soket = new Socket("localhost",9999);
in = new ObjectInputStream(soket.getInputStream());
out = new ObjectOutputStream(soket.getOutputStream());
inScore = new BufferedReader(new InputStreamReader(soket.getInputStream()));
String serverReady = in.readUTF();
tfList = (ArrayList<TrueFalse>) in.readObject();
multList = (ArrayList<Multiple>) in.readObject();
// score = new Score();
// prepare the score object
out.writeObject(userAnswers);
out.flush();
// wait for server to process score
returned_score = inScore.read();
System.out.println(returned_score);
// server has processed score and send "Bye".
} catch (ConnectException ce) {
System.out.println("Cannot connect to the server!");
} catch (IOException ie) {
System.out.println("I/O Error!");
}
in.close();
out.close();
inScore.close();
soket.close();
The true way of doing this depends on your non-functional requirements. If you expect non-Java clients to request the (status) data from the database, or clients are behind a proxy, then write your application server as a web-service.
Since you are writing a server that clients will depend on, also think about scalability (deploy the web-service on multiple web-servers behind a load-balancer) and availability (if one web-server goes down, the load-balancer should direct requests to other web-servers that are still up). And don't forget maintainability: ideally you should be able to upgrade/downgrade your application server without clients noticing.
If there are only Java clients (or the data on the line is only question-answer type text like with telnet), then you will need threads and also a database connection pool. I have used Yapool to create an "always on" application server like this. It contains a ServerSocket that you can use/look at to learn this stuff (see also the unit tests). A database pool implementation is also included.
I have also combined Yapool with a basic JSP web-service framework to create a war-file that can be deployed on Tomcat, exposing the application server as a (very simple) web-service. The advantage of a web-service like this is that you do not need to worry (much) about threads and connections: Tomcat does that for you. If you want to take it a step further, I suggest learning about REST.
As for receiving two types of lists: you are inventing your own protocol here and are free to send and receive data to/from the client as long as the client mirrors the server protocol exactly. You can send/receive any kind of object with the ObjectInputStream/ObjectOutputStream as long as it is serializable. Lists and Maps are already serializable.
For example server protocol:
out.writeUTF("Server ready");
out.flush();
// If we are here, then connection was probably not a portscan
out.writeObject(tfList);
out.writeObject(multipleList);
out.flush();
clientAnswers = (List<String>) in.readObject();
out.writeInt(checkAnswers());
out.writeUTF("Bye");
out.flush();
With the following client protocol:
String serverReady = in.readUTF();
tfList = (List<TrueFalse>) in.readObject();
multipleList = (List<Multiple>) in.readObject();
out.writeObject(userAnswers);
out.flush();
// wait for server to check answers and return score
int returned_score = in.readInt();
String serverBye = in.readUTF();
// server has processed score and send "Bye".
Related
I'm clearly missing something with network programming. I'm trying to write a program that sends a command to the server to perform a specific query on a sqlite database that is maintained on the server side. I want all SQL queries on the server side as well.
I need different methods to perform different functions by sending commands to a server. The only way I can establish a connection from the client side is by creating a new socket and input/output streams within the method, but I will have many, many methods corresponding to each button click on the GUI from the client side.
Is there a way I can use a single socket, and a single set of input/output streams to write these commands to the server, and reuse them? And if not, what do I do to make this work over and over for each button click.
I've gotten this working already but here's what it looks like:
For Clients Side
A method call to build a table of apartment tenants is called, command might be "1":
buildData(command)
within buildData:
public void buildData(String command, TableView table){
try {
Socket socket = new Socket("localHost", 5000);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.flush();
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
out.writeUTF(command);
System.out.println("Build Data Success");
System.out.println(in.readObject());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
Server Side
private static String tenantQuery = "SELECT * FROM tenants;";
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(5000);
Socket conn = server.accept();
System.out.println("Connected");
DataInputStream input = new DataInputStream(new BufferedInputStream(conn.getInputStream()));
ObjectOutputStream out = new ObjectOutputStream(conn.getOutputStream());
while(true){
command = input.readUTF();
switch (command){
case "1":
returned = buildData(tenantQuery);
out.writeObject(returned);
}
}
}
And then in the corresponding server method buildData(query):
public static ArrayList<String> buildData(String query){
try {
Class.forName("org.sqlite.JDBC").newInstance();
c = DriverManager.getConnection("jdbc:sqlite:FinalRental.db");
data = new ArrayList<>();
ResultSet rs = c.createStatement().executeQuery(query);
while (rs.next()) {
//Iterate Row
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
//Iterate Column
data.add(rs.getString(i));
}
}
System.out.println("SUCCESS!!!");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException e) {
e.printStackTrace();
}
return data;
}
From what I can tell, if I were to do this and need to have functionality to then ADD a tenant into the tenant table, I would need to create a new socket, input/output streams in what would become an addTenant() method later on when a hypothetical "Add Tenant" button is pushed in the GUI.
Just to reiterate
What I've tried:
Creating one socket and set of output/input streams, but I couldn't use the input/output streams everywhere in the code.
The Problem:
I don't know how to use one socket and set of input/output streams to do many things, or if this is even a possibility.
Question:
How do I set up a client/server relationship so that I can get data from a database on the server and return it to the client to dynamically populate a GUI display on the client side?
I'm totally new in java.
I started to learn java network programming.
My problem: i put my server in infinite while loop to keep working.
I send a message to the server, when the server reads this message it should perform some action. it is performed successfully but after exiting my client program, the server also terminates although i put it in infinite while loop.
my client code:
private void check_ordersActionPerformed(java.awt.event.ActionEvent evt) {
try {
Socket ss = new Socket("localhost", 4000);
PrintStream pr = new PrintStream(ss.getOutputStream());
pr.println("Supplier Orders");
pr.println(Supplier.username);
pr.close();
this.dispose();
} catch (IOException ex) {
Logger.getLogger(supp_page.class.getName()).log(Level.SEVERE, null, ex);
}
my server code:
int iiiiii = 0;
ServerSocket s1 = new ServerSocket(4000);
String request = null;
while(iiiiii == 0)
{
Socket ss = s1.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(ss.getInputStream()));
request = in.readLine();
switch (request) {
case "Supplier Orders":
Supplier.username = in.readLine();
orders rs = new orders();
rs.setLocation(350, 250);
rs.setSize(800, 350);
rs.setVisible(true);
rs.setTitle("Orders Table");
rs.setResizable(false);
break;
// some other messages here but iam focus on above one
}
}
let me explain more: when i call this method it send a message to the client "Supplier Orders" and a username .... when server receives this message it should launch a gui page that contains some data of the that supplier i gave username to the server.
but when i close client the server also terminates ... that should not be happened. i want the server to keep working and not exit.
so how to fix this ? .. sorry for long post.
Edit
The code for gui page, it should show some data on table: it showed data successfully but as i mention when i close client, the server also closed which i don't want to happen, i want server to keep working not to close
public class orders extends javax.swing.JFrame {
DefaultTableModel t = new DefaultTableModel();
public orders() throws IOException, ClassNotFoundException {
initComponents();
request.setModel(t);
t.addColumn("ID");
t.addColumn("PRODUCT Name");
t.addColumn("PRODUCT TYPE");
t.addColumn("PRODUCT QUANTITY");
t.addColumn("SUPPLIER ID");
t.addColumn("SUPPLIER NAME");
t.addColumn("SUPPLIER COMPANY");
t.addColumn("SUPPLIER ADDRESS");
t.addColumn("SUPPLIER PHONE NUMBER");
ObjectInputStream oin = new ObjectInputStream(new FileInputStream("Orders.bin"));
ArrayList<Order> a = (ArrayList)oin.readObject();
Iterator<Order> iter = a.iterator();
oin.close();
while(iter.hasNext())
{
Order r = iter.next();
if(Supplier.username.equals(r.getP().getS().getId() + r.getP().getS().getCompany()) && r.getStatus().equals("PENDING"))
{
t.addRow(new Object[]{r.getP().getId(),r.getP().getName(),r.getP().getType(),r.getP().getQuantity(),r.getP().getS().getId()
,r.getP().getS().getName(), r.getP().getS().getCompany(), r.getP().getS().getAddress(), r.getP().getS().getPhone_no()});
}
}
}
}
The issue is that your client is closing the socket between the client and server when the client exits as DBug said. Since you're blocking on the in.readline() in the server an IOException (Socket Closed Exception) is thrown. Your code is not handling that IOException and your while loop won't be enough to prevent the server code from exiting. You need to add a few try/catches to catch these exceptions.
Consider adding a few finally blocks to ensure your sockets and streams get closed properly.
I'm new to java and especially to java networking, but what I'm trying to do is set up a server (for a game). When TWO clients are connected to the server, I want to refuse any other connection. Am I able to close the ServerSocket? And if the ServerSocked is closed, those two connections which has been ran as threads and stored in a collection are still alive and able to cummunicate with the server? I hope you've got my point.
Here's the source code:
//Server
public synchronized List<ClientThread> getClients(){
return clients;
}
public void run() {
try {
while(true){
Socket clsock = srvsock.accept();
if(getClients().size() == 2){
System.out.println("Too many connections!");
clsock.close();
continue;
}
ClientThread clt = new ClientThread(clsock);
clients.add(clt);
clt.start();
System.out.println("Connection accepted.");
}
} catch (IOException ex) {
ex.printStackTrace(System.err);
}
}
And with this code, I'm not able to detect on the Client if the connection is still alive, or the server has closed the connection. Thanks in advance.
Code for test client:
Socket s = new Socket("localhost", 8932);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
for (int i=0; i<20; i++) {
bw.write(String.valueOf(i));
bw.newLine();
bw.flush();
Thread.sleep(1000);
}
bw.close();
And for the ClientThread:
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
while (true) {
String misc = in.readLine();
System.out.println(misc);
if (misc==null || misc.length()==0)
break;
}
in.close();
Output:
Connection accepted.
0
Connection accepted.
0
1
Too many connections!
1
2
2
3
So it works as you intended. By the way, it is usually better to implement Runnable rather than extend Thread - see "implements Runnable" vs. "extends Thread"
Every time a connection is established, the server returns a new socket ( when you use srvsock.accept() ) for the next connection. So you can close "srvsock" without affecting "clsock". To test if they are alive, check this post How do I check if a Socket is currently connected in Java?.
Hope you can solve your problem.
I have a 2 nodes that should always communicate with each other, but they don't seem to talk for more than 1 interaction. After successfully sending and receiving 1 message, they stop.
My code looks like this:
The initiator:
try {
Socket client = new Socket(ip, port);
OutputStream toNode = client.getOutputStream();
DataOutputStream out = new DataOutputStream(toNode);
out.writeUTF("Start:Message");
System.out.println("Sent data");
InputStream fromNode = client.getInputStream();
DataInputStream in = new DataInputStream(fromNode);
if(in.readUTF().equals("Return Message")) {
System.out.println("Received data");
out.writeUTF("Main:Message");
System.out.println("Sent data again");
}
else
System.out.println("Error");
client.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
The responder:
while(true) {
Socket server;
try {
server = s.accept();
DataInputStream in = new DataInputStream(server.getInputStream());
String msg = in.readUTF();
String[] broken_msg = msg.split(":");
if(broken_msg.length > 0)
System.out.println("Looping");
String ret;
if (broken[0].equalsIgnoreCase("Start")) {
ret = "Return Message";
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF(ret);
}
else if (broken[0].equalsIgnoreCase("Main")) {
//Do Useful work
}
} catch (IOException e) {
e.printStackTrace();
}
}
My output looks like this:
Looping
and:
Sent data
Received data
Sent data again
You are looping around the accept() call, accepting new connections, but your actual I/O code only reads one message per connection. You need an inner loop around the readUTF() calls, handling the entire connection until EOFException is thrown. Normally all the I/O for a connection is handled in a separate thread.
In order for programs to do repetitive actions, you would generally use looping of some sort, including for loops, while loops and do-while loops. For something like this where you don't know how many times you'd need to communicate in advance, then you would need to use a while loop, not a for loop.
Having said that, you have no while loops whatsoever inside of your connection code... so without code that would allow continued communication, your program will stop, exactly as you've programmed it to do.
Solution: fix this. Put in while loops where continued communication is needed.
i have a java client-server app in java, both using the same connection class that contains both send/receive messages.
for some reason, some of the messages i send are received in a malformed order:
here's the code
//set up
_in = new BufferedReader(new InputStreamReader(this._socket.getInputStream()));
_out = new BufferedWriter(new OutputStreamWriter(this._socket.getOutputStream()));
this._socket.setSoTimeout(S_TIMEOUT);
public synchronized boolean send(String message){
try {
_out.write(message);
_out.write(Connection.DELIMITER);
_out.flush();
return true;
} catch (IOException e) {
}
return false;
}
public String receive(){
int c;
try {
String message = "";
System.out.println("Getting message:");
c = _in.read();
while(c != -1 && c != Connection.DELIMITER) {
message += (char) c;
c = _in.read();
}
if (c == -1) {
return null;
}
return message;
} catch (IOException e) { }
return null;
}
some messages, for example "new_order" will might return with "ew_ord".
some characters are lost, others are sent separately. this seems odd as its TCP
could this be an encoding related issue?
Delimiter is (char) 0
socket timeout is 20000 (ie 20 senconds). every 10 seconds i send an empty message to make sure socket does not close
EDIT:
although it was solved using the Scanner, i must say that the original code worked fine for many messages/various machines for a very long time (a few weeks), and then suddenly failed to work with one specific message on one specific machine (other messages went through just fine). i've done socket data transfer in java MANY times and i've written many read/write methods to handle the sockets. it's the first time i ran into this.
although in the original code i set the encoding (in the posted code i didn't), i believe that the problem was encoding related. at one point, the message that was received had every second character missing. afterwards i changed it a bit, and the first/second character of the message were received in a separate message. from my understanding, it's either an encoding issue or some firewall/other security program that was running on the message sender machine, that decided to filter outgoing packets.
Try replacing your receive with a Scanner and let it do the work for you.
// in your setup
Scanner sc = new Scanner(_in).useDelimiter(Connection.DELIMETER);
public String receive() {
try {
return sc.next();
} catch(IOException e) {
return "";
}
}
For starters, I would make sure you're printing exceptions in those catch blocks.
Then, you're using the platform default encoding for converting characters to bytes. If these two processes are running on different machines, it's possible they're using different encodings. I would make sure you're specifying an encoding when you set up the Reader and Writer.
You can use UTF encoding for getting Full String of Message.
U can try this code and I am Sure About this code because i used it in My Chat Application.
String data=" ";
socket = new Socket("localhost",999);
while(true)
{
dos = new DataOutputStream(socket.getOutputStream());
dis = new DataInputStream(socket.getInputStream());
data = dis.readUTF();
jta.append(data +"\n");
}
Where jta is JTextArea.
It's for Client Side
Now For Server Side:
try
{
server = new ServerSocket(999);
Socket soc = server.accept();
while(true)
{
String data="";
try
{
dis = new DataInputStream(soc.getInputStream());
dos = new DataOutputStream(soc.getOutputStream());
data = dis.readUTF();
}
catch(Exception e)
{ }
jta.append(data + "\n");
}
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
System.exit(-1);
}