C Передача файлов через сокеты UDP (linux) - PullRequest
1 голос
/ 22 июня 2019

Я действительно новичок в сокетах, и большая часть кода написана на основе фрагментов кода и советов из других потоков и их ответов, так что это может быть трудно читать.

ЭтоВ частности, поток был тем, из которого я брал большую часть кода.

Мое назначение состоит в создании приложения, способного подключаться к серверу, а также отправлять и получать сообщения и файлы другим подключенным к нему клиентам,По сути, каждая машина работает как клиент и сервер одновременно, имея возможность отправлять и получать пакеты, пока они подключены к серверу.

Я уже могу отправлять и получать сообщения(строки), однако, я пока не могу отправлять или получать файлы, и, честно говоря, я понятия не имею, как это сделать, так как нам было мало объяснено о сокетах до того, как мне дали это назначение.

На всякий случай сокеты ДОЛЖНЫ быть UDP .Я не могу использовать TCP в этом случае.

Вот код:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include <sys/stat.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <string.h>
    #include <iostream>
    #include <sys/socket.h>
    #include <time.h>
    #include <netinet/in.h>
    #include <arpa/inet.h> // para inet_Addr, etc
    #include <netdb.h> // estructuras
    #include <sys/signal.h>
    #include <ctype.h>
    #include <openssl/md5.h>
    #include "PB.h"
    #include <fcntl.h>
    #include <ctime>
    #include <iomanip>

    #define MAX_LARGO_IP 16
    #define MAX_USPW 25
    #define MAX_LARGO_MENSAJE 255
    #define MAX_NOMBRE_ARCHIVO 20
    #define MAX_LARGO_ARCHIVO 65535
    #define BUFLEN 503

    int fd1;

    using namespace std;

    void printProgress (double percentage);

    unsigned long fsize(char* file)
    {
        FILE * f = fopen(file, "r");
        fseek(f, 0, SEEK_END);
        unsigned long len = (unsigned long)ftell(f);
        fclose(f);
        return len;
    }

    int main(int argc, char * argv[]){
        if (argc < 4)
        {
                cout << "\33[46m\33[31m[ERROR]:" << " Faltan argumentos: port, ipAuth, portAuth.\33[00m\n";
                exit (1);
        }

        // Signal handler
        // *********************************
        struct sigaction sa;
        memset (&sa, 0, sizeof (sa));
        sa.sa_handler = &manejadorSenhales;

        sigaction(SIGINT, &sa, NULL);
        sigaction(SIGTERM, &sa, NULL);
        sigaction(SIGPIPE, &sa, NULL);
        sigaction(SIGSEGV, &sa, NULL);
        sigaction(SIGKILL, &sa, NULL);
        signal(SIGALRM, SIG_IGN);
        // **********************************

        cout << "\33[34mRedes de Computadoras 2018\33[39m: Sistema de Mensajeria.\nEscuchando en el puerto " << argv[1] << ".\nProceso de pid: " << getpid() << ".\n";

        char * user = new(char[MAX_USPW]);  
        char * pass = new(char[MAX_LARGO_MENSAJE]);
        char * buf = new(char[MAX_LARGO_MENSAJE]);
        char * msjAnterior = new(char[MAX_LARGO_MENSAJE]);
        char * mensaje = new(char[MAX_LARGO_MENSAJE]);

        cout << "Usuario: ";
        cin >> user;
        cout << "Clave: ";
        cin >> pass;

        strcpy(mensaje, user);
        strcat(mensaje, "-");


    ///////////////////
    // AUTENTICATION // THIS PART CONNECTS YOU TO THE SERVER //////////////////////////////
    ///////////////////

        int numbytes;
        unsigned long ultimoRemitente = 0;
        strcpy(msjAnterior, "\0"); //inicializo el buffer como vacio

        fd1 = socket(AF_INET, SOCK_STREAM, 0);

        if (fd1 < 0){
            cout << "ERROR: socket()\n";
            exit (0);
        }

        struct hostent *he;
        struct sockaddr_in server;

        socklen_t longitudServer = sizeof(server);

        if ((he=gethostbyname(argv[2]))==NULL){       
            printf("ERROR: gethostbyname()\n");
                exit(0);
        }

        server.sin_family = AF_INET;
        server.sin_port = htons(atoi(argv[3])); 
        server.sin_addr = *((struct in_addr *)he->h_addr);  
        bzero(&(server.sin_zero),8);

        if(connect(fd1, (struct sockaddr *)&server, sizeof(struct sockaddr))==-1){ 
            printf("ERROR: connect()\n");
            exit(0);
        }

        if ((numbytes=recv(fd1,buf,MAX_LARGO_MENSAJE,0)) == -1){  
            printf("ERROR: recv()\n");
                exit(0);
        }

        buf[numbytes-2]='\0';

        if (strcmp(buf, "Redes 2019 - Obligatorio 2 - Autenticacion de Usuarios") != 0){
            printf("ERROR: protocolo incorrecto()\n");
                exit(0);
        }

        mensaje[strlen(mensaje) + 1] = '\n';
        mensaje[strlen(mensaje)] = '\r';

        send(fd1, mensaje, strlen(mensaje),0);

        if ((numbytes=recv(fd1,buf,MAX_LARGO_MENSAJE,0)) == -1){  
            printf("ERROR: recv()\n");
                exit(0);
        }

        buf[numbytes-2]='\0';

        if (strcmp(buf, "NO") == 0){
            printf("ERROR: Autenticacion Incorrecta.\n");
                exit(0);
        }

        if ((numbytes=recv(fd1,buf,MAX_LARGO_MENSAJE,0)) == -1){  
            printf("ERROR: recv()\n");
                exit(0);
        }

        buf[numbytes-2]='\0';

        struct sockaddr_in cliente;
        socklen_t largoCliente = sizeof(cliente);

        // ALL OF THIS IS USED TO SHOW DATE AND TIME OF EACH MESSAGE
        time_t timer, timerNew;       
        tm fechaActual, fechaNew; 
        time(&timer); 
        fechaActual=*(localtime(&timer)); 

        int archivo, n;

        cout << "Bienvenido " << buf << endl;

        close (fd1);

        int pid = fork();

        if (pid < 0){
            cout << "\33[46m\33[31m[ERROR]:" << " Imposible bifurcar.\33[00m\n";
            exit(0);
        }

        if (pid > 0){

    /////////////////
    // SERVER SIDE // WHERE MESSAGES AND FILES ARE RECEIVED ///////////////
    /////////////////

            printf("\33[34mRx\33[39m: Iniciada parte que recepciona mensajes. Pid %d\n", getpid());

            char ipOrigen[MAX_LARGO_IP];
            unsigned int fd_rx;
            char fname[MAX_NOMBRE_ARCHIVO];
            char flen[20];
            char messagef[BUFLEN];
            FILE *fp;
            fd_rx = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if(fd_rx < 0){
                cout << "ERROR: socket()\n";
                exit (0);
            }
            server.sin_family = AF_INET;
            server.sin_port = htons(atoi(argv[1])); 
            server.sin_addr.s_addr = INADDR_ANY;
            bzero(&(server.sin_zero),8);

            if(bind(fd_rx,(struct sockaddr*)&server, sizeof(struct sockaddr))){
                cout << "ERROR: bind()\n";
                exit (0);
            }
            while (true)
            {
                //sleep(3);
                if(recvfrom(fd_rx, buf, MAX_LARGO_MENSAJE, 0, (struct sockaddr *)&cliente, &largoCliente) == -1) // Si da error, poner sizeof(server) o &longitudServer 
                        printf("ERROR: 1 recvfrom()\n");
                else
                {
                    time(&timerNew); 
                    fechaNew=*(localtime(&timerNew));
                    // MAKES SURE A MESSAGE ISN'T SENT TWICE IN A SHORT AMOUNT OF TIME
                    if((strcmp(msjAnterior,buf) != 0)   || (fechaNew.tm_sec > fechaActual.tm_sec ) || (ultimoRemitente != cliente.sin_addr.s_addr))
                    {
                        time(&timer);
                        fechaActual=*(localtime(&timer)); 
                        cout << setfill('0');
                        cout << "[" << fechaActual.tm_year + 1900 << "." << setw(2) << fechaActual.tm_mon + 1 << "." << setw(2) << fechaActual.tm_mday
                        << " " << setw(2) << fechaActual.tm_hour << ":" << setw(2) << fechaActual.tm_min << "] " ; // SHOWS DATE AND TIME
                        strcpy(ipOrigen, inet_ntoa(cliente.sin_addr)); 
                        if(strcmp(buf, "peer_discover")==0)
                            cout << "envio de peer discover" << endl;
                        else if (strcmp(buf, "enviar") == 0)
                        {
            ///////////////////////////////////////////////////////////////
            ////// THIS IS WHERE THE FILE TRANSFER PART STARTS ////////////
            ///////////////////////////////////////////////////////////////
                            if(recvfrom(fd_rx, messagef, 20, 0, (struct sockaddr *)&cliente, &largoCliente) == -1)
                                printf("ERROR: 2 recvfrom()\n");
                            memset(messagef, 0, 503);
                            if(recvfrom(fd_rx, messagef, 20, 0, (struct sockaddr *)&cliente, &largoCliente) == -1)
                                printf("ERROR: 3 recvfrom()\n");
                            strcpy(flen, messagef);
                            unsigned long mm = atoi(messagef);
                            fp = fopen(flen, "wb");
                            int itr=1;
                            memset(messagef, 0, 503);
                            while(itr*503 < mm)
                            {
                                if(recvfrom(fd_rx, messagef, 503, 0, (struct sockaddr *)&cliente, &largoCliente) == -1){
                                    printf("ERROR: 4 recvfrom()\n");
                                    exit(0);
                                }
                                fwrite(messagef, 503, 1, fp);
                                memset(messagef, 0, 503);
                                itr++;
                            }
                            if (recvfrom(fd_rx, messagef, (mm % 503), 0, (struct sockaddr *)&cliente, &largoCliente) == -1)
                                printf("ERROR: 5 recvfrom()\n");
                            fwrite(messagef, (mm % 503), 1, fp);
                            memset(messagef, 0, 503);
                            fclose(fp);
                        }
            ///////////////////////////////////////////
            ////// FILE TRANSFER PART ENDS ////////////
            ///////////////////////////////////////////
                        else
                            cout << ipOrigen << " "  << buf << endl;    
                        strcpy(msjAnterior, buf); 
                        ultimoRemitente = cliente.sin_addr.s_addr;
                        strcpy(buf, "\0"); 
                    }
                }
            }
        } 
        else if (pid == 0)
        {
    ////////////////////
    // CLIENT SIDE ///// WHERE MESSAGES AND FILES ARE SENT FROM ////////////////
    ////////////////////
            printf("\33[34mTx\33[39m: Iniciada parte que transmite mensajes. Pid %d\n", getpid());

            char ipDestino[MAX_LARGO_IP]; 
            char buffS[MAX_LARGO_MENSAJE + 1];
            char message[MAX_LARGO_MENSAJE];
            char * enviar = new(char[7]);
            strcpy(enviar, "enviar");
            char fname[BUFLEN];
            unsigned long siz;
            char str[10];
            FILE *f;
            int enviados = 0;
            unsigned int fd_tx;
            fd_tx = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if(fd_tx < 0){
                cout << "ERROR: socket()\n";
                exit (0);
            }
            while (true)
            {
                server.sin_family = AF_INET;
                server.sin_port = htons(atoi(argv[1])); 
                bzero(&(server.sin_zero),8);
                cin >> ipDestino;
                if (ipDestino[0] == '*'){
                    int broadcastPermission = 1;
                    if (setsockopt(fd_tx, SOL_SOCKET, SO_BROADCAST, (void *) &broadcastPermission,sizeof(broadcastPermission)) < 0){
                        fprintf(stderr, "setsockopt error");
                        exit(1);
                       }
                }
                else if(ipDestino[0] == '&')
                    cout << "Se hace peer_discover" << endl;
                else if ((he=gethostbyname(ipDestino))==NULL){       
                        printf("ERROR: gethostbyname()\n");
                        exit(0);
                    }
                    server.sin_addr = *((struct in_addr *)he->h_addr);  
                cin.ignore(1,' ');
                cin.getline(buffS,MAX_LARGO_MENSAJE,'\n');
                printf("ipDestino: %s, mensaje: %s \n", ipDestino, buffS);
                if (buffS[0] == '&')    
                { 
            ///////////////////////////////////////////////////////////////
            ////// THIS IS WHERE THE FILE TRANSFER PART STARTS ////////////
            ///////////////////////////////////////////////////////////////
                    cout << "\nSE QUIERE MANDAR ARCHIVO\n";
                    sendto(fd_tx, enviar, sizeof(char), 0, (struct sockaddr *)& server, sizeof(server));
                    cout << "\nSE MANDO NOMBRE ARCHIVO\n";
                    int i = 8;
                    int j = 0;
                    while (buffS[i] != '\n'){
                        fname[j++] = buffS[i++];
                    }
                    cout << "nombre archivo: " << fname << endl; // muestra el nombre para saber si esta bien. ELIMINAR
                    sleep(1);
                    sendto(fd_tx, fname, 20, 0, (struct sockaddr *)&server, sizeof(server));
                    siz = fsize(fname);
                    sprintf(str, "%d", siz);
                    sendto(fd_tx, str, 20, 0, (struct sockaddr *)&server, sizeof(server));
                    f = fopen(fname, "rb");
                    memset(message, 0, 503);
                    fread(message, 503, 1, f);
                    int itr = 1;
                    while(itr*503 < siz)
                    {
                        if(sendto(fd_tx, message, 503, 0, (struct sockaddr*)&server, sizeof(server)) == -1){
                            cout << "ERROR: 1 send()\n";
                            exit(0);
                        }
                        memset(message, 0, 503);
                        fread(message, 503, 1, f);
                        itr++;
                    }
                    fread(message, (siz % 503), 1, f);
                    if(sendto(fd_tx, message, (siz % 503), 0, (struct sockaddr*)&server, sizeof(server)) == -1){
                        cout << "ERROR: 2 send()\n";
                        exit(0);
                    }
                    fclose(f);
                }
            ///////////////////////////////////////////
            ////// FILE TRANSFER PART ENDS ////////////
            ///////////////////////////////////////////
                else{
                    // SEND MESSAGES
                    enviados = sendto (fd_tx, buffS, sizeof(char) * (strlen(buffS) + 1), 0, (struct sockaddr *)&server, sizeof(server));
                    if(enviados != -1)
                        cout << enviados << "bytes enviados.\n" ;
                    else
                        cout << "No se pudo enviar el mensaje.\n";
                }
            }           
        }
    }

Извините.Это действительно долго, поскольку и серверная, и клиентская стороны должны находиться в одном и том же файле.

При попытке отправить файл программа возвращает «нарушение сегмента» SIGSEGV во втором cout, после snedto.

cout << "\nSE QUIERE MANDAR ARCHIVO\n";
                        sendto(fd_tx, enviar, sizeof(char), 0, (struct sockaddr *)& server, sizeof(server));
                        cout << "\nSE MANDO NOMBRE ARCHIVO\n"; <--- RIGHT HERE

Полагаю, что-то не так с серверной стороной, но я понятия не имею, что именно это может быть.

Любая помощь будет принята с благодарностью.

...