客户端不会在新形成的连接上收到消息

时间:2016-09-29 12:56:14

标签: c sockets client-server

所以我有很简单的多服务器 - 客户端应用程序(Linux)。我想为初学者做的是连接到服务器从它接收消息并将消息写入服务器然后是回声。

问题在于连接而不是来自服务器的问候消息,客户端已准备好在终端上输入消息 - 没有问候消息。在输入我想要的任何消息后,我会收到回复的问候消息!我在这做错了什么?代码如下。

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <pthread.h>

#include <netdb.h>
#include <netinet/in.h>

int server(int client_socket);
void* handle_connection(void* inc);

int main(int argc, char* argv[])
{
  int socket_fd;
  int client_socket_fd;
  int port_number;
  int client_length;
  int * new_sock;
  struct sockaddr_in serv_addr;
  struct sockaddr_in cli_addr;
  char buffer[256];

  socket_fd = socket(AF_INET, SOCK_STREAM, 0);

  if(socket_fd < 0) {
      perror("Error opening socket!");
      exit(1);
  }

  /* Initialize socket structure */
  port_number = 1234;
  bzero((char *) &serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr= INADDR_ANY;
  serv_addr.sin_port = htons(port_number);

  /* Binding */
  if(bind(socket_fd, (struct sockaddr *) &serv_addr,  sizeof(serv_addr)) < 0) {
       perror("Error on binding!");
       exit(1);
  }

  listen(socket_fd, 10);
  client_length = sizeof(cli_addr);

  while(client_socket_fd = accept(socket_fd, (struct sockaddr*) &cli_addr, &client_length)) {
     pthread_t thread_id;
     new_sock = malloc(sizeof(client_socket_fd));
     *new_sock = client_socket_fd;

     if(pthread_create(&thread_id, NULL, handle_connection, (void*) new_sock)) {
        perror("could not create thread");
        exit(1);
     }
     pthread_join(thread_id, NULL);
 }

 if(client_socket_fd < 0) {
   perror("Error on accept client");
   exit(1);
 }

  return 0;
}

void* handle_connection(void* inc)
{
  int socket_fd = *(int *) inc;
  int message_size;
  char *message;
  char buffer[256];

  message = "You have been connected\n";
  write(socket_fd, message, strlen(message));
  message = "I will repeat what you type\n";
  write(socket_fd, message, strlen(message));

  while((message_size = recv(socket_fd, buffer, sizeof(buffer)/sizeof(buffer[0]), 0)) > 0) {
    write(socket_fd, buffer, strlen(buffer));
  }

  if(message_size == 0) {
    puts("Client disconnected");
    fflush(stdout);
  }
  else if(message_size == -1) {
    perror("Reading back from client failed");
  }

  free(inc);

  return 0;

}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>

int main(int argc, char* argv[])
{
  int socket_fd, port_number;
  struct sockaddr_in serv_addr;
  struct hostent *server;
  char buffer[256];

  if(argc<2) {
    fprintf(stderr,"Incorrect arguments input\n");
    exit(0);
  }

  //port_number = atoi(argv[2]);
  port_number = 1234;

  server = gethostbyname(argv[1]);

  socket_fd = socket(AF_INET, SOCK_STREAM, 0);
  if(socket_fd < 0) {
    perror("Error opening socket");
    exit(1);
  }

  bzero((char*) &serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(port_number);
  bcopy((char*) server->h_addr,(char *) &serv_addr.sin_addr.s_addr,sizeof(server->h_length));

  if(connect(socket_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
      perror("Error connecting");
      exit(1);
  }
  while(1) {
    bzero(buffer, sizeof(buffer)/sizeof(buffer[0]));
    printf("Enter the message: ");
    bzero(buffer, sizeof(buffer)/sizeof(buffer[0]));
    fgets(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, stdin);

    if(send(socket_fd, buffer, strlen(buffer), 0) < 0) {
      perror("Error sending message");
      exit(1);
    }

    bzero(buffer, sizeof(buffer)/sizeof(buffer[0]));
    if(recv(socket_fd, buffer, sizeof(buffer), 0) < 0) {
      perror("Error reading back from server");
      exit(1);
    }

    printf("Server reply: ");
    printf("%s",buffer);
  }
  close(socket_fd);
  return 0;
}

以下是该程序的不需要的结果: unwatned result

服务器回复应该在示例中:hej。

1 个答案:

答案 0 :(得分:2)

  

问题在于连接而不是来自服务器的问候消息,客户端已准备好在终端上输入消息 - 没有问候消息。

这是因为您在使用recv()获取问候语并向用户显示问候语之前,您正在阅读用户输入。

  

在输入我想要的任何消息后,我会收到回复的问候消息!

这是因为您在调用后获取/显示问候语 fgets()

代码中存在许多错误。我会指出最大的那些。

  1. 您在pthread_join()之后立即致电pthread_create()。那&#39; S 阻止新连接(即进一步accept() s)。应该这样做 循环之外。在这种情况下,您需要一些存储空间来存储线程ID。对于 例如:
  2. const int thread_pool_step = 10;
    int thread_pool_size = thread_pool_step;
    pthread_t *thread_ids = malloc(thread_pool_size * sizeof(pthread_t));
    memset(thread_ids, 0, thread_pool_size * sizeof(pthread_t));
    int thread_index = -1;
    
    while (!do_shutdown)
      {
        /* ... */
        if (++thread_index >= thread_pool_size)
          {
            thread_pool_size += thread_pool_step;
            thread_ids = realloc(thread_ids, thread_pool_size * sizeof(pthread_t));
            if (thread_ids == NULL)
              {
                fprintf(stderr, "failed to realloc thread pool: %s\n",
                  strerror(errno));
                abort();
              }
          }
    
        pthread_create(&thread_ids[thread_index++], NULL,
                       handle_connection, (void*) new_sock);
      }
    
    while (--thread_index >= 0)
      {
        if (thread_ids[thread_index])
          pthread_join(thread_ids[thread_index], NULL);
      }
    
    if (thread_ids != NULL)
      free(thread_ids);
    
    1. 如问题评论中所述,您需要某种形式 协议。例如,您可能会在服务器发送之前阅读问候语 从&#34; READY&#34;开始的行串。客户端可能记住服务器状态,然后继续聊天逻辑:
    2. enum {
        INIT_STATE,
        READY_STATE
      } serv_state = INIT_STATE;
      
      #define check_serv_state(str, st) \
        (strncmp(str, #st, sizeof(#st) - 1) == 0)
      
      for (;;)
      {
        bzero(buffer, sizeof(buffer)/sizeof(buffer[0]));
        rc = recv(socket_fd, buffer, sizeof(buffer), 0);
        /* ... */
      
        if (serv_state == INIT_STATE)
          {
            char *part = strtok(buffer, "\n");
            do
              {
                if (check_serv_state(part, READY))
                  {
                    printf("The server is ready for the chat\n");
                    serv_state = READY_STATE;
                  }
                else
                  printf("Server: [[[%s]]]\n", part);
              }
            while ((part = strtok(NULL, "\n")));
      
            if (serv_state != READY_STATE)
              continue;
          }
        else
          printf("Server reply: %s", buffer);
      
        /* ... */
        printf("Enter the message: ");
        /* ... */
      }
      
      1. 我还建议实现一些信号处理以实现正常关机。至少,对于服务器:
      2. volatile sig_atomic_t do_shutdown = 0; /* global */
        
        static void
        sigterm_handler(int sig)
        {
          printf("Caught signal %d. Leaving...\n", sig);
          do_shutdown = 1;
        }
        
        /* ... */
        
        struct sigaction a;
        a.sa_handler = sigterm_handler;
        a.sa_flags = 0;
        sigemptyset(&a.sa_mask);
        sigaction(SIGINT,  &a, NULL);
        sigaction(SIGQUIT, &a, NULL);
        sigaction(SIGTERM, &a, NULL);
        
        /* Check do_shutdown in the loops */
        while (!do_shutdown) { /* ...recv(), accept(), etc. */ }
        

        注意,从异步上下文(信号处理程序,线程处理程序[特别是])访问全局变量通常不安全而不使用mutual exclusion

        以下是已应用修补程序的完整代码:

        server.c

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <sys/socket.h>
        #include <sys/un.h>
        #include <unistd.h>
        #include <pthread.h>
        #include <errno.h>
        #include <signal.h>
        #include <netdb.h>
        #include <netinet/in.h>
        
        volatile sig_atomic_t do_shutdown = 0;
        
        static void
        sigterm_handler(int sig)
        {
          printf("Caught signal %d. Leaving...\n", sig);
          do_shutdown = 1;
        }
        
        
        static void
        send_message(int socket_fd, const char *message)
        {
          int rc;
          int message_size = strlen(message);
        
          do
            {
              rc = send(socket_fd, message, message_size, 0);
              if (rc == -1)
                {
                  fprintf(stderr, "send() failed: %s\n", strerror(errno));
                  abort();
                }
            }
          while (rc < message_size && !do_shutdown);
        }
        
        
        static void *
        handle_connection(void *inc)
        {
          int socket_fd = *(int *)inc;
          int message_size;
          char *message;
          char buffer[256];
        
          message = "You have been connected\n";
          send_message(socket_fd, message);
        
          message = "I will repeat what you type\n";
          send_message(socket_fd, message);
        
          message = "READY\n";
          send_message(socket_fd, message);
        
          while (!do_shutdown)
            {
              memset(buffer, 0, sizeof(buffer));
              message_size = recv(socket_fd, buffer, sizeof(buffer) / sizeof(buffer[0]), 0);
              if (message_size > 0)
                send_message(socket_fd, buffer);
              else if (message_size == -1)
                {
                  fprintf(stderr, "recv() failed: %s\n", strerror(errno));
                  break;
                }
              else if (message_size == 0)
                {
                  puts("Client disconnected");
                  fflush(stdout);
                  break;
                }
            }
        
          free(inc);
        
          return 0;
        }
        
        
        int
        main(int argc, char* argv[])
        {
          int socket_fd;
          int client_socket_fd;
          int port_number;
          socklen_t client_length;
          int *new_sock;
          struct sockaddr_in serv_addr;
          struct sockaddr_in cli_addr;
        
          socket_fd = socket(AF_INET, SOCK_STREAM, 0);
          if (socket_fd < 0)
            {
              perror("Error opening socket!");
              exit(1);
            }
        
          /* Initialize socket structure */
          port_number = 12345;
          bzero((char *)&serv_addr, sizeof(serv_addr));
          serv_addr.sin_family = AF_INET;
          serv_addr.sin_addr.s_addr= INADDR_ANY;
          serv_addr.sin_port = htons(port_number);
        
          /* Binding */
          if (bind(socket_fd, (struct sockaddr *) &serv_addr,  sizeof(serv_addr)) < 0)
            {
              perror("Error on binding!");
              exit(1);
            }
        
          const int thread_pool_step = 10;
          listen(socket_fd, thread_pool_step);
          client_length = sizeof(cli_addr);
        
          int thread_pool_size = thread_pool_step;
          pthread_t *thread_ids = malloc(thread_pool_size * sizeof(pthread_t));
          if (thread_ids == NULL)
            {
              perror("Failed to allocate memory for thread ids");
              abort();
            }
          memset(thread_ids, 0, thread_pool_size * sizeof(pthread_t));
          int thread_index = -1;
        
          struct sigaction a;
          a.sa_handler = sigterm_handler;
          a.sa_flags = 0;
          sigemptyset(&a.sa_mask);
          sigaction(SIGINT,  &a, NULL);
          sigaction(SIGQUIT, &a, NULL);
          sigaction(SIGTERM, &a, NULL);
        
          while (!do_shutdown)
            {
              client_socket_fd = accept(socket_fd, (struct sockaddr *)(&cli_addr), &client_length);
              if (client_socket_fd == -1)
                {
                  fprintf(stderr, "accept() failed: %s\n", strerror(errno));
                  break;
                }
        
              new_sock = malloc(sizeof(client_socket_fd));
              *new_sock = client_socket_fd;
        
              if (++thread_index >= thread_pool_size)
                {
                  thread_pool_size += thread_pool_step;
                  thread_ids = realloc(thread_ids, thread_pool_size * sizeof(pthread_t));
                  if (thread_ids == NULL)
                    {
                      fprintf(stderr, "failed to realloc thread pool: %s\n", strerror(errno));
                      abort();
                    }
                }
        
              if (pthread_create(&thread_ids[thread_index++], NULL,
                                 handle_connection, (void*) new_sock))
                {
                  fprintf(stderr, "pthread_create() failed: %s\n", strerror(errno));
                  abort();
                }
            }
        
          puts("Waiting for threads to finish");
          while (--thread_index >= 0)
            {
              if (thread_ids[thread_index])
                pthread_join(thread_ids[thread_index], NULL);
            }
        
          if (thread_ids != NULL)
            free(thread_ids);
        
          return 0;
        }
        

        client.c

        #include <stdio.h>
        #include <stdlib.h>
        #include <netdb.h>
        #include <netinet/in.h>
        #include <string.h>
        #include <pthread.h>
        #include <unistd.h>
        
        int main(int argc, char* argv[])
        {
          int socket_fd, port_number;
          struct sockaddr_in serv_addr;
          struct hostent *server;
          char buffer[256];
          int rc;
        
          if (argc<2)
            {
              fprintf(stderr,"Incorrect arguments input\n");
              exit(0);
            }
        
          port_number = 12345;
        
          server = gethostbyname(argv[1]);
        
          socket_fd = socket(AF_INET, SOCK_STREAM, 0);
          if (socket_fd < 0)
            {
              perror("Error opening socket");
              exit(1);
            }
        
          bzero((char*) &serv_addr, sizeof(serv_addr));
          serv_addr.sin_family = AF_INET;
          serv_addr.sin_port = htons(port_number);
          bcopy((char*) server->h_addr,(char *)
                &serv_addr.sin_addr.s_addr,sizeof(server->h_length));
        
          if (connect(socket_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
            {
              perror("Error connecting");
              exit(1);
            }
        
          enum
            {
              INIT_STATE,
              READY_STATE
            } serv_state = INIT_STATE;
        
        #define check_serv_state(str, st) \
          (strncmp(str, #st, sizeof(#st) - 1) == 0)
        
          for (;;)
            {
              bzero(buffer, sizeof(buffer)/sizeof(buffer[0]));
              rc = recv(socket_fd, buffer, sizeof(buffer), 0);
              if (rc < 0)
                {
                  perror("Error reading back from server");
                  exit(1);
                }
              else if (rc == 0)
                {
                  printf("The server has been disconnected. Quitting.\n");
                  exit(0);
                }
        
              if (serv_state == INIT_STATE)
                {
                  char *part = strtok(buffer, "\n");
                  do
                    {
                      if (check_serv_state(part, READY))
                        {
                          printf("The server is ready for the chat\n");
                          serv_state = READY_STATE;
                        }
                      else
                        printf("Server: [[[%s]]]\n", part);
                    }
                  while ((part = strtok(NULL, "\n")));
        
                  if (serv_state != READY_STATE)
                    continue;
                }
              else
                printf("Server reply: %s", buffer);
        
              bzero(buffer, sizeof(buffer)/sizeof(buffer[0]));
              printf("Enter the message: ");
              bzero(buffer, sizeof(buffer)/sizeof(buffer[0]));
              fgets(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, stdin);
        
              if (send(socket_fd, buffer, strlen(buffer), 0) < 0)
                {
                  perror("Error sending message");
                  exit(1);
                }
            }
          close(socket_fd);
          return 0;
        }