sábado, 30 de julho de 2011

Compilando arquivos com a ajuda do Makefile

Olá pessoal, hoje trataremos do utilitário Makefile. Ele nos ajudará a compilar nosso programa de hoje. Nosso programa é bem simples, na verdade o objetivo principal é a utilização do Makefile quando precisamos compilar projetos com mais de um arquivo.

Os softwares que utilizamos nesse artigo:

gcc – nosso compilador, versão 4.4.5
emacs – nosso editor, você pode utilizar qualquer editor para criar seus arquivos de código.
Sistema Operacional – Debian

Nosso pequeno projeto consistirá de três arquivos:

main.c – conterá nossa aplicação principal
matematica.c – esse conterá funções matemáticas, um exemplo didático
matematica.h – nosso arquivo cabeçário

Vamos começar pelo arquivo “matemática.c”

#include "matematica.h"
//calcula fatorial de um numero
int fatorial (int n)
{
if(n==1)
return n;
else
return n * fatorial(n-1);
}
//retorna o modulo de um inteiro
int modulo(int n)
{
if(n<0)
return n * (-1);
else
return n;
}
//verifica se um número é par
//se a funcao retornar 1 - é par, 0 - é ímpar
int e_par(int n)
{
int resto;
resto = n % 2;
if (!resto)
return 1; // é par
else
return 0; //não é par
}

Agora, nós vamos criar um arquivo com a extensão .h, “matematica.h” para apenas conter as chamadas das funções.
Veja:
extern int fatorial (int n);
extern int modulo (int n);
extern int e_par (int n);

Bem simples, não concorda?

Agora vamos ao nosso programa “main.c”

#include <stdio.h>
#include "matematica.h"

int menu()
{
int op;
printf("\n\t1 - Fatorial:");
printf("\n\t2 - Modulo:");
printf("\n\t3 - Verifica se e par:");
printf("\n\tOpcao:\t");
scanf("%d",&op);
return op;
}
int main ()
{
int op,num;
do
{
op = menu();
switch(op)
{
case 1:printf("\n\tInforme um numero: ");
scanf("%d",&num);
printf("\n\tFatorial do numero: %d\n",fatorial(num));
break;
case 2:printf("\n\tInforme um numero: ");
scanf("%d",&num);
printf("\n\tO modulo do numero e: %d\n",modulo(num));
break;
case 3:printf("\n\tInforme um numero: ");
scanf("%d",&num);
num = e_par(num);
if(num)
printf("\n\tE PAR!\n");
else
printf("\n\tE IMPAR!\n");
break;
}

}while(op<4);

}

Finalizamos nossa codificação. Agora, vamos ao nosso Makefile.

programa_final: main.o matematica.o
     gcc -o programa_final main.o matematica.o
main.o: main.c matematica.h
     gcc -c main.c
matematica.o: matematica.c matematica.h
     gcc -c matematica.c
Veja que, programa_final, é o arquivo final, ou seja, aquele que vamos executar com o comando: ./programa_final. A estrutura do arquivo é bem simples. A esquerda temos o arquivo que será gerado, chamado de “target”, e a direita temos suas dependências. Abaixo vem o comando para sua compilação. Para os outros aquivos a regra é a mesma.

Assim crie um arquivo chamado Makefile, e coloque esse código em seu interior. Após isso apenas digite em sua linha de comando: “make”

Os arquivos serão compilados e você terá a seguinte saída:

gcc -c main.c
gcc -c matematica.c
gcc -o programa_final main.o matematica.o

Assim será gerado o arquivo “programa_final”. Agora o execute com o comando: ./programa_final

Outro ponto que merece atenção é que só os arquivos que foram alterados serão recompilados. Imagine que você está trabalhando com um grande número de arquivos, se você alterar apenas 1 arquivo não terá porque recompilar todos. Vamos fazer um teste, vamos alterar o arquivo main, e vamos rodar o make novamente.

A saída do comando make deveria ter sido:
gcc -c main.c
gcc -o programa_final main.o matematica.o

Sem a compilação do arquivo matematica.c, pois o mesmo não foi alterado.

Espero ter ajudado com essa pequena introdução sobre o Makefile.





sexta-feira, 22 de julho de 2011

Threads em CSharp (C#)

Pessoal hoje vamos falar sobre threads na linguagem CSharp (C#). Threads possuem várias finalidades, mas a que aqui, hoje trabalharemos, será manter uma interface que continue interagindo com o usuário, mesmo que algum processamento pesado esteja acontecendo.

Vamos ao nosso exemplo prático: Vamos criar uma função que calcula números primos e vamos mostrar o resultado em uma caixa de texto. Nossa interface será como a que segue:


Caixa de Texto, propriedades que alteramos:
Name: textBoxResult
Multiline: true
Scrolls Bars: Both

Botões:
Calc:
Name: buttonCalc
Text: Calc


CalcThread:
Name: buttonThread
Text: CalcThread

OutraTarefa:
Name: buttonOutraTarefa
Text: OutraTarefa

Nosso código

Namespaces que vamos utilizar:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;


Todos esses são default do VisualStudio, exceto um: System.Threading, ele conterá tudo que precisaremos para trabalhar com a Thread de nosso exemplo.

Código do botão Calc:

private void buttonCalc_Click(object sender, EventArgs e)
{
int maxNum = 10000;
int contador = 2;
int resto=1;
while (contador <= maxNum)
{
for (int x = 2; x < contador; x++)
{
resto = contador % x;
if ( resto == 0)
{
break;
}
}
if (resto != 0)
textBoxResult.Text = textBoxResult.Text + " " + contador.ToString();
contador++;
}

}

Código do botão CalcThread:

prime = new Thread(calculaPrimos);
prime.Start();

Bom vamos ver nossas funções auxiliares, primeiramente vamos verificar a função calculaPrimos:

public void calculaPrimos()
{
int maxNum = 10000;
int contador = 2;
int resto=1;
while (contador <= maxNum)
{
for (int x = 2; x < contador; x++)
{
resto = contador % x;
if ( resto == 0)
{
break;
}
}

if (resto != 0)
textBoxResult.Invoke(new escreveTxtDelegate(escreveTxt), new object[] { contador});
contador++;
}

}


Para escrevermos em controles que não foram criados dentro de nossa thread, precisamos fazer o uso de delegates e usamos o método invoke do textBoxResul.
Bom o delegate pode estar declarado no início, como um membro da classe, assim como nossa thread:

public delegate void escreveTxtDelegate(int contador);
Thread prime;

Nossa função para escrever no TextBox, usada pelo Delegate:

public void escreveTxt(int contador)
{
textBoxResult.Text = textBoxResult.Text + " " + contador.ToString();
}

Bom agora para finalizarmos o código de nosso botão OutraTarefa:

private void buttonOutraTarefa_Click(object sender, EventArgs e)
{
MessageBox.Show("Executando outra tarefa", "NumPrimos");
}
Para testarmos nossa aplicação, vamos fazer da seguinte maneira:

Vamos executar o programa e pressionar o botão “Calc”, se você tentar pressionar o botão OutraTarefa ele não vai mostrar a mensagem “Executando outra Tarefa”. E também se você tentar mexer a tela do programa a mesma não vai responder. Agora vamos fazer outro teste, ao invés de clicarmos no botão “Calc”, vamos clicar no botão “CalcThread” assim você poderá perceber que a mensagem será mostrada e também poderá movimentar a tela do aplicativo enquanto o processamento matemático está ocorrendo.








segunda-feira, 11 de julho de 2011

Uma aplicação cliente servidor com autenticação - Debian

Plataforma: Debian (Linux debian 2.6.32-5-amd64)

Olá Pessoal,

hoje vamos trabalhar em uma aplicação cliente/servidor – client/server – onde nosso cliente irá enviar alguns comandos como (ls, mkdir, etc...) que serão executados no servidor e exibidos na tela do cliente. A topologia é bem simples, nosso servidor deverá ficar escutando – listening - em um determidada porta. Nosso servidor porvê autenticação, e a base de dados é a mesma do sistema. Assim, você irá se logar apenas se o usuário digitado, for um usuário cadastrado no sistema. Para verificar os usuários do sistema você pode verificar em: /etc/passwd.

Bom nosso servidor também loga em um arquivo quando o cliente se conecta e quando o mesmo é desconectado:

Para rodar nosso servidor executamos o seguinte comando: ./teste 4001

Onde 4001 é o número da porta que o servidor deve estar escutando. Você pode selecionar outra porta, sempre tendo o cuidado de não colocar números que estão sendo utilizados por outros serviços, como http – porta 80, hhtps: 443, etc...

Esse é o código do nosso servidor:
Cabeçários:

#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <crypt.h>
#include <shadow.h>


Funções Auxiliares, é sempre recomendado que você organize os seus códigos escritos na Linguagem C, em funções, assim o código ficará mais claro de ser entendido. Sempre que considerar necessários comentários são bem vindos:

Função responsável por gravar os dados dos cliente conectados. Veeja que a mesma recebe a estrutura de dados do cliente.

int gravaLog(struct sockaddr_in cliaddr, char * mensagem)
{
char buff[4096]; //armazenar informações do cliente
FILE * arqLog; //Arquivo de logs para IP e porta de conexão do cliente
int porta; //porta que o cliente usou para se conectar
time_t tem = time(NULL);
arqLog = fopen("logs","a+");
inet_ntop(AF_INET, &(cliaddr.sin_addr), buff, sizeof(buff));
porta = ntohs(cliaddr.sin_port);
fprintf(arqLog,"%s: IP %s, Porta %d as %s",mensagem,buff,porta, ctime(&tem));
fclose(arqLog);
return 1;
}

Função que faz a consulta no arquivo onde são armazenadas as senhas de nosso sistema.

int AutenPasswd(char * user, char * senha)
{
struct passwd * pw;
struct spwd * shad;
pw = getpwnam(user);
shad = getspnam(user);

if(pw == NULL)
return -1;
else
{
strcpy(pw->pw_passwd,shad->sp_pwdp);

if (strcmp(crypt(senha, pw->pw_passwd),pw->pw_passwd)==0)
return 1;
else
return 0;
}

}

Essa função recebe os dados fornecidos pelo programa cliente, ela faz uso da função AutenPasswd:

//Retorna 0 se a autenticacao falhar, 1 ser ocorrer ocm sucesso
int Autenticacao(int * connfd)
{
int err; //recebe valores de retorno das funções
char buff[256], user[256], password[256];

strcpy(buff, "1");
bzero(user,256);
bzero(password,256);
err = read(*connfd,user,sizeof(user));

err = read(*connfd,password,sizeof(password));

err = AutenPasswd(user,password);

if(err != 1) //Autenticacao falhou
{
strcpy(buff, "0");
write(*connfd,buff,sizeof(buff));
close(*connfd);
return 0;
}
else
write(*connfd,buff,sizeof(buff));

return 1;

}

Funções auxiliares para trabalhar com arquivos. O resultado do comando enviado pelo cliente é colocado em um arquivo, temos que ler este aquivo e colocá-lo em uma matriz para enviar. Aqui nós fixamos o tamanho do array, mas o mais interessante seria trabalhar com alocação de memória dinâmica, pegando o número de caracteres retornados pela função ContaCaracteres, assim o programa fica melhor estruturado, pois o tamanho do array pode passar de 4096 e comportamentos não desejados, ou até que comprometam a segurança do sistema podem ocorrer.

int ContaCaracteres()
{
int contador = 1;
char letra;
FILE *arq = fopen("arqTemp","r");
letra = fgetc(arq);
while (letra != EOF)
{
contador++;
letra = fgetc(arq);
}
fclose(arq);
return contador;

}
void EnviaResultado(int * connfd)
{
FILE * arquivo;
char buffer[4096], letra;
int contador, x=0;


contador = ContaCaracteres();
arquivo = fopen("arqTemp","r");

letra = fgetc(arquivo);
while(x<contador)
{
buffer[x] = letra;
letra = fgetc(arquivo);
x++;
}
buffer[--x]='\0';


fclose(arquivo);


write(*connfd,buffer,sizeof(buffer));

}

Enfim, nosso programa main:

int main(int argc, char ** argv)
{
int listenfd, connfd, porta; //descitores de conexao e varialvel porta que ira armazenar a porta do servidor
struct sockaddr_in servaddr, cliaddr; //estruturas sockadd_in para armazenar os dados do server e do cliente
socklen_t len; //variavel para armazenar o tamanho da estrutura do cliente
char buff[4096];//vai armazenar informacoes sobre o cliente

int n,err;

if (strcmp("-h",argv[1]) ==0)
{
printf("\n\tChamada do programa:\n\t./serv numPort\n\tOnde numPort e o numero da porta TCP\n");
return 1;
}

else
if(argc != 2)
{
printf("\n\tErro parametros invalidos\n");
return 0;
}

porta = atoi(argv[1]);
listenfd = socket(AF_INET, SOCK_STREAM,0);


memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(porta);



bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd,1);



len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr,&len);//vamos aceitar o cliente

gravaLog(cliaddr, "Cliente conectou");
err = Autenticacao(&connfd);
if(!err)
return 0;



do
{



read(connfd,buff,sizeof(buff));
if(strcmp(buff,"exit")!=0)
strcat(buff, " > arqTemp");

);
system(buff);
EnviaResultado(&connfd);

}while(strcmp(buff,"exit"));

gravaLog(cliaddr, "Cliente desconectou");
close(connfd);


return 1;
}

Agora nosso programa cliente. O mesmo pode ser executado com o comando ./teste

O IP do servidor e a porta, já estão fixos no código. Seria interessante modificar, para que os mesmos sejam passados pela linha de comando.

#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>

int RequisitaAutenticacao(int * sockfd)
{
int err;
char buff[256];
char user[256], password[256];
char *ptr;


printf("\nInforme o usuário: ");

scanf("%s",user);
ptr = getpass("\nInforme sua senha:");
strcpy(password,ptr);

err = write(*sockfd, user, sizeof(user)); //envia usuario
err = write(*sockfd,password,sizeof(password)); //escreve a senha




err = read(*sockfd,buff,sizeof(buff));

if(!strcmp(buff,"1"))
return 1; //autenticacao OK
else
return -1; //autenticacao falhou


}

int main()
{
char buff[4096];
int sockfd, ret;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET,SOCK_STREAM,0);
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(4001);
inet_pton(AF_INET,"10.0.2.2", &servaddr.sin_addr);
connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr));
ret = RequisitaAutenticacao(&sockfd);
if(ret != 1)
{
printf("\n\tAutenticacao Falhou");
close(sockfd);
return 0;
}

else
printf("\n\tUsuario Autenticado com SUCESSO!!!");

do
{
printf("\n>");


fgets(buff,4095,stdin);
buff[strlen(buff)-1]='\0';
write(sockfd,buff,sizeof(buff));

if(strcmp(buff,"exit")!=0)
{

read(sockfd,buff,sizeof(buff));
printf("\n%s",buff);
}

}while(strcmp(buff,"exit"));
close(sockfd);



return 0;
}

quarta-feira, 6 de julho de 2011

Usando DllImport no Csharp (C#)

Algumas vezes temos a necessidade de usarmos funções que estão em Dlls que não foram escritas em C#. Surge então, alguns problemas, como passagens de parâmetros, passagens de ponteiros, entre outros que não são diretamente compatíveis entre C# e a linguagem em que a Dll foi escrita. O Objetivo desse tutorial é introduzir a utilização do comando DllImport utilizando uma Dll que foi escrita em C. Assim teremos funções que precisaremos passar ponteiros, structs, etc que precisarão de uma compatibilização.

Primeiramente vamos criar um projeto em C++ para criarmos nossa Dll. Vou apenas colocar o código que usamos para nossas funções e structs. O restante acrescentado pelo compilador será omitido.

Assim as funções de nossa DLL são as seguintes: Observe que normalmente você já terá a Dll que será importada, aqui estamos criando para fins de demonstração.

struct dados //estrutura simples
{
int valor;
};

struct dadosString //contem string interna
{
int valor;
char nome[50];
};

extern "C"
{
__declspec(dllexport) void Printf()
{
printf ("Parabens conseguimos importar a DLL\n");
}

}

extern "C"
{
__declspec(dllexport) void Printf_M(char * mensagem)
{
printf("A mensagem que voce quer mostrar na tela:\n%s\n",mensagem);
}
}

extern "C"
{
__declspec(dllexport) int ContaCaracteres(char * mensagem)
{
int contador = 0;
while(*mensagem)
{
contador++;
mensagem++;
}

return contador;
}
}

extern "C"
{
__declspec(dllexport) char* ConverteParaMaiuscula(char * mensagem)
{
char *p;
p = mensagem;
while(*p)
{
*p = toupper(*p);
p++;
}
return mensagem;
}
}

extern "C"
{
__declspec(dllexport) void PreencheEstruturaInt(struct dados * d)
{
d->valor = 45;
}
}

extern "C"
{
__declspec(dllexport) void PreencheEstruturaString(struct dadosString * d)
{
d->valor = 55;
strcpy(d->nome,"Teste da Funcao");
}
}

Bem simples....

Nosso próximo passo será criar um projeto em CSHARP, para chamarmos nossa DLL.

Dentro de nosso projeto vamos criar uma classe chamada: operacional.cs . É nessa classe que faremos a chamada do DllImport.

Aqui está o código da nossa classe. Veja que a string “path” contém o caminho onde está a Dll, você deve ajustar para onde sua Dll está, por exemplo: path = @“C:\minhaDll”;

Importante, para usar DllImport, precisamos incluir o seguinte namespace:

using System.Runtime.InteropServices;


class operacional
{
private const string path = @"C:\Users\Eduardo\Documents\Visual Studio 2010\Projects\Dll\Debug\Dll.dll";
[StructLayout(LayoutKind.Sequential)]
public struct dados
{
public int valor;
}

public struct dadosString
{
public int valor;
[MarshalAs(UnmanagedType.ByValTStr,SizeConst = 50)]public string nome;
}

//importa função: void Printf()
[DllImport(path)]
public static extern void Printf();

//Importa Função: void Printf_M(char * mensagem)
[DllImport(path, CallingConvention = CallingConvention.Cdecl)]
public static extern void Printf_M(string mensagem);

//Importa Função: int ContaCaracteres(char * mensagem)
[DllImport(path, CallingConvention = CallingConvention.Cdecl)]
public static extern int ContaCaracteres(string mensagem);

//Importa Função: char* ConverteParaMaiuscula(char * mensagem)
[DllImport(path, CallingConvention = CallingConvention.Cdecl)]
public static extern string ConverteParaMaiuscula(string mensagem);

//Importa Função: void PreencheEstruturaInt(struct dados * d)
[DllImport(path,CallingConvention = CallingConvention.Cdecl)]
public static extern void PreencheEstruturaInt(ref dados estrutura);

//Importa Função: PreencheEstruturaString(struct dadosString * d)
[DllImport(path,CallingConvention = CallingConvention.Cdecl)]
public static extern void PreencheEstruturaString(ref dadosString estrutura);

}

Agora a segue nosso programa de teste:

namespace ChamaDll
{
class Program
{
static void Main(string[] args)
{

int retorno=0;
string mensagem = "Teste com passagem de ponteiros";
operacional.dados estrutura;
estrutura.valor = 0;

//chamada sem parâmetros e sem valor de retorno
operacional.Printf();

//chamada com parametros
operacional.Printf_M(mensagem);

//Chamada com valor de retorno
retorno = operacional.ContaCaracteres(mensagem);
Console.WriteLine("Numero de caracteres: {0}",retorno);

//Chamada modificando o ponteiro - string
mensagem = operacional.ConverteParaMaiuscula(mensagem);
Console.WriteLine(mensagem);

//chamada com struct
Console.WriteLine("Valor da estrutura antes da chamada: {0}", estrutura.valor);
operacional.PreencheEstruturaInt(ref estrutura);
Console.WriteLine("Valor da estrutura depois da chamda: {0}", estrutura.valor);

//chama strutura que contem uma string
operacional.dadosString estruturaString;
estruturaString.nome = "NULL";
estruturaString.valor = 0;

operacional.PreencheEstruturaString(ref estruturaString);

Console.WriteLine("Nome: {0}, Idade:{1}", estruturaString.nome, estruturaString.valor);
Console.Read();
}
}
}



Resultado: