首页 > 解决方案 > C 通过 UDP 套接字传输文件 (linux)

问题描述

我对套接字真的很陌生,大部分代码都是通过从其他线程及其答案中获取代码和建议来编写的,所以这可能很难阅读。

这个线程特别是我从中获取大部分代码的线程。

我的任务包括创建一个能够连接到服务器的应用程序,并向连接到它的其他客户端发送和接收消息和文件。基本上,每台机器同时作为客户端和服务器工作,只要它们连接到服务器,就能够发送和接收数据包。

我已经能够发送和接收消息(字符串),但是,我还不能发送或接收文件,坦率地说,我不知道该怎么做,因为我们之前没有太多关于套接字的解释被赋予了这个任务。

以防万一,套接字必须是 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";
                }
            }           
        }
    }

对不起。服务器端和客户端必须在同一个文件中,这真的很长。

在尝试发送文件时,程序在 snedto 之后的第二个 cout 中返回 SIGSEGV“段违规”。

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

我猜服务器端有问题,但我不知道它到底是什么。

任何帮助将不胜感激。

标签: cfilesocketsudptransfer

解决方案


推荐阅读