从线程中的标准输入读取以写入 c 中的文件

Posted

技术标签:

【中文标题】从线程中的标准输入读取以写入 c 中的文件【英文标题】:Reading from stdin in a thread to write in a file in c 【发布时间】:2014-07-23 10:18:53 【问题描述】:

我正在 c 中使用 TCP 创建一个 FTP 服务器。我的文件传输出现问题,我希望能够从客户端向/从服务器“放置”或“获取”文件。 我正在使用 select() 处理多连接并使用线程来处理文件传输。 我创建了一个简单的 .c 示例来总结我的问题:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void    *read_stdin(void * null)

  int   fd;
  int   len;
  char  ret;
  char  buff[1024];

  fd = open("dest", O_RDWR | O_TRUNC | O_CREAT, 0600);
  len = 1;
  while (len)
   
     ret = read(0, &len, 1);
     len = atoi(ret);
     if (len)
      
       read(0, buff, len);
       write(fd, buff, len);
      
   
 return (null);


int             main()

 pthread_t     t;
 int           fd;
 char           len;
 char          buff[1024];

  pthread_create(&t, NULL, &read_stdin, NULL);
  fd = open("source", O_RDONLY);
  while ((len = read(fd, buff, 1024)))
    
         write(0, &len, 1);
         write(0, buff, len);
    
   write(0, "0", 1);
  pthread_join(t, NULL);
  return (0);

现在我的问题是线程中的 read() 被阻塞了,它没有从主进程读取我在 STDIN 上写的内容,我不明白为什么,它在我的程序中做同样的事情吗但不是从标准输入读取,而是从套接字读取,但为了简单起见,我在此示例中使用了标准输入。

谢谢!

编辑:将 main 中的 len 从 int 更改为 char,并使用 char 读取我的 thread_func,然后使用 atoi() 进行转换。

【问题讨论】:

您是否将您的套接字设置为非阻塞——fcntl(sockfd, F_SETFL, O_NONBLOCK);?你真的在使用read() 从套接字读取吗? 并行运行 read 不会使其成为非阻塞的。您可能想在STDIN_FILENOselect 阅读。 当我说它阻塞时,我的意思是它没有读取我在主进程中写的内容,我不希望它是非阻塞的,我希望它读取我的我在 STDIN 上写作 这段代码有很多问题:写入标准输入,int/char 到处不匹配,在char 上调用atoi(),不检查返回值。 . 【参考方案1】:

write(0, &amp;len, 1);len 的一个字节写入stdin,但len 实际上有sizeof(int) 字节。此外,当您使用read(0, &amp;len, 1); 读取该值时,您只会读取一个字节而不是sizeof(int)

此外,您在每次执行循环时都会读取len 并在流中接收随机值。除非您不小心将空字节读入len,否则条件将始终为真,您最终会陷入无限循环。读取整个流后,下一次调用 read 将阻塞。

所以你应该正确地将消息的长度写入套接字并正确读回。此外,您还必须检查您是否已经读取了所有字节,以防止调用 read 阻塞。

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void* read_stdin(void * null) 
        int fd;
        char len;
        char ret;
        char buff[255];

        fd = open("dest", O_RDWR | O_TRUNC | O_CREAT, 0600);
        len = 1;
        while (len) 
                len = read(0, &len, sizeof(len));
                if (len) 
                        read(0, buff, len);
                        write(fd, buff, len);
                
        
        close(fd);
        return NULL;


int main() 
        pthread_t t;
        int fd;
        char len;
        char buff[128];

        pthread_create(&t, NULL, &read_stdin, NULL);
        fd = open("source", O_RDONLY);
        while (len = read(fd, buff, 128))  // you can only read 128 characters as len is a signed char (assuming an eight bit byte)
                write(0, &len, sizeof(len));
                write(0, buff, len);
        
        //write(0, "0", 1);
        close(0);
        close(fd);
        pthread_join(t, NULL);

        return 0;

请注意,此代码仅在标准输入未绑定到 tty 时才有效。如果是,在您自己阅读之前,它会被终端读取。 如果标准输入绑定到 tty,您将需要一个管道或套接字对,正如 loreb 在他的回答中提到的那样。

我尝试尽可能少地调整您的代码,以使其正常工作。它仍然不好,而且容易出错。不幸的是,我没有提及任何显示如何正确操作的内容。但也许这里的其他人可以提供参考。

【讨论】:

确实你是对的,我应该给 write 4 的第三个 arg 来写 int 或者使用一个 char 并且只写一个字节。我更新了我的更改,但它并没有改变我在线程函数中读取的不是读取 len 值或缓冲区的事实。 @user3795248 我很快就会回家,然后我可以进一步帮助您并为您提供一些代码。【参考方案2】:

不能写入 fd 0 并通过 read 读回相同的数据! 您需要某种特殊类型的 fd(管道、套接字对)在一端写入并从另一端读取。 请注意,如果您尝试,您可能会得到数据已写入但如果您的标准输入是 tty 则永远不会读取,请参阅以下程序(尝试从终端读取,然后使用重定向输入):

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define TRACE(wtf) \
        fprintf(stderr, "%u: %s...\n", __LINE__, wtf)
#define BUF 123
int main()

        char buf[BUF] = "abc\n";
        char got[BUF];
        int n;
        /* writes to tty, appears on terminal, but it's NOT READABLE FROM STDIN */
        if(write(0, buf, sizeof buf) < 0) 
                perror("write");
                return 111;
        
        TRACE("wok");
        /* read should block (until you type something) */
        if((n = read(0, got, sizeof got)) < 0) 
                perror("read");
                return 111;
        
        TRACE("rok");
        if(memcmp(buf, got, n) != 0)
                return printf("wtf?\n");
        return 0;

编辑:为了更清楚,这也是 addition foobar 的答案;我只是无法将代码示例放在评论中:)

【讨论】:

【参考方案3】:

编辑:

感谢您的回答。 我解决了我的问题: 我的文件传输是按块进行的,我首先将要读取的大小发送到将要接收文件的一侧,然后就在块本身之后。 但问题是,在将要接收数据的一侧,两个写入都在一端进行得太快,而在另一端无法读取。 因此,我必须在两次写入之间使用 read() 将 serv 与客户端同步,并在两次读取之间进行一次写入以同步它们。也许这会对某人有所帮助。

代码:

客户端想要获取文件时:

  #include <sys/types.h>
  #include <sys/socket.h>
  #include <inttypes.h>
  #include <fcntl.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #include <stdio.h>
  #include "client.h"

 static int      write_to_file(int fd, int cs)

   int           len;
   char          buff[2048];
   char          size[24];

   memset(size, 0, 24);
   read(cs, size, 24);
   len = atoi(size);
   memset(buff, 0, 2048);
   if (len)
    
      write(cs, "\n", 1);
      len = read(cs, buff, len);
      write(fd, buff, len);
    
  return (len);


int     get_file_content(t_client *c, char *filename)

  int   len;
  int   fd;

  if ((fd = open(filename, O_RDWR | O_TRUNC | O_CREAT, 0600)) == -1)
    return (my_perror(strerror(errno)));
  write(c->cs, c->cmd, strlen(c->cmd));
  len = 1;
  while (len)
    len = write_to_file(fd, c->cs);
  close(fd);
  printf("ok\n");
  return (0);

发送文件的“get”服务器端:

#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include "server.h"

static void     write_to_sck(int len, char *buff, t_fd *now)
 
  char          size[24];
  char          tmp[3];

  memset(size, 0, 24);
  memset(tmp, 0, 2);
  sprintf(size, "%d\n", len);
  write(now->sck, size, strlen(size));
  read(now->sck, tmp, 1);
  write(now->sck, buff, len);


 void            *write_file_content(void *ptr)
 
  t_fd          *now;
  t_files       *file;
  int           len;
  char          buff[2048];

  now = (t_fd *)ptr;
  find_file(now->head, &file, now->curr_file);
  pthread_mutex_lock(&file->m);
  if ((file->fd = open(file->name, O_RDONLY)) == -1)
   
     write(now->sck, strerror(errno), strlen(strerror(errno)));
      return (ptr);
   
   memset(buff, 0, 2048);
   while ((len = read(file->fd, buff, 2048)))
   
     write_to_sck(len, buff, now);
      memset(buff, 0, 2048);
    
  write(now->sck, "0\n", 1);
  close(file->fd);
  pthread_mutex_unlock(&file->m);
  now->threaded = 0;
  return (0);
 
int     get_func(t_fd *now)
 
 char  **tab;

 if (!(tab = str_to_wordtab(now->cmd)))
  
    write(now->sck, "ko\n", 3);
    return (my_perror("error: str_to_wordtab() in get_func()"));
  
 if (access(tab[1], R_OK) == -1)
  
    free_tab(tab);
     return (write(now->sck, strerror(errno), strlen(strerror(errno))));
  
 if (add_new_file(now, tab[1]))
  
    free_tab(tab);
    return (write(now->sck, "ko\n", 3));
  
 now->threaded = 1;
 pthread_create(&now->t, NULL, write_file_content, now);
 free_tab(tab);
 return (0);

我的服务器在 t_fd * 中有一个包含他和他的客户端的套接字链接列表,并且客户端在 t_client 中有一个包含他的 fd 的结构。

【讨论】:

以上是关于从线程中的标准输入读取以写入 c 中的文件的主要内容,如果未能解决你的问题,请参考以下文章

C语言,程序读取标准输入是啥意思?

使用 Qt 从标准输入异步读取

从标准输入读取所有文本到字符串

编写一个从标准输入读取并打印到标准输出的 c 程序,没有内置的行长限制

文本处理工具uniq去重与sort排序

文件操作