【问题标题】:Sending file (client to server and server to client)发送文件(客户端到服务器和服务器到客户端)
【发布时间】:2015-01-17 16:02:06
【问题描述】:

我有 client.c 和 server.c ,我正在尝试将文件从客户端发送到服务器以及从服务器发送到客户端... 文件由服务器接收,但当服务器发送文件时,客户端停止或不接收文件, sendFile() 函数:

void sendfile(int fd,char* filename)
{
    char buff[0x1000]; 
    FILE *file = fopen(filename, "rb"); 
    if (!file)
    {
        printf("Can't open file for reading"); 
        return;
    }
    while (!feof(file)) 
    { 
        int rval = fread(buff, 1, sizeof(buff), file); 
        if (rval < 1)
        {
            printf("Can't read from file\n");
            fclose(file);
            return;
        }

        int off = 0;
        do
        {
            int sent = send(fd, &buff[off], rval - off, 0);
            printf("Sending ...\n");
            if (sent < 1)
            {
                printf("Can't write to socket");
                fclose(file);
                return;
            }
            printf("[SendFile]Fisierul a fost trimis!\n");
            off += sent;
        }
        while (off < rval);
    } 
 fclose(file);
} 

getFile():

void getFile(int fd,char* filename)
 {
    int rval; 
    char buff[0x1000]; 
    FILE *file = fopen(filename, "w+"); 
    if (!file)
    {
        printf("Can't open file for writing");
        return;
    }

    do
    {
        rval = recv(fd, buff, sizeof(buff), 0);
        if (rval < 0)
        {
            printf("Can't read from socket");
            fclose(file);
            return;
        }

        if (rval == 0)
            break;

        int off = 0;
        do
        {
            int written = fwrite(&buff[off], 1, rval - off, file);
        printf("[server]Writing %d bytes\n",written);
            if (written < 1)
            {
                printf("Can't write to file");
                fclose(file);
                return;
            }

            off += written;
        }
        while (off < rval);
    printf("[server]File received!\n"); 
    }  
    while(1);
    fclose(file); 
 }

客户端main():

int main (int argc, char *argv[])
{
  int sd;
  struct sockaddr_in server;

  if (argc != 2)
    {
      printf ("[client] Sintaxa: %s <filename>\n", argv[0]);
      return -1;
    }

  if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("[client] Eroare la socket().\n");
      return errno;
    }

  server.sin_family = AF_INET;
  server.sin_addr.s_addr = inet_addr("127.0.0.1");
  server.sin_port = htons (port);

  if (connect (sd, (struct sockaddr *) &server,sizeof (struct sockaddr)) == -1)
    {
      perror ("[client]Eroare la connect().\n");
      return errno;
    }
    sendfile(sd,"test.cpp");
    printf("File sent\n");
    getFile(sd,"server.c");
    close (sd);
}

来自 server.c 的循环

while (1)
    {
      int client;
      int length = sizeof (from);

      printf ("[server]Asteptam la portul %d...\n",port);
      fflush (stdout);

      client = accept (sd, (struct sockaddr *) &from, &length);

      if (client < 0)
      {
      perror ("[server]Eroare la accept().\n");
      continue;
      }

    if((pid=fork()) == 0 )
    {

    getFile(client,"test.cpp");
        sendfile(client,"server.c");
        printf("File sent\n");
    close(client);
  } 

 }

【问题讨论】:

  • 不要这样做while(!feof(...)),因为它几乎在所有情况下都不会像您期望的那样工作。原因是文件结束标志直到 您尝试从文件之外读取时才设置,这意味着这样的循环将迭代一次太多。相反,例如while ((res = fread(...)) &gt; 0)
  • 另外,您可能想了解 Linux 系统调用 sendfile
  • 程序到底挂在哪里?在此之前是否有任何调用发出错误信号?它表示哪个错误?如果您删除 fork() 代码并只编写两个单独的可执行文件会怎样?
  • fork 用于使服务器并发...当我从 client.c 中删除行 getFile(sd,"server.c") 时,程序正确结束并且服务器执行该行with printf("文件已发送\n");
  • 我知道fork() 做了什么,我的建议背后的想法是通过删除这部分来降低复杂性。此外,如果用固定值替换文件输入怎么办?此外,关于它挂在哪里,你的回答没有帮助。你也可以说它挂在main() 的某个地方。您必须在阻塞的呼叫上进一步圈子。

标签: c linux sockets


【解决方案1】:

开始。

  1. accept()、connect()、recv() .. 都是阻塞调用,所以理论上你的程序可以在这些调用的任何地方被阻塞,直到你使用非阻塞套接字。从程序看起来你正在使用阻塞套接字

  2. 我建议你先阅读这样一个好的套接字编程教程https://beej.us/guide/bgnet/

其次

我建议您使用诸如 wireshark 之类的工具或任何其他数据包分析工具来调试您的问题。

最后你可以做类似下面的代码sn-ps来试试

  1. 使用 fnctl API 调用将套接字设置为非阻塞模式。

    int rc = fcntl(newFd, F_GETFL,0);
    rc |= O_NONBLOCK;
    rc = fcntl(newFd, F_SETFL,rc);
    
  2. 为事件轮询套接字 您可以使用 select、poll 之类的方法
    相同的。这是 Beej 教程中选择的一个很好的示例。 https://beej.us/guide/bgnet/html/multi/selectman.html

  3. 正确处理错误。非阻塞套接字可以返回必须正确处理的 EAGAIN 或 EWOULDBLOCK 错误。该错误意味着套接字目前不可读取或不可写,请查找事件并重试。有点意思。

      rval = recv(fd, buff, sizeof(buff), 0);
      if (rval < 0)
      {
           if((errno == EWOULDBLOCK)||(errno ==EAGAIN))
           {
               continue;
           }
           else
               //cleanup
       }
    

【讨论】:

    【解决方案2】:

    tre 问题是来自 getFile 的do { ... } while(1); ...所以我删除了循环...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多