Tutorial - Aplicação em C para transferência de arquivo usando socket TCP e Thread

Aplicação baseada na arquitetura cliente-servidor escrita em C para realizar múltiplas conexões com diversos clientes para transferências de arquivos de qualquer tamanho. É uma ótimo exemplo para conhecer o funcionamento de thread com sockets e recursos em C para pesquisa em diretórios no Linux.

[ Hits: 22.316 ]

Por: Ronaldo Borges em 11/12/2015 | Blog: https://www.facebook.com/ronyjah1


Arquivo cliente, código em C - Transferência do arquivo



O cliente deve ser compilado e executado na máquina qual se queria receber o arquivo do servidor. No comando da aplicação cliente deve ser informado o endereço IP, a porta definida no servidor e o arquivo que quer ser obter do servidor.

Para compilar o código C da aplicação cliente copie e salve como cliente.c em sua máquina o arquivo abaixo e compile da seguinte forma:

gcc -o client client.c

Código C do cliente:

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

#define MAX_MSG 1024

/*
 Cliente envia mensagem ao servidor e imprime resposta
 recebida do Servidor
 */

int main(int argc, char *argv[]) {
    FILE *received_file;
    received_file = fopen(argv[3], "w");
    ssize_t len;
    char buffer[BUFSIZ];
    int aba;

      if (argc != 4) {
        fprintf(stderr, "use:./cliente [IP] [Porta] [arquivo]\n");
        return -1;
    } else if (!isdigit(*argv[2])) {
        fprintf(stderr, "Argumento invalido '%s'\n", argv[2]);
        fprintf(stderr, "use:./cliente [IP] [Porta] [arquivo]\n");
        return -1;
    }

    // variaveis
    int socket_desc;
    struct sockaddr_in servidor;
    char *mensagem;
    char resposta_servidor[MAX_MSG];
    int tamanho;
    mensagem = argv[3];
    char* aux1 = argv[2];
    int portaServidor = atoi(aux1);

    /*****************************************/
    /* Criando um socket */
    // AF_INET = ARPA INTERNET PROTOCOLS
    // SOCK_STREAM = orientado a conexao
    // 0 = protocolo padrao para o tipo escolhido -- TCP
    socket_desc = socket(AF_INET, SOCK_STREAM, 0);

    if (socket_desc == -1) {
        printf("Nao foi possivel criar socket\n");
        return -1;
    }

    /* Informacoes para conectar no servidor */
    // IP do servidor
    // familia ARPANET
    // Porta - hton = host to network short (2bytes)
    servidor.sin_addr.s_addr = inet_addr(argv[1]);
    servidor.sin_family = AF_INET;
    servidor.sin_port = htons(portaServidor);

    //Conectando no servidor remoto
    if (connect(socket_desc, (struct sockaddr *) &servidor, sizeof (servidor)) < 0) {
        printf("Nao foi possivel conectar\n");
        return -1;
    }
    printf("Conectado no servidor\n");
    /*****************************************/


    /*******COMUNICAO - TROCA DE MENSAGENS **************/

    //Enviando uma mensagem
    //mensagem 1 enviando nome do arquivo.  
    if (send(socket_desc, mensagem, strlen(mensagem), 0) < 0) {
        printf("Erro ao enviar mensagem\n");
        return -1;
    }
    printf("Dados enviados\n");

    memset(mensagem, 0, sizeof mensagem);
    memset(resposta_servidor, 0, sizeof resposta_servidor);

    //Recebendo resposta do servidor
    //mensagem 2 recebendo que arquivo existe   
    if((tamanho = read(socket_desc, resposta_servidor, MAX_MSG)) < 0) {
        printf("Falha ao receber resposta\n");
        return -1;
    }

    printf("Resposta recebida: %s\n", resposta_servidor);
    if (strcmp(resposta_servidor, "200") == 0) {

        mensagem = "OK";
        //mensagem 3 enviado ok
        write(socket_desc, mensagem, strlen(mensagem));
        //mensagem 4 recebendo o tamanho do arquivo;
        memset(resposta_servidor, 0, sizeof resposta_servidor);
        read(socket_desc, resposta_servidor, 1024);

        int tamanhoDoArquivo = atoi(resposta_servidor);
        printf("\nTamanho do arquivo a ser copiado: %s \n", resposta_servidor);
        aba = tamanhoDoArquivo;

    }else{
        fprintf(stderr, "Arquivo nao encontrado no servirdor'%s'\n", argv[3]);
        close(socket_desc);
        printf("Cliente finalizado com sucesso!\n");
        return 0;
    }

    while (((len = recv(socket_desc, buffer, BUFSIZ, 0)) > 0)&& (aba > 0)) {
        fwrite(buffer, sizeof (char), len, received_file);
        aba -= len;
        fprintf(stdout, "Recebidos %d bytes e aguardamos :- %d bytes\n", len, aba);
        if (aba <= 0) {
            break;
        }
    }
    fclose(received_file);
    close(socket_desc);

    printf("Cliente finalizado com sucesso!\n");
    return 0;
}

Conclusão

Não é a forma profissional de se fazer uma aplicação cliente servidor para transferência de arquivos usando socket e thread, porém acredito que pode ser muito útil para aquelas que estão iniciando em programação C, thread e sockets. Desta forma sugiro esta esta aplicação é apenas de cunho didático.

Obrigado.

Página anterior    

Páginas do artigo
   1. Arquivo servidor, código em C - Thread + socket
   2. Arquivo cliente, código em C - Transferência do arquivo
Outros artigos deste autor

Tutorial hadoop - Guia prático de um cluster com 3 computadores

Leitura recomendada

Monitorando o consumo de banda com Bwbar

BSD Sockets em linguagem C

O Modelo de Referência OSI

Controlando UPLOAD com o CBQ

Apreendendo a utilizar o GNU Debugger (parte 2)

  
Comentários
[1] Comentário enviado por mvforce em 14/12/2015 - 15:50h

Parabéns pelo tutorial... código bem claro e limpo.
Seria bastante didático se você colocasse antes do código fonte uma sequencia das ações lógicas do programa.

[2] Comentário enviado por ronyjah em 14/12/2015 - 16:15h


[1] Comentário enviado por mvforce em 14/12/2015 - 15:50h

Parabéns pelo tutorial... código bem claro e limpo.
Seria bastante didático se você colocasse antes do código fonte uma sequencia das ações lógicas do programa.


Este é meu primeiro artigo deve haver bastante falhas. Vou seguir seu conselho nos próximos (será tutorial sobre sistemas distribuídos, assim que sair gostaria de receber sua opinião). Muito obrigado prezado Mvforce.

[3] Comentário enviado por JairPMJr em 03/03/2016 - 08:21h

Para o seu primeiro artigo, esta muito bom.

Meus parabéns, o código bem comentado.

Abraço.

[4] Comentário enviado por camus88 em 21/04/2016 - 17:58h

Com ficaria com a função fork()? Estou estudando tal função mas ainda não consegui aplicá-la nesta situação. O fork(), diferente do Threads, ele duplica o processo criandoum semelhante filho alterando claro o PID(proces ID), minha duvida é onde colocaria está função(Linsten, Bind, Accept, Socket) para que assim tivesse multiplo acesso? Segue meu código de transferência de arquivo...

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

#define NOME_ARQ_MAXLEN 128
#define TAMMAX_BLOCO 65536

int main(int argc, char *argv[]){
if (argc != 2) {
printf("uso: %s <porta>\n", argv[0]);
return 0;
}

int ls;
struct sockaddr_in addr;
struct sockaddr_in clt_addr;
int len_addr, clt;
char nome_arq[NOME_ARQ_MAXLEN];
int nr, ns;
int fd;
int nr_r;
char bloco[TAMMAX_BLOCO];

//socket()
ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ls == -1) {
perror("socket()");
return -1;
}

//bind()
addr.sin_port = htons(atoi(argv[1]));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(ls,
(struct sockaddr *)&addr,
sizeof(struct sockaddr_in)) == -1) {
perror("bind()");
return -1;
}

//listen()
if (listen(ls, 1024) == -1){
perror("listen()");
return -1;
}
while (1) {
len_addr = sizeof(struct sockaddr_in);
clt = accept(ls,
(struct sockaddr*)&clt_addr,
(socklen_t *)&len_addr);
if (clt == -1) {
perror("accept()");
continue;
}
bzero(nome_arq, NOME_ARQ_MAXLEN);
nr = recv(clt, nome_arq, NOME_ARQ_MAXLEN, 0);
if (nr == -1){
perror("recv()");
continue;
}
fd = open(nome_arq, O_RDONLY);
if (fd == -1) {
char *resp;
resp = strerror(errno);
send(clt, resp, strlen(resp), 0);
continue;
}
ns = send(clt, nome_arq, nr, 0);
if (ns == -1) {
perror("send(nome_arq)");
continue;
}

do {
bzero(bloco, TAMMAX_BLOCO);
nr_r = read(fd, bloco, TAMMAX_BLOCO);
if (nr_r > 0){
ns = send(clt, bloco, nr_r, 0);
if (ns == -1) {
perror("send(bloco)");
continue;
}
}
}while(nr_r > 0);
close(fd);
close(clt);
}
close(ls);
return 0;
}

[5] Comentário enviado por arthuraureliops em 19/03/2022 - 17:16h

Olá Ronaldo, gostaria de tirar uma dúvida com você, poderia me enviar seu contato?


Contribuir com comentário