package redessocket;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Lopez, Mariano
*/
public class SocketServer {
ServerSocket server = null;
//array de clientes
ArrayList<SocketInfo> clientes;
public SocketServer(int port) throws IOException{
this.server = new ServerSocket(port);
this. clientes = new ArrayList<>();
}
public void listen() throws IOException{
//escuchar comandos por teclado
escucharComandosServer();
//si se logro instanciar.. mientras que no este cerrado el socket
while(server != null && !server.isClosed()){
Socket clientSocket = null;
try {
//escucha de peticiones
clientSocket = server.accept();
if(clientSocket != null){
lanzarHilo(clientSocket);
}
}catch (IOException e){System.out.println(e.toString());}
}
}
public static void main(String[] args) {
System.out.println("Socket Server - Redes de datos 2015 - Lopez, Mariano");
BufferedReader entrada = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Ingrese el puerto para inicializar el servidor: ");
try {
int port = Integer.parseInt(entrada.readLine());
SocketServer server = new SocketServer(port);
System.out.println("Inicialización correcta! puerto: "+port+"\nLISTENING");
server.listen();
} catch (IOException ex) {
System.out.println(ex.toString());
}catch(NumberFormatException fe){
System.out.println(fe.toString());
}
}
//comandos que se pueden ejecutar solo en el servidor
private void escucharComandosServer() throws IOException{
BufferedReader entradaDesdeUsuario = new BufferedReader(new InputStreamReader(System.in));
Thread t = new Thread(new Runnable() {
@Override
public void run() {
boolean flag_fin = true;
while(flag_fin){
try {
if(entradaDesdeUsuario.ready()){
String in =entradaDesdeUsuario.readLine();
switch(in){
case "exit":
System.out.println("Shooting down...");
KillEmAll();
server.close();
flag_fin=false;
break;
case "who":
System.out.println(clientesInfo());
break;
default:
System.out.println("Comandos:\nwho\t\tInformación sobre clientes conectados\nexit\t\tFinalizar todas las conexiones y cerrar el socket\n");
break;
}
}
} catch (IOException ex) {
Logger.getLogger(SocketServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
t.start();
}
//hilo para atender a cada socket cliente
private void lanzarHilo(Socket clientSocket){
SocketInfo socket_info = new SocketInfo(clientSocket);
clientes.add(socket_info);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
//string para almacenar mensajes del cliente
String line;
//ip del cliente
String ip = clientSocket.getInetAddress().toString();
//puerto del cliente
int puerto = clientSocket.getPort();
System.out.println("Nuevo cliente: "+ip+":"+puerto);
//output stream para escritura con el cliente
DataOutputStream salidaACliente = new DataOutputStream(clientSocket.getOutputStream());
//stream para lectura del cliente
DataInputStream in = new DataInputStream(clientSocket.getInputStream());
//mientras este conectado
while (!clientSocket.isClosed()) {
//leer desde el stream del cliente
line = readStringUTF(in);
System.out.println("\nCliente: "+ip+":"+puerto+"\n"+line);
//en la linea 0 esta el comando a ejecutar
line = (line.split("\n"))[0];
//si el mensaje es exit -> cerrar conexión
if(line.equals("exit")){
System.out.println("El cliente: "+ip+":"+puerto +" ha solicitado cierre");
clientes.remove(socket_info);
clientSocket.close();
break;
}else{
//todas las acciones que puede realizar el servidor
acciones(line, salidaACliente,socket_info);
}
}//fin while
//cerrar stream
in.close();
} catch (IOException ex) {
System.out.println(ex.toString());
}
}//end run
});
t.start();
}
//comandos que pueden realizar los clientes
private void acciones(String in,DataOutputStream salidaACliente,SocketInfo socket_info) throws IOException{
//historial de solicitudes
socket_info.addToHistory(in);
switch(in){
case "cores":
respuesta(salidaACliente, 200, "Procesadores disponibles para JVM (cores): " +Runtime.getRuntime().availableProcessors());
break;
case "ram -free":
respuesta(salidaACliente, 200, "Memoria libre para JVM: " + Runtime.getRuntime().freeMemory()+"Bytes");
break;
case "ram -total":
respuesta(salidaACliente, 200, "JVM total de memoria: " +Runtime.getRuntime().totalMemory()+"Bytes");
break;
case "ram -max":
respuesta(salidaACliente, 200, "Maxima cantidad de RAM que puede llegar a usar la JVM: " +Runtime.getRuntime().maxMemory()+"Bytes");
break;
case "so -name":
respuesta(salidaACliente, 200, "Sistema Operativo: "+System.getProperty("os.name"));
break;
case "history":
respuesta(salidaACliente, 200, socket_info.getHistory());
break;
case "help":
String aux = "Lista de comandos\n"
+ "cores\t\tProcesadores disponibles para JVM\n"
+ "ram -free\t\tMemoria libre para JVM\n"
+ "ram -total\t\tJVM total de memoria\n"
+ "ram -max\t\tMaxima cantidad de RAM que puede llegar a usar la JVM\n"
+ "so -name\t\tSistema Operativo\n"
+ "history\t\tHistorial de comandos\n"
+ "exit\t\tterminar conexión\n";
respuesta(salidaACliente, 200, aux);
break;
default:
respuesta(salidaACliente, 400, "Comando no reconocido");
break;
}
}
//finaliza todas las conexiones existentes ... tambien es el primer disco de Metallica.
private void KillEmAll() throws IOException{
for(SocketInfo s:clientes){
s.getSocket().close();
}
}
//String con información de los clientes online
private String clientesInfo(){
String retorno = "Cantidad de clientes online: "+clientes.size()+"\n";
retorno+="Socket información(IP, puerto remoto y puerto local) \t Fecha de la conexión\n";
for(SocketInfo s:clientes){retorno+=s.showInfo();}
return retorno;
}
//escribir cabecera + respuesta en el DataOutputStram para el cliente
private void respuesta(DataOutputStream salidaACliente,int code, String mensaje) throws IOException{
String estado = "";
switch(code){
case 200:
estado="OK";
break;
case 400:
estado="Bad request";
break;
case 500:
estado="Internal Error";
break;
default:
estado="OK";
break;
}
Date fecha = new Date();
String aux = code+" "+estado+"\nDate: "+fecha+"\nContent-Type: text charset=UTF-8\nContent-Length: "+mensaje.length()+"\n\n"+mensaje+"\n";
byte[] data=aux.getBytes("UTF-8");
salidaACliente.writeInt(data.length);
salidaACliente.write(data);
}
private static String readStringUTF(DataInputStream in) throws IOException{
int length=in.readInt();
byte[] data=new byte[length];
in.readFully(data);
return new String(data,"UTF-8");
}
//clase auxiliar para guardar atributos
private class SocketInfo{
private Socket socket;
private Date fecha;
private String history;
private DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
public SocketInfo(Socket s){
this.socket = s;
this.fecha = new Date();
this.history="Historial de comandos:\n";
}
public void addToHistory(String s){
this.history+=s+"\t"+dateFormat.format(new Date())+"\n";
}
public String getHistory(){
return this.history+"\n******************************************************************";
}
public Socket getSocket(){
return this.socket;
}
public Date getFechaConexion(){
return this.fecha;
}
public String showInfo(){
return this.getSocket().toString()+"\t"+dateFormat.format(this.getFechaConexion())+"\n"+this.getHistory()+"\n\n";
}
}
}