网络编程有关文件传输的问题

网络编程 文件传输
一个功能是输入ls,显示服务端当前目录下所有文件,另一个功能是输入 download +文件名,将该文件从服务器传到客户端,为什么运行时不管输入什么命令都是服务端输出bad file descriptor,客户端输出Unsupported command,这是为什么?怎么修改?
代码如下
服务端

//server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
#include<fcntl.h>
#define BUFFER_SIZE 1024
void list_dir(int sockfd)
 {
    char buffer[BUFFER_SIZE];
    memset(buffer, 0, sizeof(buffer));
    DIR *dir;
    struct dirent *entry;
    if ((dir = opendir(".")) == NULL)
        {
        sprintf(buffer, "无法打开目录\n");
        if (send(sockfd, buffer, strlen(buffer), 0) < 0) {
            perror("send error");
            exit(EXIT_FAILURE);
               }
        return;
        }
while ((entry = readdir(dir)) != NULL)
 {
      if (entry->d_name[0] != '.')
           {
            strcat(buffer, entry->d_name);
            strcat(buffer, "\n");
           }
    }
    if (send(sockfd, buffer, strlen(buffer), 0) < 0)
 {
        perror("send error");
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char *argv[]) 
{
    char str[100];
    char *pos;
    ssize_t n;
    FILE *fp;
    char buffer[BUFFER_SIZE];
    // 创建socket
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) 
    {
        perror("socket error");
        exit(EXIT_FAILURE);
    }    
    // 设置服务端地址
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr)); //结构体初始化
    servaddr.sin_family = AF_INET;
    // servaddr.sin_port = htons(argv[2]);
    servaddr.sin_port = htons(atoi(argv[1]));
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    // 绑定端口
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) 
    {
        perror("bind error");
        exit(EXIT_FAILURE);
    }
    // 监听端口
    if (listen(listenfd, 5) < 0) 
    {
        perror("listen error");
        exit(EXIT_FAILURE);    
    }
    printf("服务端已启动,等待客户端连接...\n");
    // 循环接收客户端连接
    int connfd;
    struct sockaddr_in cliaddr;
    memset(&cliaddr, 0, sizeof(cliaddr));
    socklen_t clilen = sizeof(cliaddr);

        connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
        if (connfd < 0) 
        {
            perror("accept error");
        }
        printf("客户端 %s:%d 已连接\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));//ip地址和端口号?
        // 发送欢迎信息
        memset(buffer, 0, sizeof(buffer));
        sprintf(buffer, "欢迎连接到服务端!");
        if (send(connfd, buffer, strlen(buffer), 0) < 0) 
        {
            perror("send error");
            exit(EXIT_FAILURE);
        }
        // 循环接收命令并执行
        while (1) 
        {
             n = read(connfd, buffer, sizeof(buffer));
            //memset(buffer, 0, sizeof(buffer));
            if (n < 0)
            {
                perror("recv error");
                exit(1);
            }
            if (strcmp(buffer, "exit") == 0) 
            {
                printf("客户端 %s:%d 已断开连接\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
                break;
             } else if (strcmp(buffer, "ls") == 0) 
            {
                list_dir(connfd);
             } else if(strncmp(buffer,"download ",9)==0)
                {  
                char *filename = buffer + 9;
                filename[strlen(filename)-1]='\0';
                 //recv(connfd,str,1024,MSG_DONTWAIT);
                  //pos = strchr(str, ' ');
                // 打开文件
                fp = fopen(filename, "rb");
                if (fp == NULL)
                {
                                   // 文件不存在
                   strcpy(buffer, "File not found!\n");
                   write(connfd, buffer, strlen(buffer));
                } else
                                {
                    // 读取文件并发送给客户端
                    while ((n = fread(buffer, 1, 1024, fp)) > 0)
                    {
                        write(connfd, buffer, n);
                    }
                    fclose(fp);
                }
              } else {
                                // 不支持的命令
                strcpy(buffer, "Unsupported command!\n");
                write(connfd, buffer, strlen(buffer));
            }
        // 关闭客户端连接
        close(connfd);
    }
// 关闭socket
    close(listenfd);
    return 0;
}

客户端

//client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 1024
int main(int argc, char *argv[]) {  
ssize_t n;  
  // 创建socket    
 int sockfd = socket(AF_INET, SOCK_STREAM, 0);
 if (sockfd < 0) {        perror("socket error");        exit(EXIT_FAILURE);    }    
  // 设置服务端地址    
 struct sockaddr_in servaddr;    
 memset(&servaddr, 0, sizeof(servaddr));    
 servaddr.sin_family = AF_INET;    
 servaddr.sin_port = htons(atoi(argv[1]));    
 servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");    
  // 连接服务端    
 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)  
  {        
  perror("connect error");         
  exit(EXIT_FAILURE);    
  }    
  // 接收服务端欢迎信息    
 char buffer[BUFFER_SIZE];     
 memset(buffer, 0, sizeof(buffer));    
 if (recv(sockfd, buffer, sizeof(buffer), 0) < 0)  
  {        
  perror("recv error");        
  exit(EXIT_FAILURE);    
 }    
 printf("%s\n", buffer);    
  // 循环输入命令并接收回复    
 while (1)  
 {        
      // 获取用户输入的命令
   printf("请输入命令:");         
   memset(buffer, 0, sizeof(buffer));        
   fgets(buffer, sizeof(buffer), stdin);        
   buffer[strlen(buffer) - 1] = '\0'; 

    // 发送命令到服务器
    write(sockfd, buffer, strlen(buffer));

// 读取服务器的响应
    while ((n = read(sockfd, buffer, 1024)) > 0)
    {
        fwrite(buffer, 1, n, stdout);
    } 
   //printf("%s\n", buffer);
 }    
  // 关闭socket    
 close(sockfd);    
 return 0;
}

修改了一下server的代码,关键点就一句,在接收到数据后,如果n >0,加一句buffer[n]=0;
代码中加了一些打印,微调了一下代码结构,if(n>0)的代码放在前面了。clent没改。
另外一个问题是,你的服务端在n<0的时候就结束了,这么做是不对的,建议在输入exit的时候(或者连接断开的时候)退出,否则你的代码无法处理多条指令。
运行结果:

img

server代码:

//server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
#include<fcntl.h>
#define BUFFER_SIZE 1024
void list_dir(int sockfd)
{
    char buffer[BUFFER_SIZE];
    memset(buffer, 0, sizeof(buffer));
    DIR *dir;
    struct dirent *entry;
    if ((dir = opendir(".")) == NULL)
    {
        sprintf(buffer, "无法打开目录\n");
        if (send(sockfd, buffer, strlen(buffer), 0) < 0) {
            perror("send error");
            exit(EXIT_FAILURE);
        }
        return;
    }
    while ((entry = readdir(dir)) != NULL)
    {
        if (entry->d_name[0] != '.')
        {
            strcat(buffer, entry->d_name);
            strcat(buffer, "\n");
        }
    }
    if (send(sockfd, buffer, strlen(buffer), 0) < 0)
    {
        perror("send error");
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char *argv[])
{
    char str[100];
    char *pos;
    ssize_t n;
    FILE *fp;
    char buffer[BUFFER_SIZE];
    // 创建socket
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0)
    {
        perror("socket error");
        exit(EXIT_FAILURE);
    }
    // 设置服务端地址
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr)); //结构体初始化
    servaddr.sin_family = AF_INET;
    // servaddr.sin_port = htons(argv[2]);
    servaddr.sin_port = htons(atoi(argv[1]));
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    // 绑定端口
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        perror("bind error");
        exit(EXIT_FAILURE);
    }
    // 监听端口
    if (listen(listenfd, 5) < 0)
    {
        perror("listen error");
        exit(EXIT_FAILURE);
    }
    printf("服务端已启动,等待客户端连接...\n");
    // 循环接收客户端连接
    int connfd;
    struct sockaddr_in cliaddr;
    memset(&cliaddr, 0, sizeof(cliaddr));
    socklen_t clilen = sizeof(cliaddr);

    connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
    if (connfd < 0)
    {
        perror("accept error");
    }
    printf("客户端 %s:%d 已连接\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));//ip地址和端口号?
    // 发送欢迎信息
    memset(buffer, 0, sizeof(buffer));
    sprintf(buffer, "欢迎连接到服务端!");
    if (send(connfd, buffer, strlen(buffer), 0) < 0)
    {
        perror("send error");
        exit(EXIT_FAILURE);
    }
    // 循环接收命令并执行
    while (1)
    {
        n = read(connfd, buffer, sizeof(buffer));
        //memset(buffer, 0, sizeof(buffer));
        printf("receved[%d bytes]:%s\n",n,buffer);
        if(n > 0)
        {
            printf("deal %s...\n",buffer);
            buffer[n]='\0'; //mod
            if (strcmp(buffer, "exit") == 0)
            {
                printf("客户端 %s:%d 已断开连接\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
                break;
            } else if (strcmp(buffer, "ls") == 0)
            {
                list_dir(connfd);
            } else if(strncmp(buffer,"download ",9)==0)
            {
                char *filename = buffer + 9;
                filename[strlen(filename)-1]='\0';
                //recv(connfd,str,1024,MSG_DONTWAIT);
                //pos = strchr(str, ' ');
                // 打开文件
                fp = fopen(filename, "rb");
                if (fp == NULL)
                {
                    // 文件不存在
                    strcpy(buffer, "File not found!\n");
                    write(connfd, buffer, strlen(buffer));
                } else
                {
                    // 读取文件并发送给客户端
                    while ((n = fread(buffer, 1, 1024, fp)) > 0)
                    {
                        write(connfd, buffer, n);
                    }
                    fclose(fp);
                }
            } else {
                // 不支持的命令
                strcpy(buffer, "Unsupported command!\n");
                write(connfd, buffer, strlen(buffer));
            }
        }
        else
        {
            perror("recv error");
            exit(1);
        }

        // 关闭客户端连接
        close(connfd);
    }
    // 关闭socket
    close(listenfd);
    return 0;
}


服务端两个问题:
1、bad file descriptor是权限问题,首先你要确认你当前的文件夹权限;
2、你的服务端把接收的循环去掉了,你只能处理一次客户端的命令。


 // 循环接收客户端连接
    int connfd;
    struct sockaddr_in cliaddr;
    memset(&cliaddr, 0, sizeof(cliaddr));
    socklen_t clilen = sizeof(cliaddr);

    while(1){//如果你不想只收一次客户端的数据,这个循环不能去
        connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
        if (connfd < 0) 
        {
            perror("accept error");
        }
        printf("客户端 %s:%d 已连接\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));//ip地址和端口号?
        // 发送欢迎信息
        memset(buffer, 0, sizeof(buffer));
        sprintf(buffer, "欢迎连接到服务端!");
        if (send(connfd, buffer, strlen(buffer), 0) < 0) 
        {
            perror("send error");
            exit(EXIT_FAILURE);
        }
        // 循环接收命令并执行
        while (1) 
        {
            //将接收的字符串清空
            memset(buffer, 0, sizeof(buffer));
            //阻塞式接收
            n = read(connfd, buffer, sizeof(buffer));
            printf("客户端 %s",buffer);//打印接收数据
            if (n < 0)
            {
                perror("recv error");
                exit(1);
            }
            if (strcmp(buffer, "exit") == 0) 
            {
                printf("客户端 %s:%d 已断开连接\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
                break;
             } 
             else if (strcmp(buffer, "ls") == 0) 
             {
                list_dir(connfd);
             } 
             else if(strncmp(buffer,"download ",9)==0)
             {  
                char *filename = buffer + 9;
                filename[strlen(filename)-1]='\0';
                 //recv(connfd,str,1024,MSG_DONTWAIT);
                  //pos = strchr(str, ' ');
                // 打开文件
                fp = fopen(filename, "rb");
                if (fp == NULL)
                {
                                   // 文件不存在
                   strcpy(buffer, "File not found!\n");
                   write(connfd, buffer, strlen(buffer));
                }
                else
                {
                    // 读取文件并发送给客户端
                    while ((n = fread(buffer, 1, 1024, fp)) > 0)
                    {
                        write(connfd, buffer, n);
                    }
                    fclose(fp);
                }
              }
              else {
                // 不支持的命令
                strcpy(buffer, "Unsupported command!\n");
                write(connfd, buffer, strlen(buffer));
            }
        }
        // 接收exit指令 退出接收,关闭客户端连接
        close(connfd);
    }

至于,Unsupported command是你自己发送的命令是不是出了问题,可以断点或者加日志判断一下

输出的都是代码逻辑中的else分支,说明if条件不满足,把buffer输出看看是什么内容。

您好,我是有问必答小助手,您的问题已经有小伙伴帮您解答,感谢您对有问必答的支持与关注!
PS:问答VIP年卡 【限时加赠:IT技术图书免费领】,了解详情>>> https://vip.csdn.net/askvip?utm_source=1146287632不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答来自chatgpt:

    该问题的原因是在服务端代码中,在传输文件时未设置传输完成的标志,导致客户端无法判断何时传输完成,从而出现了bad file descriptor的错误;同时在客户端代码中,在传输文件时未按照预定的格式传输,导致服务端无法正确识别命令,从而出现Unsupported command的错误。解决方法如下:

    1. 在服务端代码中,在传输文件前添加传输文件大小的信息和传输完成的标志。具体代码如下:
    # 从服务器传输文件到客户端
    filename = data.split(' ')[-1]
    filesize = os.path.getsize(filename)
    client_socket.send(f'{filename},{filesize}'.encode('utf-8'))
    
    with open(filename, 'rb') as f:
        while True:
            chunk = f.read(1024)
            if not chunk:
                # 传输完成的标志
                client_socket.send('EOF'.encode('utf-8'))
                break
            client_socket.send(chunk)
    
    1. 在客户端代码中,在接收到服务端传来的文件信息后,按照预定的格式传输文件。具体代码如下:
    # 处理服务端发来的数据
    if data.startswith('Unsupported command'):
        print('不支持的命令,请重新输入!')
    elif ',' in data:
        # 从服务器接收文件到本地
        filename, filesize = data.split(',')
        with open(filename, 'wb') as f:
            while True:
                chunk = client_socket.recv(1024)
                if chunk == b'EOF':
                    break
                f.write(chunk)
        print(f'{filename} 文件传输成功!')
    else:
        print('服务端发来的消息:', data)
    

    注意代码中的细节处理,包括EOF的编码解码、传输文件大小等。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^