1

私のアプリケーションでは、単純なhttpサーバーのスレッドを作成しました。次に、アプリケーション内からhttpサーバーに接続しようとしましたが、recv呼び出しで制御がブロック/ハングしました。

しかし、linux GETコマンドを使用してアプリケーションのhttpサーバーに接続しようとすると、httpサーバーに正常に接続されます。

グーグルを検索して私の理解によると、これは正しいアプローチではないことがわかりました。

しかし、これを実行したい場合は、アプリケーション内からhttpサーバーに接続できるように、どのようにソケットを作成する必要がありますか。

以下は私のhttpサーバーソケットがどのように作成されたかです

pthread_create(&pt_server, NULL, http_srvr, NULL);

//http server handler
void *http_server()
{
    int sockfd, new_fd;             
    struct sockaddr_in my_addr;     
    struct sockaddr_in their_addr;  
    socklen_t sin_size;
    struct sigaction sa;
    int yes=1;

    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {
    perror("socket");
    exit(1);
    }

    if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1)
    {
    perror("setsockopt");
    exit(1);
    }

    my_addr.sin_family = AF_INET;               // host byte order
    my_addr.sin_port = htons(HTTP_PORT);        // short, network byte order
    my_addr.sin_addr.s_addr = INADDR_ANY;       // automatically fill with my IP
    memset(&(my_addr.sin_zero), '\0', 8);       // zero the rest of the struct

    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
    {
    perror("bind");
    exit(1);
    }

    printf("Listening to sockets\n");
    if (listen(sockfd, BACKLOG) == -1)
    {
    perror("listen");
    exit(1);
    }

    sa.sa_handler = sigchld_handler; // reap all dead processes
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1)
    {
    perror("sigaction");
    exit(1);
    }

    printf("server: waiting for connections...\n");

    while(1) {  // main accept() loop
        sin_size = sizeof(struct sockaddr_in);
        if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,&sin_size)) == -1)
        {
          perror("accept");
          continue;
        }
        printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));

        handle_connection(new_fd);
    }
}

そして、以下は私が私のhttpサーバーにhttpPOSTをしている方法です

  /* create socket */
  if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    return ERRSOCK;
  setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0);


  /* connect to server */
  if (connect(s, &server, sizeof(server)) < 0) 
    ret=ERRCONN;
  else {
    if (pfd) *pfd=s;

    /* create header */
    if (proxy) {
      sprintf(header,
"%s http://%.128s:%d/%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
          command,
          http_server1,
          http_port,
          url,
          http_user_agent,
          additional_header
          );
    } else {
      sprintf(header,
"%s /%.256s HTTP/1.0\015\012User-Agent: %s\015\012%s\015\012",
          command,
          url,
          http_user_agent,
          additional_header
          );
    }

    hlg=strlen(header);


    /* send header */   
    if (send(s,header,hlg,0)!=hlg)
      ret= ERRWRHD;

    /* send data */
    else if (length && data && (send(s,data,length,0)!=length) )
      ret= ERRWRDT;

    else {
      /* read result & check */
      ret=http_read_line(s,header,MAXBUF-1);

以下はhttp_read_lineの内容であり、この関数ではrecv呼び出しがブロックされています

static int http_read_line (fd,buffer,max) 
     int fd; /* file descriptor to read from */
     char *buffer; /* placeholder for data */
     int max; /* max number of bytes to read */
{ /* not efficient on long lines (multiple unbuffered 1 char reads) */
  int n=0;
  while (n<max) {
    if (recv(fd,buffer,1,0)!=1) {
      n= -n;
      break;
    }
    n++;
    if (*buffer=='\015') continue; /* ignore CR */
    if (*buffer=='\012') break;    /* LF is the separator */
    buffer++;
  }
  *buffer=0;
  return n;
}
4

2 に答える 2

3

HTTP 1.0ヘッダーを送信するか、HTTP1.1のcontent-lengthについて読む必要があります。サーバーが接続を閉じる義務がないときにEOSへのストリームを読み取っているので、ブロックします。Content-Lengthヘッダーは、本文に含まれるデータの量を示します。その数のバイトのみを読み取ろうとする必要があります。

HTTP 1.0を指定した場合(およびファンシーヘッダーなし)、サーバーは応答の送信後に接続を閉じます。

于 2012-09-17T22:13:34.580 に答える
0

「私のアプリケーションでは、単純なhttpサーバーのスレッドを作成しました。次に、アプリケーション内からhttpサーバーに接続しようとしましたが、recv呼び出しで制御がブロック/ハングしました。」

つまり、recvが0を返すことはありません。recv関数が0を返すのはいつですか。->TCPFINセグメントを取得したとき。サーバーがTCPFINセグメントをクライアントに送信していないようです。ここで最も可能性が高い理由は、クライアントコードを変更する必要があるためです。クライアントからデータを送信していますが、FINを送信することはないため、サーバー機能は永久に継続され、FINは送信されていないと思います。これにより、recvは永遠に待機しました。

現在のコードでは、おそらく修正は行を追加することです

else {
  /*Send the FIN segment, but we can still read the socket*/
  shutdown(s, SHUT_WR); 
  /* read result & check */
  ret=http_read_line(s,header,MAXBUF-1);

この場合、シャットダウン関数はTCP FINを送信し、サーバー関数は戻ることができ、場合によっては適切に閉じることができます。

そして、適切に閉じると、サーバーからのFINがクライアントによって受信されます。これにより、recvは永久にブロックされるのではなく、0を返します。

クライアントからのデータ転送をさらに続行する場合は、再度接続する必要があります。または、別のアルゴリズムが必要になる場合があります。

私の説明が現在の問題の解決に役立つことを願っています。

于 2012-09-17T17:30:54.750 に答える