Erros ao manipular arquivos

1. Erros ao manipular arquivos

Frederick Honorio e Silva
FredySilva

(usa Ubuntu)

Enviado em 07/04/2013 - 18:39h

Boa noite, estou fazendo uma aplicação cliente/servidor onde o cliente informa o caminho do arquivo e o envia ao servidor e ele salva o arquivo. Ambos compilam o código segue abaixo e gostaria de saber se e possível enviar o arquivo compactado.

Grato

cliente.c

#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>

int sock;
struct sockaddr_in address;
char caminho[100], tam[6];
FILE *origem, *copia;
char *localOrigem = "/home/maryfredy/Área de Trabalho/origem.txt";
char *localCopia = "/home/maryfredy/Área de Trabalho/copia.txt";

int conectar(char *server) {

if ((sock = socket(AF_INET, SOCK_STREAM, 0)) > 0) {
printf("Socket configurado\n");
}
address.sin_family = AF_INET;
address.sin_port = htons(8888);

inet_pton(AF_INET,server,&address.sin_addr);

if (connect(sock,(struct sockaddr *)&address,sizeof(address)) == 0) {
return 1;
}

return 0;

}

int abrirArquivo( ) {

if ((origem=fopen (caminho, "rb")) == NULL)
{
printf ("o arquivo de origem nao pode ser aberto\n");
exit (1);
}
if ((copia=fopen (localCopia, "wb")) == NULL)
{
printf ("o arquivo para copia nao pode ser aberto\n");
exit (1);
}
}

long calcTamArq(FILE *arquivo) {


long Atual = ftell(arquivo);
long tam;

fseek(arquivo, 0l, SEEK_END);
tam = ftell(arquivo);

fseek(arquivo, Atual, SEEK_SET);
printf ("TAMANHO: %ld\n",tam);
return tam;
}

void enviar(char mensagem[] ){
if(send(sock,mensagem,sizeof(mensagem),0)<0){
perror("Erro no envio de dados");
exit(1);
}
}

char* recebe(){
char recebido[100];
if(recv(sock,recebido,sizeof(recebido),0)<0){
perror("Erro no recebimento de dados ");
exit(1);
}
}

void enviaChar(char mensagem ){
if(send(sock,mensagem,sizeof(mensagem),0)<0){
perror("Erro no envio ");
exit(1);
}
}

main ( ){

char ch[1];
long tamArq;
char serverName[20];

printf("Digite o IP ou hostname do servidor ao qual deseja de conectar: ");
gets(serverName);

if(conectar(serverName)){
printf ("Digite o caminho do arquivo:\n");
gets(caminho);
abrirArquivo ( );
tamArq = calcTamArq(origem);
sprintf(tam,"%ld",tamArq);
printf ("TAMANHO: %s\n",tam);
enviar(tam);

while(!feof(origem))
{
ch[0] = getc (origem);
if(ferror(origem)) {
printf("erro lendo o arquivo");
break;
}
enviar(ch);
putc (ch[0], copia);
if(ferror(copia)) {
printf("erro na gravacao do arquivo");
break;
}
}
fclose (origem);
fclose (copia);
return 0;
}


else {
printf("\nNao foi possi­vel conectar...");
}


}

servidor.c

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>

int sockServer, sockClient, addrlen;
struct sockaddr_in address;
char arquivo[100], copia[6]
;
int iniciaServer() {

system("clear");

if ((sockServer = socket(AF_INET, SOCK_STREAM, 0)) > 0) {
printf("Socket criado\n");
}

address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8888);

if (bind(sockServer,(struct sockaddr *)&address,sizeof(address)) == 0) {
printf("Aguardando conexao...\n");
}

listen(sockServer, 3);

addrlen = sizeof(struct sockaddr_in);

sockClient = accept(sockServer,(struct sockaddr *)&address,&addrlen);

if (sockClient > 0) {
return 1;
}

return 0;

}

void receber (){

if(recv(sockClient,copia,sizeof(copia),0)<0){
perror("Erro no recebimento de dados");
exit(1);
}
}

void recebeChar (char ch[]){

if(recv(sockClient,ch,sizeof(ch),0)<0){
perror("Erro no recebimento de dados");
exit(1);
}
}

main ( ){

char *localCopia = "/home/maryfredy/Área de Trabalho/copia.txt";
FILE *Copia;
char ch[1];
long fileSize;

if (iniciaServer( )) {
printf("\nConexao estabelecida!");

if ((Copia=fopen (localCopia, "wb")) == NULL)
{
printf ("Arquivo para copia nao pode ser aberto\n");
exit (1);
}

printf("\nTamanho do Arquivo: %s\n\n", copia);
fileSize = atof(copia);
printf("\nTamanho do Arquivo: %ld\n\n", fileSize);

int i;
for(i=0;i<fileSize;i++){
recebeChar(ch);
putc (ch[0], Copia);

if(ferror(Copia)) {
printf("erro na gravacao do arquivo");
break;
}
}

printf("fim");
fclose (Copia);
fclose (sockServer);
close(sockClient);

}
}


os erros
no servidor

Falha de segmentação (imagem do núcleo gravada)


e no cliente
Erro no envio de dados: Bad file descriptor





  


2. Re: Erros ao manipular arquivos

Paulo
paulo1205

(usa Ubuntu)

Enviado em 09/04/2013 - 17:24h

Sim, é possível enivar o arquivo compactado. Você pode usar uma biblioteca para comprimir os dados a serem transmitidos e descomprimir os dados que estiverem sendo recebidos. Leia sobre a zlib, por exemplo.

Quanto aos seus problemas, segue primeiro uma dica geral: habitue-se a ligar o máximo de opções de diagnóstico do compilador. Para a família do GCC, eu sempre recomendo "-Wall -Werror -O2 -pedantic". Isso vai pegar uma porção de erros bobos, como variáveis não incializadas, conversões possivelmente erradas em printf() e scanf(), atribuições misturadas entre inteiros e ponteiros, argumentos de função de tipos incompatíveis com aqueles que a função espera, uso de funções inseguras ou obsoletas (como gets(), por exemplo), uso de práticas obsoletas, entre outras coisas. Só fazendo isso, você já vai ter algumas coisas a alterar nos códigos acima.

Fora isso, temos no cliente os seguintes problemas saltando aos olhos (i.e. eu não testei, apenas olhei o que você escreveu).

1) Você pede para que se digite "IP ou hostname" do servidor mas, na função conectar(), usa inet_pton(), que trabalha apenas com IPs. Para tratar hostnames, você precisará de gethostbyname() ou equivalente.

2) Ainda na função conectar(), o primeiro argumento de socket() deveria ser PF_INET, não AF_INET. Ainda que, na prática, a maioria das implementações use o mesmo valor para os dois símbolos, um programa não deve contar com isso como se fosse sempre verdade. Nos demais lugares onde aparece AF_INET, o uso está correto.

3) Você testa se o socket foi criado com sucesso só para dizer que o foi? Não acha que deveria tratar a possibilidade de erro?

4) O argumento da função conectar() poderia ser const char *, em lugar de char *. O que você fez não é estritamente um erro, mas é sempre bom que uma função que não altere o valor referenciado por um ponteiro o declare como sendo um ponteiro para dados constantes.

5) Dado o tamanho da variável global tam, um array de caracteres de seis posições, o tamanho máximo dos seus arquivos não pode ser maior do que 99999 bytes. Se for, a função sprintf() vai escrever os bytes excedentes por cima de uma área de memória pertencente a outra variável ou mesmo fora da que está alocada para o programa. E isso é mau, muito mau.

6) A função enviar() usa o tamanho de mensagem (cujo tipo é ponteiro para caracteres) como quantidade de bytes a serem enviados. Esse tamanho é sempre o mesmo (4 bytes numa máquina com ponteiros de 32 bits, 8 bytes numa com ponteiros de 64 bits), não dependendo realmente dos dados que você passa à função quando a chama noutras partes do programa. Acho que você não quer realmente isso, já que numa hora passa dados de seis bytes, noutra dados com apenas um byte. Possivelmente você teria de passar à função um parâmetro a mais, dizendo o real tamanho em bytes dos dados a serem enviados.

7) Você realmente quer ler, enviar e gravar um byte de cada vez? Isso acaba ficando muito dispendioso. Seria melhor trabalhar com blocos de bytes (por exemplo, 4kB de cada vez), usando fread(), fwrite() e a versão modificada da sua enviar().

8) Você não fechou o socket aberto antes de terminar o programa. No seu caso, isso não é grave porque o programa termina logo em seguida, mas é bom você se acostumar com a devolução explícita dos recursos que você requisitar ao sistema (melhor ainda seria uma prática como RAII, associada a destrutores de objeto, mas isso praticamente só é usado em C++).

9) Você não chamou exit() nem um comando "return valor_inteiro;" ao final de main(). Deveria tê-lo feito, pois, apesar de você também não ter declarado explicitamente -- o que também deveria ter feito! --, a função main() deve sempre sair devolvendo um valor inteiro. Tipicamente, esse valor é zero se a execução tiver sido bem sucedida, e um valor diferente de zero costuma indicar erro.


A maioria dos problemas no servidor é análoga aos do cliente. Acrescento alguns comentários.

1) Por que atof(), se o tipo de dados desejado é long? Use atol() ou, preferencialmente, strtol(), que têm como verificar se a conversão teve erro.

2) Você mistura a declaração de variáveis e comandos. Isso é válido em C++ e nas versões do C de 1999 em diante, mas muitos compiladores ainda usam o C de 1989/1990, em que isso não é válido. Na dúvida, seja explícito no código, ou prefira a forma de 1989, em que todas as delcarações deve preceder outros comandos presentes no mesmo bloco.

3) Na chamada a recv(), dentro de recebeChar(), de novo você incorre em erro no tamanho dos dados. Como você só está recebendo um bytes de cada vez, pode ser explícito e colocar o valor inteiro 1, já que sizeof(char) é sempre 1, por definição. Alternativamente, pode usar "sizeof *ch". Contudo, é desperdício ler byte a byte; crie um bloco, e leia em blocos, passando o tamanho do bloco como parâmetro para função, e fazendo com que ela retorne o tamanho efetivamente lido (que pode ser menor do que o tamanho do bloco).






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts