2011-12-16 16 views
2

私はマルチスレッドのWebサーバーをプログラムしました。ここにはプログラムの1つの機能があります。この関数は、出力ファイル記述子(fd)、コンテンツタイプ、提供するデータへのポインタ(*buf)、およびデータのサイズ(numbytes)を取ります。それは常に5775バイトで立ち往生する!私はsend()の代わりにwrite()を使ってみましたが、役に立たない!私は一度にbuf全体を送ろうとしましたが、それをチャンクで転送しようとしましたが、wgetは5775バイトにぎこちないことを示しています!ここでは、コードは次のようになります。TCP接続でファイル全体を送信できません! (UNIX C)

int return_result(int fd, char *content_type, char *buf, int numbytes) 
{ 
    char out_buf[BUF_SIZE], numb[6]; 
    int buf_len, total = 0, buf_size; 
    long int i = 0; 
    sprintf(numb, "%d", numbytes); 
    strcpy(out_buf, "HTTP/1.1 200 OK \nContent-Type: "); 
    strcat(out_buf, content_type); 
    strcat(out_buf, "\nContent-Length: "); 
    strcat(out_buf, numb); 
    strcat(out_buf, "\nConnection: Close\n \n"); 
    printf("\nSending HTTP Header\n %d bytes sent!", 
      send(fd, out_buf, strlen(out_buf), 0)); 
    char *start = NULL, *str = NULL, *temp = NULL; 
    start = buf; 
    printf("\n Start Pointer Val = %ld", &start); 
    while (start != NULL) { 
     printf("\n While Loop"); 
     if (i + 2048 * sizeof(char) < numbytes) { 
      printf("\n If 1"); 
      str = (char *)malloc(sizeof(char) * 2048); 
      memcpy(str, start, sizeof(char) * 2048); 
      i = i + 2048 * sizeof(char); 
      buf_size = send(fd, str, 2048, 0); 
      free(str); 
      printf("\n Sent %d bytes total : %d", buf_size, total = 
        total + buf_size); 

      temp = start + sizeof(char) * 2048; 
      start = temp; 

     } else { 

      i = numbytes - i * sizeof(char); 
      if (i > 0) { 
       printf("\n If 2"); 
       printf("\n Value of i %d", i); 
       str = (char *)malloc(sizeof(char) * i); 
       memcpy(str, start, sizeof(char) * i); 
       printf("Total bytes finally sent:%d", total = 
         total + send(fd, str, i, 0)); 
       if (total == numbytes) { 
        printf("\nTransfer Complete!"); 
       } 
       free(str); 

      } 
      start = NULL; 
     } 
    } 
    printf("out of loop!"); 
    return 0; 
} 
+0

あなたが実際に得ている「最終的には送信された合計バイト数を:..」のメッセージを、あなたが期待するものに一致しますか?もしそうなら、それはバッファリング/フラッシュの問題かもしれません。 –

+0

はい、実際にはサーバー側からすべてのバイトを送りますが、wgetは5775しか受け取りません! – Zombie

答えて

1

(明確にするために省略printf()文)このような何かを試してみてください:

int send_buf(in fd, void *buf, int numbytes) 
{ 
    char *start = (char*) buf; 
    while (numbytes > 0) 
    { 
     int sent = send(fd, start, numbytes, 0); 
     if (sent <= 0) 
     { 
      if ((sent == -1) && (errno == EAGAIN)) 
      { 
       fd_set wfds; 
       FD_ZERO(&wfds); 
       FD_SET(fd, &wfds); 
       if (select(fd + 1, NULL, &wfds, NULL, NULL) == 1) 
        continue; 
      } 
      return -1; 
     } 

     start += sent; 
     numbytes -= sent; 
    } 

    return 0; 
} 

int return_result(int fd, char *content_type, void *buf, int numbytes) 
{ 
    char out_buf[BUF_SIZE], 

    int len = sprintf(out_buf, 
     "HTTP/1.1 200 OK\r\n" 
     "Content-Type: %s\r\n" 
     "Content-Length: %d\r\n" 
     "Connection: Close\r\n" 
     "\r\n", 
     content_type, 
     numb); 

    if (send_buf(fd, out_buf, len) != 0) 
     return -1; 

    if (send_buf(fd, buf, numbytes) != 0) 
     return -1; 

    return 0; 
} 
+0

パーフェクト!どうもありがとうございました!私のソリューションの問題は何ですか? – Zombie

+0

あなたがフォーマットされたHTTPヘッダであろうと入力データバッファのチャンクであろうと、特定のデータブロックを送信していたときは、ブロックごとに 'send()'を呼び出すだけでした。ブロック全体を一度に送信することは保証されていません。実際、多くの場合そうではないでしょう。 'send()'は実際に受け入れられたバイト数を返します。 enireブロックがsenになるまで、 'send()'を呼び出す必要があります。私の例では、ノンブロッキングモードで動作しているソケット(あなたが使っていることを言っていない)に対して 'select()'を余分に呼び出すことで、まったく同じことができます。 –

+0

あなたのコードのもう一つのバグはこの行です: 'temp = start + sizeof(char)* 2048;'。 'temp = start +(buf_size * sizeof(char));'これは次のように簡略化することができます: 'temp = start + buf_size;'ポインタ演算を使用します。 : 'start + = buf_size;'私のコードで使っているように。 –

2

私はAdvanced Programming in the Unix Environment, 2nd editionから次writen()機能を使用してコードを置き換える提案したいと思います:

ssize_t    /* Write "n" bytes to a descriptor */ 
writen(int fd, const void *ptr, size_t n) 
{ 
     size_t   nleft; 
     ssize_t   nwritten; 

     nleft = n; 
     while (nleft > 0) { 
       if ((nwritten = write(fd, ptr, nleft)) < 0) { 
         if (nleft == n) 
           return(-1); /* error, return -1 */ 
         else 
           break;  /* error, return amount written so far */ 
       } else if (nwritten == 0) { 
         break; 
       } 
       nleft -= nwritten; 
       ptr += nwritten; 
     } 
     return(n - nleft);  /* return >= 0 */ 
} 

このコードは、すでにデバッグと知られている作業をし、さらににwrite(2)を許可されます物事がうまくいけば、より速いスピードで行くためにPIPE_BUFバイトを書いてください。

send(2)shouldあなたがリクエストしたすべてのデータを送信できない場合は、ブロックします。もっと面白いと思うのは、ブロックを分割するための周囲の努力なしに、プレーンなsend(2)のバージョンをデバッグすることだと思います。 write(2)send(2)両方より

ベターsendfile(2)だろう - ファイルを開く、sendfile(2)に記述し、ソケットを通過し、そして可能ならばゼロコピーメカニズムを使用して、カーネルがあなたのためにすべてを処理させます。

最後の1点:HTTP uses CRLF、普通のキャリッジリターンではありません。それぞれ\n\r\nに置き換えてください。

+0

あなたの入力に感謝します!私はあなたのコードを追加しましたが、wgetはまだ5775バイトに固執します!私は別のマシンにssh'ed、と私はどこに同じ問題を得ることが判明!何かご意見は? – Zombie

関連する問題