Salve galera! Depois de muito tempo, vou postar um trabalho que fiz na faculdade em linguagem de programação I, que é um conversor de medidas, feito em C e Java.
Para isso, fiz um server em C, uma linguagem de programação estruturada se comunicar com um client feito em Java, uma linguagem Orientada a Objeto.
O propósito deste projeto foi mostrar como é possível fazer duas linguagens de programação se comunicarem.
Toda comunicação foi feita através de sockets, tanto do lado servidor como do lado cliente. Os sockets são os programas responsáveis pela comunicação ou interligação de outros programas pelo protocolo declarado, que no caso foi usado o TCP / IP.
O que é TCP/IP?
TCP / IP (TRANSMISSION CONTROL PROTOCOL / INTERNET PROTOCOL) é um padrão de comunicação que reúne um conjunto de protocolos tais como TCP, IP, FTP (FILE TRANSFER PROTOCOL), TELNET, entre outros.
As informações que trafegam na rede necessitam do TCP/IP, por isso ele é utilizado como protocolo primário da rede na internet. Este protocolo foi dividido em “camadas” bem definidas, cada uma realizando sua parte na tarefa de comunicação (aplicação, transporte, rede, e enlace/físico). Este modelo tem a seguinte vantagem: por ter os processos de comunicação bem definidos e divididos em cada camada, qualquer alteração poderá ser feita isoladamente, não precisando reescrever todo o protocolo. O TCP/IP tem como principal característica a transmissão de dados em máquinas que diferem em suas arquiteturas .
O que é sockets?
Especificamente em computação, um soquete pode ser usado em ligações de redes de computadores para um fim de um elo bidirecional de comunicação entre dois programas. A interface padronizada de soquetes surgiu originalmente no sistema operacional Unix BSD (Berkeley Software Distribution); portanto, eles são muitas vezes chamados de Berkeley Sockets. É também uma abstração computacional que mapeia diretamente a uma porta de transporte (TCP ou UDP) e mais um endereço de rede. Com esse conceito é possível identificar unicamente um aplicativo ou servidor na rede de comunicação IP.
Tipo de socket usado:
SOCK_STREAM: Fornece sequencial, seguro, e em ambos os sentidos, conexões baseadas em "byte streams". Dados "out-of-band" do mecanismo de transmissão devem ser suportados. O protocolo TCP é baseado neste tipo de socket.
Bom, mas chega de teoria e vamos ao código.
Abaixo vou postar o código feito em C:
server.C
#include <stdio.h>
#include <conio.h>
#include <winsock.h>
#include <malloc.h>
#include <string.h>
#include <windows.h> //Repara que utilzaremos este header para utilizarmos a função Sleep();
WSADATA data;
SOCKET winsock;
SOCKADDR_IN sock;
//Declaração global de variáveis
char comando[1024];
char *escolha;
char *valor;
//Função para converter para litro
float to_litro(char *valor)
{
float ml = 0;
ml = atof(valor);
float litro = ml / 1000;
return litro;
}
//Função para converter para mililitro
float to_ml(char *valor)
{
float l = 0;
l = atof(valor);
float mili = l * 1000;
return mili;
}
//Função para converter para grama
float to_grama(char *valor)
{
float k = 0;
k = atof(valor);
float kg = k * 1000;
return kg;
}
//Função para converter para quilo
float to_kg(char *valor)
{
float k = 0;
k = atof(valor);
float kg = k / 1000;
return kg;
}
//Função para converter para farenheit
float to_farenheit(char *valor)
{
float c = 0;
c = atof(valor);
float farenheit = (c *9)/5 + 32;
return farenheit;
}
//Função para converter para celsius
float to_celsius(char *valor)
{
float f = 0;
f = atof(valor);
float celsius = (f - 32)/1.8;
return celsius;
}
int main(){
//Verifica se deu erro ao inicializar o winsock
if(WSAStartup(MAKEWORD(1,1),&data)==SOCKET_ERROR)
{
printf("Erro ao inicializar o winsock");
return 0;
}
//Verifica se deu erro ao criar o socket
if((winsock = socket(AF_INET,SOCK_STREAM,0))==SOCKET_ERROR)
{
printf("Erro ao criar socket\n");
return 0;
}
//Atribui uma família ao sock do tipo AF_INET
sock.sin_family=AF_INET;
//Atribui uma porta ao sock
sock.sin_port=htons(4321);
//Verifica se foi possível fazer um bind do endereço IP ao sock
if(bind(winsock,(SOCKADDR*)&sock,sizeof(sock))==SOCKET_ERROR)
{
printf("Erro ao tentar utilizar a funcao BIND\n");
return 0;
}
//Essa função fica ouvindo até obter um conexão
listen(winsock,1);
//Esse laço verifica se a função accept não obteve erro
while((winsock = accept(winsock,0,0))==SOCKET_ERROR)
{
Sleep(1);
}
printf("Cliente conectado!\n");
//Nesse laço, após obter a conexão com o cliente, aguarda os comandos para começar a conversão
while(1)
{
//Declaração das variáveis
int bytes;
int choice;
float celsius, farenheit, kg, grama, ml, litro;
char result[1024];
//Aguarda um segundo
Sleep(1);
//Recebe do cliente a string e aloca o resultado no vetor comando
memset(comando,0,1024);
//O método recv recebe um valor inteiro e com isso, consigo verificar
//caso o método receba o valor -1, indicando que perdeu a conexão
bytes=recv(winsock,comando,1024,0);
if(bytes==-1)
{
printf("\nConexao perdida\n");
return 0;
}
//Atribui a variável valor o que estiver antes do TOKEN ">"
valor = strtok(comando,">");
//Atribui a variável valor o que estiver depois do TOKEN ">"
escolha = strtok(NULL,">");
//converto escolha que é uma string em int e atribui a choice
choice = atoi(escolha);
switch(choice)
{
case 1:
printf("Tipo Celsius:\n");
printf("Valor obtido: %s\n", valor);
celsius = to_celsius(valor);
sprintf(result, "%.3f", celsius);
strcat(result," C\r\n");
printf("Valor convertido para Celsius: %s\n",result);
send(winsock,result,strlen(result),0);
break;
case 2:
printf("Tipo Farenheit:\n");
printf("Valor obtido: %s\n", valor);
farenheit = to_farenheit(valor);
sprintf(result, "%.3f", farenheit);
strcat(result," F\r\n");
printf("Valor convertido para Farenheit: %s\n",result);
send(winsock,result,strlen(result),0);
break;
case 3:
printf("Tipo grama:\n");
printf("Valor obtido: %s\n", valor);
grama = to_grama(valor);
sprintf(result, "%.f", grama);
strcat(result," g\r\n");
printf("Valor convertido para grama: %s\n",result);
send(winsock,result,strlen(result),0);
break;
case 4:
printf("Tipo Kg:\n");
printf("Valor obtido: %s\n", valor);
kg = to_kg(valor);
sprintf(result, "%.3f", kg);
strcat(result," Kg\r\n");
printf("Valor convertido para Kg: %s\n",result);
send(winsock,result,strlen(result),0);
break;
case 5:
printf("Tipo Litro:\n");
printf("Valor obtido: %s\n", valor);
litro = to_litro(valor);
sprintf(result, "%.3f", litro);
strcat(result," L\r\n");
printf("Valor convertido para litro: %s\n",result);
send(winsock,result,strlen(result),0);
break;
case 6:
printf("Tipo ml:\n");
printf("Valor obtido: %s\n", valor);
ml = to_ml(valor);
sprintf(result, "%.f", ml);
strcat(result," ml\r\n");
printf("Valor convertido para L: %s\n",result);
send(winsock,result,strlen(result),0);
break;
}
}
getch();
//Fecha a conexão
closesocket(winsock);
WSACleanup();
return 0;
}
Como o código está todo comentado, não irei ficar explicando o que cada método ou variável faz.
Agora, segue as classes Java:
Fiz duas classes, uma chamada Reader.java, que estende a classe Thread e a outra que a classe ClientConverter.java.
Reader.java
import java.awt.TextField;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Reader extends Thread {
//Declaração de variáveis
protected ClientConverter cliente;
private TextField output;
public Reader(ClientConverter c, TextField output)
{
super("reader");
this.cliente=c;
this.output=output;
}
public void run()
{
String line;
try
{
//Recebe o valor enviado pelo server através do socket
BufferedReader in = new BufferedReader(new InputStreamReader(cliente.clisoc.getInputStream()));
while(true)
{
//Atribui a variável line o conteúdo recebido
line=in.readLine();
//Adiciona o valor convertido no OutputArea
output.setText(line);
}
}
catch(IOException e)
{
System.out.println("Reader:"+e);
}
}
}
Segue a classe client:
ClientConverter.java
import java.applet.Applet;
import java.awt.Choice;
import java.awt.Event;
import java.awt.Label;
import java.awt.TextField;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import javax.swing.JOptionPane;
public class ClientConverter extends Applet
{
//Declaração de variáveis
private static final long serialVersionUID = 1500488503654718075L;
public static final int DEFAULT_PORT=4321;
public Socket clisoc;
private Thread reader;
public TextField outputArea;
public TextField inputArea;
public TextField inputText;
public PrintStream out;
public Choice combobox;
public String address = "127.0.0.1";
public String inLine;
//Descricao para a caixa comboBox
private String[] description = {
"Escolha o tipo de conversão:",
"Farenheit >>>> Celsius",
"Celsius >>>> Farenheit",
"Kg >>>> gramas",
"gramas >>>> Kg",
"mililitro >>>> Litro",
"Litro >>>> mililitro"};
//Cria as linhas de leitura e escrita e as inicia.
public void init()
{
outputArea = new TextField(35);
inputArea = new TextField(35);
inputText = new TextField(35);
combobox=new Choice();
//Inseri os valores para o combobox
for(int i=0;i<description.length;i++){
combobox.addItem(description[i]);
}
//Tela da Applet
add(new Label("Converter usando conexão (Socket TCP)\n"));
add(new Label("Selecione o tipo de conversão:"));
add(combobox);
add(new Label("Digite um valor e pressione Enter para converter:\n"));
add(inputArea);
add(new Label("Valor digitado:"));
add(inputText);
add(new Label("Resultado:"));
add(outputArea);
resize(300,350);
try
{
//Cria um socket cliente passando o endereco e a porta do servidor
clisoc=new Socket(address,DEFAULT_PORT);
reader=new Reader(this, outputArea);
out=new PrintStream(clisoc.getOutputStream());
//Define prioridades desiguais para que o console seja compartilhado
//de forma efetiva.
reader.setPriority(3);
reader.start();
}
catch(IOException e)
{
System.err.println(e);
System.exit(0);
}
}
public boolean handleEvent(Event evt)
{
if (evt.target==inputArea)
{
char c=(char)evt.key;
//Verifica de o usuário pressionou a tecla ENTER sem ao menos ter selecionado um tipo
if(c=='\n' && combobox.getSelectedIndex()==0){
combobox.getFocusListeners();
inputArea.setText("");
JOptionPane.showMessageDialog(null, "Selecione um tipo para converter!");
}
//Vigia se o usuario pressiona a tecla ENTER.
//Isso permite saber a mensagem esta pronta para ser enviada!
if (c=='\n')
{
inLine=inputArea.getText();
out.println(inLine+">"+combobox.getSelectedIndex());
inputArea.setText("");
//Envia a mensagem, passando o valor e o tipo de conversao como parametro.
return true;
}
inputText.setText(inLine);
}
return false;
}
}
Para que dê certo, você irá precisar compactar o projeto, criando um jar e colocá-lo na mesma pasta que o html criado abaixo.
Segue a classe clientConverter.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en-US">
<head>
<title>Converter Java Applet</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
</head>
<body>
<h1>Converter Java Applet</h1>
<applet code="ClientConverter.class" archive="ClientConvert.jar" width="300" height="350" />
</body>
</html>
Por ser um applet, você irá precisar mudar a segurança do certificado ou criar uma assinatura (que irei abordar em outro tópico).
Caso queira apenas mudar a segurança, vá em painel de controle --> Java --> Na aba de segurança, alterar para médio e aplicar.
Para inicializar a aplicação, inicialize o server.c primeiro.
Ao clicar no clientConverter.html, irá aparecer um alerta de segurança, avisando que uma aplicação não assinada está tentando ser executada. Clique em executar e seu converter irá ser inicializado.
Pronto, agora é só selecionar o tipo de conversão e pressionar ENTER para que o server converta e envie ao client o resultado.
E é isso, espero que tenha ajudado e até a próxima!!!
Um grande abraço;
Daniel Hideki.
Referências
http://www-usr.inf.ufsm.br/~giovani/sockets/sockets.txt,
http://reality.sgi.com/employees/jam_sb/mocap/MoCapWP_v2.0.html,
http://www2.unoeste.br/~chico/comunicacao_socket/,
http://www.forum-invaders.com.br/vb/showthread.php/8249-Tutorial-de-Sockets-para-iniciantes-C,
http://devio.us/~cooler/artigo_socket_em_C/index.html