2

伙计们,我需要一些帮助。我必须实现一个服务器/客户端项目来将文件从客户端传输到服务器。服务器接收 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);
}

客户端.c

#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。

谢谢你的时间。-

4

2 回答 2

1

我认为主要结构没有任何问题。您正在侦听多个端口,为每个端口生成一个进程。每个进程(应该)为每个客户端生成一个新线程。

您在服务器中确实存在带有 thread_args.address 的竞争条件(如果两个客户端快速连续连接,可能会被覆盖),但很容易避免。不要在 TCP 套接字上使用 recvfrom ——这真的没有意义。使用 read(),甚至更好的 fread(),它可以避免 EINTR 问题。在开始时一次读取一个字节,直到遇到第一个分隔符(尽管最好将协议更改为发送 32 位字而不是 ascii 表示)。

但主要问题是你有一个大程序,你只是说“它不起作用”。只要您希望我们为您调试它,发布一些诊断信息怎么样?是否创建了服务器进程?打印什么?第一个客户,第二个客户失败还是什么?

于 2013-06-18T22:22:58.257 回答
1

您可以使用 fork() 创建子进程,父进程可以监听,当客户端连接时,父进程可以将该客户端移交给子进程并继续监听其他客户端。然后当另一个客户端再次连接时 fork() 并且您可以再次创建另一个子进程来处理该客户端。像这样你可以同时处理客户端。

pid = fork()

当 pid = 0 时,它是子进程,如果 pid > 0 它是父进程,如果 pid < 0 则分叉时出错。

于 2020-05-05T13:17:12.470 回答