Reputation: 111
i have designed a Socket base Server for android clients, which receives requests and deals with mysql database, now im expecting about 2000 requests per hour, im wondering is this server viable for that amount or even more? is there any thing else or any better protocol i should use instead socket to can deal with larger requests, but thre ThreadPoolExecutor?
public class Server {
// a unique ID for each connection
private static int uniqueId;
// an ArrayList to keep the list of the Client
private ArrayList<ClientThread> al;
// if I am in a GUI
private ServerGUI sg;
// to display time
private SimpleDateFormat sdf;
// the port number to listen for connection
private int port;
// the boolean that will be turned of to stop the server
private boolean keepGoing;
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost:3306/openfire";
static final String USER = "admin";
static final String PASS = "admin";
static boolean jobdone = false;
protected ServerSocket serverSocket = null;
protected boolean isStopped = false;
protected Thread runningThread= null;
Connection conn = null;
Statement stmt = null;
public Server(int port) {
this(port, null);
}
public Server(int port, ServerGUI sg) {
this.sg = sg;
this.port = port;
sdf = new SimpleDateFormat("HH:mm:ss");
al = new ArrayList<ClientThread>();
}
public void start() throws InterruptedException {
keepGoing = true;
try
{
// the socket used by the server
ServerSocket serverSocket = new ServerSocket(port);
// infinite loop to wait for connections
while(keepGoing)
{
// format message saying we are waiting
display("Server waiting for Clients on port " + port +
".");
Socket socket = serverSocket.accept(); // accept
connection
// if I was asked to stop
if(!keepGoing)
break;
ClientThread t = new ClientThread(socket); // make a
thread of it
jobdone=false;
al.add(t); // save
it in the ArrayList
t.start();
}
// I was asked to stop
try {
serverSocket.close();
for(int i = 0; i < al.size(); ++i) {
ClientThread tc = al.get(i);
try {
tc.sInput.close();
tc.sOutput.close();
tc.socket.close();
}
catch(IOException ioE) {
// not much I can do
}
}
}
catch(Exception e) {
display("Exception closing the server and clients: " + e);
}
}
// something went bad
catch (IOException e) {
String msg = sdf.format(new Date()) + " Exception on new ServerSocket: " + e
+ "\n";
display(msg);
}
}
/*
* For the GUI to stop the server
*/
protected void stop() {
keepGoing = false;
// connect to myself as Client to exit statement
// Socket socket = serverSocket.accept();
try {
new Socket("localhost", port);
}
catch(Exception e) {
// nothing I can really do
}
}
/*
* Display an event (not a message) to the console or the GUI
*/
private void display(String msg) {
String time = sdf.format(new Date()) + " " + msg;
if(sg == null)
System.out.println(time);
else
sg.appendEvent(time + "\n");
}
// create a server object and start it
public static void shutdown() {
jobdone = true;
}
/** One instance of this thread will run for each client */
class ClientThread extends Thread {
// the socket where to listen/talk
String Type;
Socket socket;
ObjectInputStream sInput;
ObjectOutputStream sOutput;
// my unique id (easier for deconnection)
int id;
String encrypted = "'adomgaldkopnfaosdppkdsad'";
// Constructore
ClientThread(Socket socket) throws InterruptedException {
// a unique id
id = ++uniqueId;
this.socket = socket;
/* Creating both Data Stream */
System.out.println("Thread trying to create Object Input/Output
Streams");
while (!jobdone){
try
{
// create output first
sOutput = new ObjectOutputStream(socket.getOutputStream());
sInput = new ObjectInputStream(socket.getInputStream());
// read the username
String RegisterRequest = (String) sInput.readObject();
String[] result = RegisterRequest.split("\\,");
String theuser = result[0];
display("theuser="+theuser);
String thepass = result[1];
display("thepass="+thepass);
String thename = result[2];
display("thename="+thename);
String themail = result[3];
display("themail="+themail);
String thephone = result[4];
display("thephone="+thephone);
String newRID = result[5];
display("RID="+newRID);
String OldRID=newRID;
try{
//STEP 2: Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
//STEP 3: Open a connection
System.out.println("Connecting to a selected
database...");
conn = DriverManager.getConnection(DB_URL, USER,
PASS);
System.out.println("Connected database
successfully...");
String selectSQL1 = "SELECT username FROM ofuser
WHERE username= '"+theuser+"'";
PreparedStatement preparedStatement =
conn.prepareStatement(selectSQL1);
ResultSet usernameRS =
preparedStatement.executeQuery(selectSQL1);
if (!usernameRS.next()){
//STEP 4: Execute a query
System.out.println("Inserting records into the
table...");
stmt = conn.createStatement();
String sql = "INSERT INTO ofuser VALUES
('"+theuser+"','"+thepass+"',null,'"+thename+"','"+themail+"',
'"+4234+"','"+23432+"','"+the
phone+"','"+null+"','"+null+"')";
stmt.executeUpdate(sql);
Type="T";
String REresponse ="('"+Type+"','"+OldRID+"')";
System.out.println(REresponse);
sOutput.writeObject(REresponse);
sOutput.flush();
// sOutput.writeObject(Type);
System.out.println("SUCCESS");
}else{
Type="F";
String REresponse ="('"+Type+"','"+OldRID+"')";
Type="F";
sOutput.writeObject(REresponse);
sOutput.flush();
System.out.println("FAIL");
}
return;
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}finally{
//finally block used to close resources
try{
if(stmt!=null)
conn.close();
}catch(SQLException se){
}// do nothing
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}//end finally try
}
}//end try
catch (IOException e) {
display("Exception creating new Input/output Streams: " +
e);
return;
}
// have to catch ClassNotFoundException
// but I read a String, I am sure it will work
catch (ClassNotFoundException e) {
}
Thread.sleep(5000);
Server.shutdown();
}
}
// what will run forever
// remove myself from the arrayList containing the list of the
// connected Clients
public void main(String[] args) throws InterruptedException {
// start server on port 1500 unless a PortNumber is specified
int portNumber = 1500;
switch(args.length) {
case 1:
try {
portNumber = Integer.parseInt(args[0]);
}
catch(Exception e) {
System.out.println("Invalid port number.");
System.out.println("Usage is: > java
Server [portNumber]");
return;
}
case 0:
break;
default:
System.out.println("Usage is: > java Server
[portNumber]");
return;
}
// create a server object and start it
Server server = new Server(portNumber);
server.start();
}
// try to close everything
private void close() {
// try to close the connection
try {
if(sOutput != null) sOutput.close();
}
catch(Exception e) {}
try {
if(sInput != null) sInput.close();
}
catch(Exception e) {};
try {
if(socket != null) socket.close();
}
catch (Exception e) {}
}
/*
* Write a String to the Client output stream
*/
private boolean writeMsg(String msg) {
// if Client is still connected send the message to it
if(!socket.isConnected()) {
close();
return false;
}
// write the message to the stream
try {
sOutput.writeObject(msg);
}
// if an error occurs, do not abort just inform the user
catch(IOException e) {
display("Error sending message to " );
display(e.toString());
}
return true;
}
}
//public void main(String[] args) {
// System.out.println("Goodbye!");
// }//end main
}
Upvotes: 0
Views: 167
Reputation: 171
be aware of high ram usage, it seems you're creating so much objects per request... my suggestion is that you should create a limited number of objects(for example 100) and then put them into an ArrayList and then call them respectively, I mean :
public static void startServer() {
try {
final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(12345);
final List<Operator> operators = new ArrayList<>();
final int nOperators = /* your choice */;
for (int i = 0; i < nOperators; i++) {
operators.add(i, new Operator());
}
final int parallelism = Runtime.getRuntime().availableProcessors();
ForkJoinPool pool = new ForkJoinPool(parallelism);
pool.submit(() -> new Thread(() -> {
int i = 0;
while (true) {
try {
operators.get(i).newOperation(serverSocketChannel.accept());
i++;
if (i == nOperators) {
i = 0;
}
} catch (IOException ignored) {
}
}
})).fork().invoke().start();
} catch (IOException ignored) {
}
}
and then you will see a huge increase in speed and performance...
EDIT:
for example:
public class Operator {
public void newOperation(final SocketChannel socketChannel) throws IOException {
DataInputStream dis = new DataInputStream(socketChannel.socket().getInputStream());
DataOutputStream dos = new DataOutputStream(socketChannel.socket().getOutputStream());
//TODO something here
dis.close();
dos.close();
socketChannel.close();
}
}
more information about ServerSocketChannel and SocketChannel and ForkJoinPool
good luck :)
EDIT by @PeterLawrey This is how I might write it assuming I has lots of short lived connections with CPU intensive operations. If you have operations which block on network/IO you might want more threads. Since you want Plain IO, it makes sense to stick with plain IO.
public static void startServer() throws IOException {
// don't ignore the server failing to bind.
final ServerSocket ss = new ServerSocket(12345);
final int parallelism = Runtime.getRuntime().availableProcessors();
// add one for the acceptor
final ExecutorService pool = ExecutorService.newFixedThreadPool(parallelism+1);
pool.submit(() -> {
try {
while (!ss.isClosed())
pool.submit(new Handler(serverSocketChannel.accept()));
} catch (IOException ignored) {
});
}
class Handler implements Runnable {
static final ThreadLocal<ExpensiveObject> expensiveObjectIWantToReuse = new ThreadLocal<>();
final Socket s;
final DataInputStream in;
final DataOutputStream out;
Handler(Socket s) {
this.s = s;
this.in = new DataInputStream(new BufferedInputStream(s.getInputStream()));
this.out = new DataOutputStream(new BufferedOutputStream(s.getOutputStream()));
}
public void run() {
try {
processStream(in, out);
} catch (Throwable t) {
// log t
} finally (
close(out); // also flush()es
close(in);
close(s);
}
}
public static void close(Closable c) {
try {
if (c != null) c.close();
} catch (IOException ignored) { }
}
protected void processStream(DataInput di, DataOutput out) {
// do something
}
}
Upvotes: 0
Reputation: 533462
2000 per hour is ~ 0.55 per second. You should be able to handle that rate on a mobile phone running as your server (assuming the requests are not complex)
if you need to handle up to 100K connections and over one billion requests per hour, TCP is likely to be fine. If you want much more than this you could use UDP, or buy a second server. More servers is likely to be much simpler ;)
Note: your database and/or your network bandwidth is likely to be max-ed out long before your TCP server will be.
Upvotes: 1