首页 > 解决方案 > C套接字在同一请求中上传和下载文件

问题描述

我正在尝试接收客户端上传的文件,并在同一个套接字描述符中发送命令从服务器中以块的形式下载文件

问题是,如果套接字描述符在不同的文件中,事情就可以完美地工作,但是如果相同的文件客户端和服务器程序挂起

第二个问题是即使它在不同的文件中,我也无法向客户端发送一条消息,说明文件已收到

谁能请教

PS- 运行程序可能需要创建一个名为 fileclient.txt 的文件并输入一些随机文本

服务器.c

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

#define PORT 8088

int main(void){   
    int fd =0, confd = 0,b,tot;
    struct sockaddr_in serv_addr;

    char buff[1025];
    fd = socket(AF_INET, SOCK_STREAM, 0);
    printf("Socket created\n");

    memset(&serv_addr, '0', sizeof(serv_addr));
    memset(buff, '0', sizeof(buff));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(PORT);

    bind(fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    listen(fd, 10);

    while(1){
        confd = accept(fd, (struct sockaddr*)NULL, NULL);
        if (confd==-1) {
            perror("Accept");
            continue;
        }

        //READ COMMAND reads first 4 characters of the buffer
        int bf = recv(confd, buff, 1024,0);
        char *filecmd = malloc(5 * sizeof(char));
        memcpy(filecmd, buff, 4);
        filecmd[4] = 0; //string termination 

        //if command is DOWN send chunks to the client 
        if(strcmp(filecmd, "DOWN") == 0){
            int bt;
            char sendbuffer[100];
            FILE *fpdl = fopen("file.txt", "rb");
            if(fpdl == NULL){
                perror("File");
                return 2;
            }
            while( (bt = fread(sendbuffer, 1, sizeof(sendbuffer), fpdl))>0 ){
                send(confd, sendbuffer, bt, 0);
            }
            fclose(fpdl);
        }
        //if DOWN did not match that means client is uploading a file write down the file on server
        else{           
            FILE* fp = fopen( "newfile.txt", "wb");
            tot=0;
            tot+=bf;
            fwrite(buff, 1, bf, fp);

            if(fp != NULL){
                while( (b = recv(confd, buff, 1024,0))> 0 ) {                   
                    tot+=b;
                    fwrite(buff, 1, b, fp);
                }
                printf("Received byte: %d\n",tot);
                if (b<0) perror("Receiving Error");
                fclose(fp);

            } else {
                perror("File");
            }
        }
        free(filecmd);
        close(confd);
    }
    return 0;
}

客户端.c

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

#define PORT 8088


int main(int argc, char *argv[]){

    int sfd =0, n=0, b;
    char sendbuffer[100];

    struct sockaddr_in serv_addr;
    sfd = socket(AF_INET, SOCK_STREAM, 0);

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    b=connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (b==-1) {
        perror("Connect");
        return 1;
    }

    FILE *fp = fopen("fileclient.txt", "rb");
    if(fp == NULL){
        perror("File");
        return 2;
    }

    while( (b = fread(sendbuffer, 1, sizeof(sendbuffer), fp))>0 ){
        send(sfd, sendbuffer, b, 0);
    }
    fclose(fp);


    char *rpccommand = "DOWN"; 
    send(sfd , rpccommand , strlen(rpccommand) , 0 ); 

    char buffer[1024] = {0}; 
    FILE* fpx = fopen( "downloadfile.txt", "wb");
    int tot=0, bn;
    if(fpx != NULL){
        while( (bn = recv(sfd, buffer, 1024,0))> 0 ) {
            tot+=bn;
            fwrite(buffer, 1, bn, fpx);
        }
        printf("Received byte: %d\n",tot);
        if (bn<0) perror("Receiving");
        fclose(fpx);
    } else {
        perror("File");
    }
    return 0; 
}

标签: csockets

解决方案


您似乎期望服务器识别字符串“DOWN”并触发下载。这不太可能发生。

TCP 连接不关心放入套接字的数据粒度。如果您在一侧一次放入 1000 个字节,则不能保证一次性收到这 1000 个字节。您可能会收到 1000 字节或 500 + 500 字节的 999 + 1 字节。

如果您发送多个请求,同样适用。放入 1000+4 字节可能会导致接收 1000+4 或 1004 或 500+500+4 字节或任何其他组合。

这意味着您不能简单地依赖于首先接收文件上传的所有字节,然后等待另一个命令(“DOWN”)或另一个文件。您很可能会收到“DOWN”以及上传文件的最后一个字节,并将它们简单地存储到输出文件中。

这同样适用于下载方向。客户端将无法区分文件下载和“上传完成”通知。

要解决您的问题,您需要在协议中引入更多逻辑。有多种选择:

  • 每个操作或每个方向使用 1 个套接字和 1 个连接。无需将所有内容混合到一个套接字中。
  • 添加一些关于文件上传的指示,例如在上传前预期的总长度。
  • 使用 1 个套接字进行控制流,使用多个其他套接字进行数据传输(有关详细信息,请参阅 FTP)

推荐阅读