Comunicación por sockets con Java ME
Vamos a intentar la conexión móvil-pc a través de sockets. La parte del servidor será en el móvil y, por tanto, será desarrollada con Java ME. El cliente será el pc con Java SE "swing" aunque quiero utilizar Java FX , ya que parece que permite interfaces más modernas (así que tarea futura).
Código de parte del servidor Java ME
A continuación el código del midlet:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package view;
import controller.socketController;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
/**
* @author fran
*/
public class serverSocketMidlet extends MIDlet implements CommandListener {
private Form form = null;
private TextField infoTxt = null;
private Command exit = null, init = null,
stop = null;
private socketController sc = null;
public serverSocketMidlet() {
infoTxt = new TextField("info", "Esperando conexión", 50, 0);
form = new Form("Server socket");
form.append(infoTxt);
exit = new Command("Exit", Command.EXIT, 0);
stop = new Command("Stop", Command.OK, 1);
init = new Command("Start", Command.OK, 2);
Command commands[] = {exit, stop, init};
addCommands(commands);
form.setCommandListener(this);
}
public void startApp() {
setDisplayable(form);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public void setDisplayable(Displayable d) {
Display.getDisplay(this).setCurrent(d);
}
public void commandAction(Command c, Displayable d) {
if (c == exit) {
destroyApp(true);
notifyDestroyed();
} else if (c == init) {
if (sc == null) {
sc = new socketController(infoTxt);
sc.initSocket();
}
//sc.initSocket();
} else if (c == stop) {
sc.stopSocket();
}
}
private void addCommands(Command[] commands) {
for (int i = 0; i < commands.length; i++) {
form.addCommand(commands[i]);
}
}
}
Como sabéis en el Midlet se encuentran los métodos startApp, pauseApp y destroyApp. Es obvio cuando se ejecuta cada método.
Ahora vemos las clases que se encargan de las operaciones del programa (la parte de control).
public class socketController {
private ServerSocketConnection ssc = null;
private SocketConnection sc = null;
private InputStream is = null;
private TextField infoTxt = null;
private socketThread scThread = null;
public socketController(TextField infoTxt) {
this.infoTxt = infoTxt;
scThread = new socketThread(ssc, sc, is, infoTxt);
}
public void initSocket() {
scThread.start();
}
public void stopSocket() {
try {
//close the sockets elements:
if (scThread.getOut() != null) {
scThread.getOut().close();
}
if (scThread.getOutWriter() != null) {
scThread.getOutWriter().close();
}
if (scThread.getIs() != null) {
scThread.getIs().close();
scThread.setIs(null);
}
if (scThread.getSc() != null) {
scThread.getSc().close();
scThread.setSc(null);
}
if (scThread.getSsc() != null) {
scThread.getSsc().close();
scThread.setSsc(null);
}
//put in them null values
scThread.setOut(null);
scThread.setOutWriter(null);
infoTxt.setString("Socket detenido con éxito.");
} catch (IOException ex) {
ex.printStackTrace();
}
}
Esta clase se encarga de general la clase que se encarga de arrancar el socket. También crea los objetos serverSocketConnection, SocketConnection e InputStream. Son objetos que hemos de manipular necesariamente en el arranque como en el cierre del socket. Cómo hemos hecho que el Thread de arranque sea diferente del de cierre del socket he definido las variables aquí y las pasamos como argumento a la siguiente clase que se encarga de arrancar el cocket:
public socketThread(ServerSocketConnection ssc, SocketConnection sc,
InputStream is, TextField infoTxt) {
this.ssc = ssc;
this.sc = sc;
this.is = is;
this.infoTxt = infoTxt;
bff = new StringBuffer();
}
public InputStream getIs() {
return is;
}
public SocketConnection getSc() {
return sc;
}
public ServerSocketConnection getSsc() {
return ssc;
}
public OutputStream getOut() {
return out;
}
public OutputStreamWriter getOutWriter() {
return outWriter;
}
public void setIs(InputStream is) {
this.is = is;
}
public void setSc(SocketConnection sc) {
this.sc = sc;
}
public void setSsc(ServerSocketConnection ssc) {
this.ssc = ssc;
}
public void setOut(OutputStream out) {
this.out = out;
}
public void setOutWriter(OutputStreamWriter outWriter) {
this.outWriter = outWriter;
}
public void run() {
try {
ssc = (ServerSocketConnection) Connector.open(cnn);
infoTxt.setString("Listening to connection.");
sc = (SocketConnection) ssc.acceptAndOpen();
out = sc.openOutputStream();
outWriter = new OutputStreamWriter(out);
is = sc.openInputStream();
int ch = 0;
String init = "comenzando conexión";
// byte[] b = init.getBytes();
// out.write(b);
// out.flush();
int count = 0;
while ((ch = is.read()) != -1) {
count = count++;
bff.append((char) ch);
if (is.available() == 0) {
String exitMsg = "";
exitMsg = bff.toString();
infoTxt.setString(bff.toString());
outWriter.write(exitMsg);
bff.delete(0, bff.length());
outWriter.flush();
}
}
} catch (IOException ex) {
String message = ex.getMessage();
}
}
}
Esta clase es la que se encarga de recibir información y rebotarla al cliente de nuevo. Es una operación sencilla. Simplemente esta apicacción pretende verificar la conexión pc-móvil con la configuración cliente- servidor a través de sockets. Como sabemos, esta clase es un Thread nuevo. Así es independiente del Thread que se encarga de cerrarla o de parar la conexión. Podéis encontrar información sobre las clases utilizadas fácilmente a través de internet.
Código de parte del cliente con Java swing:
La parte del cliente la hemos implementado en el pc. A continuación os mostraré las diferentes clases que forman parte del programa. La clase que viene a continuación se encarga de la vista de la ventana principal menos de un JPanel informativo en el que aparecerán los mensajes enviados y recibidos.public class mainWindow extends JFrame {
private JButton initButton = null, stopButton = null,
sendButton = null;
private socketController sController = null;
private JPanel container = null;
private JLabel infoReceivedLbl = null, ipLbl = null,
msgLbl = null, infoLbl = null;
private JTextField ipTxt, msgTxt = null;
private BorderLayout borderLayout = null;
private infoPnl infoPnl = null;
public mainWindow() throws HeadlessException {
this.setTitle("client socket");
this.setSize(800, 600);
this.container = new JPanel();
this.setContentPane(container);
container.setVisible(true);
borderLayout = new BorderLayout();
container.setLayout(borderLayout);
initComponents();
this.setVisible(true);
}
private void initComponents() {
initButton = new JButton("Inicio");
stopButton = new JButton("Stop");
sendButton = new JButton("Enviar");
ipTxt = new JTextField();
msgTxt = new JTextField();
infoReceivedLbl = new JLabel();
ipLbl = new JLabel("Introduzca la ip del móvil:");
msgLbl = new JLabel("Introduzca el mensaje a enviar:");
infoLbl = new JLabel("Mensaje recibido:");
infoPnl = new infoPnl(ipTxt, msgTxt, infoReceivedLbl, ipLbl, msgLbl, infoLbl);
sController = new socketController(infoReceivedLbl, ipTxt, msgTxt);
setLayout();
Dimension d = ipTxt.getSize();
d.width = 150;
ipTxt.setMaximumSize(d);
initButton.addActionListener(sController);
sendButton.addActionListener(sController);
stopButton.addActionListener(sController);
sController.setFrame(this);
}
public void setLayout() {
FlowLayout flowLayout = new FlowLayout();
flowLayout.setAlignment(FlowLayout.CENTER);
JPanel bttnsPnl = new JPanel(flowLayout);
bttnsPnl.add(initButton);
bttnsPnl.add(sendButton);
bttnsPnl.add(stopButton);
container.add(bttnsPnl, BorderLayout.PAGE_START);
container.add(infoPnl, BorderLayout.CENTER);
}
}
Para situar componentes utilizamos diferentes Layouts. Para situar los botones utilizamos FlowLayout que simplemente sitúa los componentes uno detrás de otro en fila. Para situar el JPanel informativo usamos la constante BorderLayout.CENTER que sitúa el panel en el centro. Al no tener paneles situados en laterales se expande y ocupa el espacio restante. Sobre los layouts hay gran información en http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html.
La clase encargada de definir el diseño del panel informativo está a continuación:
public class infoPnl extends JPanel {
private JLabel infoReceivedLbl = null, ipLbl = null,
msgLbl = null, infoLbl = null;
private JTextField ipTxt, msgTxt = null;
public infoPnl(JTextField ipTxt, JTextField msgTxt, JLabel infoReceivedLbl,
JLabel ipLbl, JLabel msgLbl, JLabel infoLbl) {
this.ipTxt = ipTxt;
this.msgTxt = msgTxt;
this.infoReceivedLbl = infoReceivedLbl;
this.ipLbl = ipLbl;
this.msgLbl = msgLbl;
this.infoLbl = infoLbl;
settingPanel();
Dimension d= ipTxt.getSize();
d.setSize(d.getWidth()/4, d.getHeight());
}
private void settingPanel() {
GroupLayout gL = new GroupLayout(this);
this.setLayout(gL);
gL.setAutoCreateGaps(true);
gL.setAutoCreateContainerGaps(true);
//configuring groupLayout:
setHorizontalLayout(gL);
setVerticalLayout(gL);
}
public void setHorizontalLayout(GroupLayout gL) {
Group c1 = gL.createParallelGroup(GroupLayout.Alignment.LEADING);
c1.addComponent(ipLbl);
c1.addComponent(msgLbl);
c1.addComponent(infoLbl);
Group c2 = gL.createParallelGroup(GroupLayout.Alignment.LEADING);
c2.addComponent(ipTxt);
c2.addComponent(msgTxt);
c2.addComponent(infoReceivedLbl);
SequentialGroup c = gL.createSequentialGroup();
c.addGroup(c1);
c.addGroup(c2);
gL.setHorizontalGroup(c);
}
public void setVerticalLayout(GroupLayout gL) {
Group r1 = gL.createParallelGroup(GroupLayout.Alignment.BASELINE);
r1.addComponent(ipLbl);
r1.addComponent(ipTxt);
Group r2 = gL.createParallelGroup(GroupLayout.Alignment.BASELINE);
r2.addComponent(msgLbl);
r2.addComponent(msgTxt);
Group r3 = gL.createParallelGroup(GroupLayout.Alignment.BASELINE);
r3.addComponent(infoLbl);
r3.addComponent(infoReceivedLbl);
SequentialGroup rows = gL.createSequentialGroup();
rows.addGroup(r1);
rows.addGroup(r2);
rows.addGroup(r3);
gL.setVerticalGroup(rows);
}
}
Esta clase usa el GroupLayout que es otro tipo de Layout, más flexible y que permite más combinaciones que el sencillo FlowLayout.
Finalmente la clase que se encarga del control del socket es:
public class socketController implements ActionListener {
private Socket s = null;
private PrintWriter out = null;
private BufferedReader in = null;
private String localIp = "localhost";
private JLabel infoReceivedLbl = null;
private JTextField ipTxt = null, msgTxt = null;
private JFrame frame = null;
//private OutputStream out = null;
//private InputStream in = null;
public socketController(JLabel infoReceivedLbl, JTextField ipTxt, JTextField msgTxt) {
this.infoReceivedLbl = infoReceivedLbl;
this.ipTxt = ipTxt;
this.msgTxt = msgTxt;
}
private void initCommunication() throws UnknownHostException, IOException {
if (!ipTxt.getText().equals("")) {
s = new Socket(ipTxt.getText(), 1234);
out = new PrintWriter(s.getOutputStream(), true);
//out = s.getOutputStream();
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
this.ipTxt.setEnabled(false);
} else {
JOptionPane.showMessageDialog(frame, "Introduzca la ip del móvil a conectarse",
"Ip vacia", JOptionPane.INFORMATION_MESSAGE);
}
/*
* try (BufferedReader stdIn = new BufferedReader( new
* InputStreamReader(System.in))) { String userInput;
*/
}
public void setFrame(JFrame frame) {
this.frame = frame;
}
@Override
public void actionPerformed(ActionEvent e) {
JButton pusshedBttn = (JButton) e.getSource();
switch (pusshedBttn.getText()) {
case "Inicio":
if (s == null) {
try {
initCommunication();
} catch (UnknownHostException ex) {
Logger.getLogger(socketController.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(socketController.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
JOptionPane.showMessageDialog(frame, "No pulse más.Conexión con socket iniciada",
"Conexión iniciada", JOptionPane.INFORMATION_MESSAGE);
}
break;
case "Enviar":
if (!"".equals(msgTxt.getText())) {
try {
String salida = msgTxt.getText();
byte[] b = salida.getBytes();
//out.write(b);
//out.flush();
out.println(salida);
int ch = 0;
String readLine = in.readLine();
infoReceivedLbl.setText(readLine);
} catch (IOException ex) {
Logger.getLogger(socketController.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
//custom title, error icon
JOptionPane.showMessageDialog(frame,
"Mensaje vacio. Introduzca un mensaje para enviar.",
"Eror de mensaje vacio",
JOptionPane.ERROR_MESSAGE);
}
break;
case "Stop":
try {
out.close();
in.close();
s.close();
} catch (IOException ex) {
Logger.getLogger(socketController.class.getName()).log(Level.SEVERE, null, ex);
}
break;
}
}
}
Como se puede ver es una clase que implementa la interfaz ActionListener y se encarga de gestionar los métodos del programa (únicamente la pulsación de los tres botones existentes).
Próximamente, publicaré el video de cómo funciona el programa en un móvil y en el pc.
No hay comentarios:
Publicar un comentario