0

我一直在尝试学习 unix 网络编程,所以我尝试编写一个客户端/服务器程序,其中客户端发送消息,服务器返回转换为大写字母的消息。

当我运行服务器并将客户端连接到我自己的机器并发送一些测试字符串时,它工作正常,直到我之前输入的内容被写入屏幕。

我怀疑它与缓冲区有关。这是我启动服务器后运行的示例:

可以@ubuntu:~$ cd 桌面

可以@ubuntu:~/Desktop$ ./tcpuppcli 127.0.0.1

输入要回显的字符串:test

回声响应​​:测试

输入要回显的字符串:string2

回声响应​​:STRING2

输入要回显的字符串:string3

回声响应​​:STRING3

输入要回显的字符串:aaaaaaaaafsfagd

回声响应​​:AAAAAAAAAFSFAGD

输入要回显的字符串:gdsgsg

回声响应​​:GDSGSG

AAFSFAGD ----------->!这是带有来自先前输入的字符的奇怪行!

输入要回显的字符串:^C

可以@ubuntu:~/桌面$

服务端和客户端的代码如下:

// 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不是面向消息的传输,它是面向的。

绝对不能保证write一端的单身会导致另一端的单身read。事实上,在某些情况下,可以保证不会发生这种情况。

您应该在数据包中加入某种形式的结构或分界线,以指示下一个数据块的长度。在这种情况下,一个好的方法是包含字符串长度的 32 位整数,打包为四个字节。网络协议通常以“大端”格式(又名“网络顺序”)发送这样的东西,因此您可以htonl()在发送和ntohl()接收时转换值。

在发送端,您可以使用writev()将长度值和数据本身写入一个系统调用。在接收端,只需使用read()获取长度,然后再read()读取那么多数据。

另外,不要忘记任何 read()orwrite()调用可以发送比实际指定的更少的数据。每个这样的操作都应该包装在一个循环中,该循环重复调用该函数,直到处理了适当数量的字节。

于 2012-08-26T23:20:26.430 回答