0

私は UNIX ネットワーク プログラミングを学ぼうとしていたので、クライアントがメッセージを送信し、サーバーが大文字に変換されたメッセージを返すクライアント/サーバー プログラムを作成しようとしました。

サーバーを実行し、クライアントを使用して自分のマシンに接続し、いくつかのテスト文字列を送信すると、以前に入力したものが画面に書き込まれるまで問題なく動作します。

バッファと関係があると思われます。サーバーを起動した後に実行されるサンプルは次のとおりです。

can@ubuntu:~$ cd デスクトップ

can@ubuntu:~/Desktop$ ./tcpuppcli 127.0.0.1

エコーする文字列を入力してください: test

エコー応答: テスト

エコーする文字列を入力してください: string2

エコー応答: STRING2

エコーする文字列を入力してください: string3

エコー応答: STRING3

エコーする文字列を入力してください: aaaaaaaaafsfagd

エコー応答: AAAAAAAAAFSFAGD

エコーする文字列を入力してください: gdsgsg

エコー応答: GDSGSG

AAFSFAGD ----------->!これは、前の入力からの文字を含む奇妙な行です!

エコーする文字列を入力してください: ^C

can@ubuntu:~/Desktop$

サーバーとクライアントのコードは次のとおりです。

// methods with prefix w_ are just error checking wrappers from UNP by Stevens    
// server
#include "socketwrap.h" // headers necessary and constants like MAXLINE
#include <ctype.h>

void sigchld_handler( int sig);
void str_upper( int connfd);
void toUpperCase( char buffer[], int length);

int main( int argc, char** argv)
{
  int listenfd;
  int connfd;
  pid_t childpid;
  struct sockaddr_in serverAddress;
  struct sockaddr_in clientAddress;
  socklen_t length;
  struct sigaction sa;

  // Create the socket
  listenfd = w_socket( AF_INET, SOCK_STREAM, 0);

  // Clear the serverAddress structure
  bzero( &serverAddress, sizeof( serverAddress));
  // Set up the serverAddress structure
  serverAddress.sin_family = AF_INET;
  serverAddress.sin_addr.s_addr = htonl( INADDR_ANY);
  serverAddress.sin_port = htons( 11979);

  // Bind the socket to a well-defined port
  w_bind( listenfd, (struct sockaddr*) &serverAddress, sizeof( serverAddress));

  // Start listening for connections
  w_listen( listenfd, BACKLOG);

  // Handle any zombie children by using a signal handler
  sa.sa_handler = sigchld_handler;
  sigemptyset( &sa.sa_mask);
  sa.sa_flags = SA_RESTART;
  if( sigaction( SIGCHLD, &sa, NULL) == -1)
  {
    perror( "signal error");
    exit( 1);
  }

  printf( "Waiting for connections...\n");

  while( 1)
  {
      length = sizeof( clientAddress);
      connfd = w_accept( listenfd, ( struct sockaddr*) &clientAddress, &length);
      if( connfd < 0)
      {
          if( errno == EINTR)
          {
            continue; // back to while
          }
          else
          {
            perror( "accept error");
            exit( 1);
          }
      }

      printf( "Obtained connection...\n");
      childpid = fork();
      if ( childpid == 0) /* child process */
      {
        w_close( listenfd); /* close listening socket */
        str_upper( connfd); // process the request
        exit( 0);
      }

    w_close( connfd); /* parent closes connected socket */
  }

}

void sigchld_handler( int sig)
{
  while( waitpid( -1, NULL, WNOHANG) > 0);
}

void str_upper( int connfd)
{
  char buffer[MAXLINE];
  while( read( connfd, buffer, MAXLINE - 1) > 0)
  {
    toUpperCase( buffer, strlen( buffer));
    write( connfd, buffer, strlen( buffer));
  }
}

void toUpperCase( char buffer[], int length)
{
  int i;
  for( i = 0; i < length - 1; i++)
  {
      buffer[i] = toupper( buffer[i]);
  }
}

// client
#include "socketwrap.h"

void str_cli( int connfd);

int main( int argc, char** argv)
{
  int sockfd;
  struct sockaddr_in serverAddress;

  if( argc != 2)
  {
      printf( "Invalid argument count\n");
      printf( "Correct usage: tcpcli4 <IPaddress>\n");
      exit( 1);
  }


  sockfd = w_socket( AF_INET, SOCK_STREAM, 0);
  bzero( &serverAddress, sizeof( serverAddress));
  serverAddress.sin_family = AF_INET;
  serverAddress.sin_port = htons( 11979);
  if( inet_pton( AF_INET, argv[1], &serverAddress.sin_addr) <= 0)
  {
      perror( "inet_pton error");
      exit( 1);
  }

  w_connect( sockfd, ( struct sockaddr*) &serverAddress, sizeof( serverAddress));

  str_cli( sockfd);
  exit( 0);
}

void str_cli( int connfd)
{
  char buffer[MAXLINE];

  printf( "Enter the string to echo: ");
  while( fgets( buffer, MAXLINE, stdin) > 0)
  {

    // Send string to echo server, and retrieve response
    write( connfd, buffer, strlen( buffer));
    read( connfd, buffer, MAXLINE - 1);

    // Output echoed string
    printf( "Echo response: %s\n", buffer);
    printf( "Enter the string to echo: ");
  }
}

追加情報が必要な場合、または不明な点がある場合はお知らせください

すべての返信に感謝します

4

2 に答える 2

2

read()'\0'文字列にターミネータを追加しないため、strlen()呼び出しの結果は明確に定義されていません。文字列の長さを知るには、の戻り値を使用する必要がありますread()(変数に格納し、テストしてから破棄するだけではありません)。> 0

しばらくの間は正常に機能しているように見えましたが、'\0'初期化されていないときにバッファにあったsを使用すると、運が悪くなります。読み取りループの前に試してみるとmemset(buffer, 'X', sizeof buffer)、以前の失敗が発生します。

于 2012-08-26T23:11:30.440 に答える
0

\0Alan Curry が指摘した問題と同様に、 TCP はメッセージ指向のトランスポートではなく、ストリーム指向であることを忘れないでください。

一方の端でのシングルがもう一方の端でのシングルになるという保証はまったくありません. 実際、状況によっては、それが起こらないことが保証されています。writeread

データの次のチャンクがどれくらいの長さになるかを示すために、何らかの形式の構造または境界をパケットに入れる必要があります。この場合の適切なものは、文字列の長さを含む 32 ビットの整数で、4 バイトとしてパックされます。ネットワークプロトコルは通常、このようなものを「ビッグエンディアン」形式(別名「ネットワークオーダー」)でhtonl()送信するため、送信時と受信時に値を変換するために使用しますntohl()

writev()送信側では、長さの値とデータ自体の書き込みを 1 つのシステム コールに結合するために使用できます。受信側では、 を使用read()して長さを取得し、1 秒read()でその量のデータを読み取ります。

また、 any read()またはwrite()call が実際に指定されたよりも少ないデータを送信できることを忘れないでください。このような各操作は、適切なバイト数が処理されるまで関数を繰り返し呼び出すループでラップする必要があります。

于 2012-08-26T23:20:26.430 に答える