簡體   English   中英

使用套接字傳輸文件服務器/客戶端 linux C

[英]transfer files server/client linux C with socket

伙計們,我需要一些幫助。 我必須實現一個服務器/客戶端項目,以便將文件從客戶端傳輸到服務器。 服務器接收 N 個套接字,每個服務器進程一個套接字,使用 fork(),每個套接字(線程)超過 1 個客戶端。 客戶端向服務器發送一個文件,它必須備份它並顯示 TX 速度和字節數。 我解決了許多服務器進程的 TX 問題,但是當我嘗試在同一個套接字上應用並發線程時,它不起作用,沒有收到消息和文件。

服務器使用 1 個參數: number_of_sockets

客戶端使用 3 個參數: server_ip server_port file_name

服務器.c

#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/resource.h>
#include<sys/wait.h>
#include<signal.h>
#include<unistd.h>
#include<time.h>

// accepting connections
short int aceptar_conexiones = 1;

//struct for threads
typedef struct{
    int fd_socket;
    struct sockaddr_in address;
    socklen_t sock_length;
}thread_args;

//struct for associate server-process and sockets
typedef struct{
    int fd_socket;
    pid_t pid_proceso;
}fd_socket_proceso;

//max sockets open in server
unsigned int max_sockets;
//max server-process in server
unsigned int cant_procesos;
//dynamic array for associated struct
fd_socket_proceso *fdsp;
//mutex for sync threads
pthread_mutex_t mutex_thread;

#define TAM_BUFFER 1024

//handler for threads
//void *connection_handler(void *);
void connection_handler(void *);
void finaliza_sockets();
void finaliza_procesos();
void error(const char *);

int main(int argc, char *argv[]) {
    int fd_listen_socket, fd_communication_socket, i;
    max_sockets = atoi(argv[1]);
    struct sockaddr_in listen_address, connection_address;
    socklen_t con_addr_len = sizeof(connection_address);

    //allocating memory for dynamic array
    fdsp = (fd_socket_proceso *)malloc(sizeof(fd_socket_proceso)*max_sockets);

    //create and open sockets, grabbing it on array, it starts on 1024 and on, max 5 clients per socket
    for(i=0 ; i<max_sockets ; i++){
        fd_listen_socket = socket(AF_INET, SOCK_STREAM, 0);
        if(fd_listen_socket < 0) error("No se pudo crear el socket.\n");
        bzero(&listen_address, sizeof(struct sockaddr_in));
        listen_address.sin_family = AF_INET;
        listen_address.sin_port = htons(1024+i); // Puede utilizarse cualquier puerto
        listen_address.sin_addr.s_addr = htonl(INADDR_ANY); // Cualquier direccion propia
        if(bind(fd_listen_socket, (struct sockaddr *)&listen_address, sizeof(struct sockaddr)) < 0) error("No se puede enlazar el socket.\n");
        printf("Servidor escuchando en puerto: %d\n",ntohs(listen_address.sin_port));
        listen(fd_listen_socket, 5);
        fdsp[i].fd_socket = fd_listen_socket;
    }

    printf("Comenzamos a escuchar conexiones...\n");
    fflush(stdout);

    //fork per socket
    for(i=0 ; i<max_sockets ; i++ , cant_procesos++){
        if(!(fdsp[i].pid_proceso=fork())){
            while(aceptar_conexiones){
                bzero(&connection_address, sizeof(connection_address));
                if((fd_communication_socket = accept(fdsp[i].fd_socket, (struct sockaddr *)&connection_address, &con_addr_len))==-1){
                    finaliza_sockets();
                    finaliza_procesos();
                    perror("Error en la comunicacion con el socket");
                    exit(EXIT_FAILURE);
                }
                pthread_t thread_cliente;
                pthread_attr_t thread_attr;
                thread_args t_args;
                pthread_attr_init(&thread_attr);
                pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
                t_args.address = connection_address;
                t_args.fd_socket = fd_communication_socket;
                t_args.sock_length = con_addr_len;
                //pthread_create(&thread_cliente, NULL, connection_handler, (void *)&t_args);
                connection_handler((void *)&t_args);
            }
        }
    }
    finaliza_sockets();
    finaliza_procesos();
    printf("Server offline.\n");
}

//void *connection_handler(void *t_args)
void connection_handler(void *t_args)
{
    thread_args *t = (thread_args *)t_args;
    char buffer[TAM_BUFFER];
    char *buffer2;
    FILE *fp;
    char *datos[2];
    char file_name[TAM_BUFFER];
    char *ip_cliente = inet_ntoa(t->address.sin_addr);
    int port_cliente = ntohs(t->address.sin_port);
    long int file_size, bytes_restantes;
    ssize_t recv_size;
    int t_inicio ,t_fin , t_transferencia;
    int i;

    bzero(buffer, sizeof(buffer));
    //1st msg recieve file_size concat using char '°' with file_name
    recv_size = recvfrom(t->fd_socket, buffer, sizeof(buffer), 0, (struct sockaddr *)&(t->address), &(t->sock_length));

    //parse file_size and file_name
    buffer2=strtok(buffer,"°");
    for(i=0 ; buffer2 ; i++){
        datos[i]=buffer2;
        buffer2=strtok(NULL,"°");
    }
    file_size = atoi(datos[0]);
    strcpy(file_name, datos[1]);

    //concat file_name for backup
    strcat(file_name, ".bkp");
    if(fp=fopen(file_name, "r")){
        printf("\nError, el fichero %s provisto por el cliente %s ya existe! Este sera omitido por el servidor.\n",file_name, ip_cliente);
        fflush(stdout);
        close(t->fd_socket);
        return;
    }else{
        if((fp=fopen(file_name,"w"))==NULL){
            close(t->fd_socket);
            printf("\nError en la creacion del fichero %s provisto por el cliente %s.\n",file_name, ip_cliente);
            fflush(stdout);
            exit(errno);
        }else{
            bytes_restantes = file_size;
            bzero(buffer, sizeof(buffer));
            //calculate begin TX
            t_inicio = clock();
            //recieve file from sendfile() from client
            recv_size = recv(t->fd_socket, buffer, sizeof(buffer), 0);
            printf("llego, recv: %ld\n",recv_size);
            while(recv_size > 0 && bytes_restantes > 0){
                //calculate finish TX
                t_fin = clock();
                bytes_restantes -= recv_size;
                //write on file descriptor
                fwrite(buffer, sizeof(char), recv_size, fp);
                //calculate TX speed
                t_transferencia = t_fin-t_inicio/CLOCKS_PER_SEC;
                //print data about TX
                printf("Cliente:%s:%d, Fichero:%*s, Bytes recibidos:%*ld, Bytes restantes:%*ld, Velocidad TX:%d (bytes/seg)\r",
                    ip_cliente, port_cliente, strlen(file_name)*1L, file_name ,sizeof(long int), file_size-bytes_restantes, sizeof(long int), 
                    bytes_restantes, t_transferencia);
                fflush(stdout);
                bzero(buffer, sizeof(buffer));
                //recieve file from sendfile() from client
                recv_size = recv(t->fd_socket, buffer, sizeof(buffer), 0);
                t_inicio = clock();
            }
        }
    }
    printf("\nCliente:%s:%d\tFichero:%s\tTransferencia finalizada.\n",ip_cliente, port_cliente, file_name);
    fflush(stdout);
    fclose(fp);
    close(t->fd_socket);
    //pthread_exit(0);
}
//close sockets
void finaliza_sockets(){
    int i;
    for(i=0 ; i<max_sockets ; i++)
        close(fdsp[i].fd_socket);
    free(fdsp);
}
//wait for all server-process
void finaliza_procesos(){
    int i;
    for(i=0 ; i<cant_procesos ; i++)
        wait(0);
}
void error(const char *msg){
    perror(msg);
    exit(errno);
}

客戶端

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<signal.h>
#include<time.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/sendfile.h>

#define TAM_BUFFER 1024

int main(int argc, char *argv[])
{
    int fd_socket;
    int i;
    int tini, tfin, ttransferencia;
    struct sockaddr_in server_address;
    char *path, *buffer, *vect[30], linea[TAM_BUFFER], linea2[TAM_BUFFER], file_name[TAM_BUFFER/4];

    int fd_file;
    long int send_bytes, offset, remain_data;
    struct stat file_stat;

    //file_name , maybe use path
    path = argv[3];

    if((fd_file = open(path, O_RDONLY))==-1){
        perror("error en la apertura del archivo\n");
        exit(EXIT_FAILURE);
    }
    if(fstat(fd_file, &file_stat)<0){
        perror("error fstat");
        exit(EXIT_FAILURE);
    }

    //connection with server
    fd_socket = socket(AF_INET, SOCK_STREAM, 0);
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(atoi(argv[2]));
    server_address.sin_addr.s_addr = inet_addr(argv[1]);

    bzero(&(server_address.sin_zero),8);    

    signal(SIGINT,SIG_DFL);

    //establishing connection
    if(connect(fd_socket,(struct sockaddr *)&server_address,sizeof(struct sockaddr)) < -1){
        perror("error on connection with server");
        exit(errno);
    }

    //parse file_name from path 
    i=0;
    buffer=strtok(path,"/");
    while(buffer != NULL)
    {
        vect[i]=buffer;
        buffer=strtok(NULL,"/");
        i++;
    }

    //concat file_size with char '°' and file_name
    strcpy(file_name, vect[i-1]);
    sprintf(linea,"%ld",file_stat.st_size);
    strcat(linea,"°");
    strcat(linea, file_name);

    //send concatenated char
    send_bytes = send(fd_socket,linea,strlen(linea),0);

    //send file to server
    offset = 0;
    remain_data = file_stat.st_size;
    while(((send_bytes = sendfile(fd_socket, fd_file,(off_t *) &offset, TAM_BUFFER)) > 0) && (remain_data > 0)){
        printf("enviados: %*ld bytes\toffset: %*ld\tbytes restantes: %ld\r",sizeof(send_bytes), send_bytes, sizeof(offset), offset, remain_data);
        sizeof(stdout);
        remain_data -= send_bytes;
    }

    close(fd_file);
    close(fd_socket);
    return 0;
}

正如我所說,我的問題是我需要在每個套接字上進行並發。 我如何管理和同步它們? 我嘗試了基本的東西,但它不起作用,所以我評論了thread_createthread_exit func。

謝謝你的時間。-

我認為主要結構沒有任何問題。 您正在偵聽多個端口,為每個端口生成一個進程。 每個進程(應該)為每個客戶端生成一個新線程。

您在使用 thread_args.address 的服務器中確實存在競爭條件(如果兩個客戶端快速連續連接,可能會被覆蓋),但很容易避免。 不要在 TCP 套接字上使用 recvfrom —— 這真的沒有意義。 使用 read() 或更好的 fread(),它可以避免 EINTR 問題。 在開始時一次讀取一個字節,直到遇到第一個分隔符(盡管最好更改協議以發送 32 位字而不是 ascii 表示)。

但主要問題是你有一個很大的程序,你只是說“它不起作用”。 只要您希望我們為您調試,發布一些診斷信息如何? 服務器進程是否被創建? 打印什么? 對於第一個客戶端,第二個客戶端還是什么,它是否失敗?

您可以使用 fork() 創建子進程,父進程可以監聽,當客戶端連接時,父進程可以將該客戶端移交給子進程並繼續監聽其他客戶端。 然后當另一個客戶端再次連接 fork() 並且再次連接時,您可以創建另一個子進程來處理該客戶端。 像這樣,您可以同時處理客戶端。

pid = fork()

當 pid = 0 時它是子進程,如果 pid > 0 它是父進程,如果 pid < 0 則分叉錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM