C中的多会话聊天服务器

时间:2013-09-16 17:31:01

标签: c network-programming

我正在尝试用C编写多会话聊天服务器。我从一个终端托管服务器,并从其他终端telnet到它。 在VMWare播放器上使用ubuntu 13.04。

这是怎么回事:
我将循环从3开始递增到fdmax,使用sd(监听器)接受新连接,newsd表示新的套接字描述符。

当我在一个窗口中打印'hi'时,它会在所有窗口中打印,包括我输入的窗口。此外,还会出现大量随机垃圾。 我只想要我输入的内容(我如何摆脱垃圾>),以及所有窗口,除了我输入的那个!

#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/select.h>

void *get_in_addr(struct sockaddr *sa)
{
  if (sa->sa_family == AF_INET)
  {
    return &(((struct sockaddr_in*) sa)->sin_addr);
  }

  return &(((struct sockaddr_in6*) sa)->sin6_addr);
}

int main(int argc, char **argv)
{
  //ptr used for traversal, serv used for the linked list of struct addinfos , hints for      the getaddrinfo function
  struct addrinfo *ptr, hints, *serv;
  int max_cli, dat, x, i;
  struct sockaddr_storage cli_addr;
  socklen_t addr_size;
  char cli_ip[INET_ADDRSTRLEN];
  char inc[256]; //one command line is 80 characters
  memset(inc, 0, strlen(inc));
  int sd, newsd;
  fd_set master;
  fd_set read_fds;
  char value[256];

  FD_ZERO(&master);
  FD_ZERO(&read_fds);

  //argv[1]-server ip argv[2]-server port argv[3]-maximum client number

  int fdmax;
  int opt = 1;

  /*if(argc!=4)
   {
   printf("Please re-enter data. Data insufficient\n");
   exit(1);
   }
   if(atoi(argv[2])<1025)
   {
   printf("Reserved port. Please try again\n");
   exit(1);
   }*/
  max_cli = atoi(argv[3]);

  memset(&hints, 0, sizeof hints);
  hints.ai_family = AF_INET;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;

  /* Verify the inputs and generate linked list of possible IPs to use*/

  if (sd = getaddrinfo(argv[1], argv[2], &hints, &serv))
  {
    fprintf(stderr, "Error calling getaddrinfo %s\n", gai_strerror(sd));
    exit(1);
  }

  for (ptr = serv; ptr != NULL ; ptr = ptr->ai_next)
  {

    void *addr;
    if (ptr->ai_family == AF_INET)
    {
      struct sockaddr_in *ipv4 = (struct sockaddr_in *) ptr->ai_addr;
      addr = &(ipv4->sin_addr);
    }
    inet_ntop(ptr->ai_family, addr, value, sizeof value);
    //printf("%s\n",value);     

    //Form connection with one of the IP addresses   
    sd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
    if (sd < 0)
      continue;

    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);

    if (bind(sd, ptr->ai_addr, ptr->ai_addrlen) < 0)
    {
      close(sd);
      continue;
    }

    break; //Indicates one working socket found and bound
  } //end for

  if (ptr == NULL )
  {
    fprintf(stderr, "Bind failed\n");
    exit(2);
  }

  freeaddrinfo(serv);

  if (listen(sd, 15) == -1)
  {
    printf("Error occurred while listening\n");
    exit(3);
  }

  /* Socket found, bound and now listening for active connections*/

  FD_SET(sd, &master);

  fdmax = sd; //Latest active socket descriptor

  while (1)
  {

    read_fds = master; //Copy the master list so that the original list doesn't get damaged

    if (select(fdmax + 1, &read_fds, NULL, NULL, NULL ) == -1)
    {
      perror("Select failed.\n");
      exit(4);
    }

    for (i = 3; i <= fdmax; i++)
    {
      //printf("i");
      //printf("entered for loop\n");
      if (FD_ISSET(i,&read_fds))    //new connection->false, existing one->true
      {
        //  printf("Started reading descriptors!\n");

        if (i == sd)    //primary connection,exists, accept new file descriptor
        { //printf("Read first connection!\n");
          addr_size = sizeof cli_addr;
          newsd = accept(sd, (struct sockaddr *) &cli_addr, &addr_size);
          printf("Accepted new connection socket %d\n", newsd);
          FD_SET(newsd, &master);
          if (newsd == -1)
          {
            perror("accept");
          }
          if (newsd > fdmax)
          {
            fdmax = newsd;
          }
          printf("%d %d\n", newsd, fdmax);
          continue;
        }
        else if (i != sd) //existing connection, so accept data
        {
          if (dat = recv(i, &inc, sizeof inc, 0) <= 0)
          {
            if (dat == 0)
            {
              printf(" Socket %d has quit the   chatroom", i);
            }
            if (dat < 0)
            {
              perror("Error on Receive");
            }
            //  char *s=&inc;
            //printf("%d\n %s",dat);
            close(i);
            FD_CLR(i, &master);
          }

          //Nothing wrong with the input from client i.  Broadcast!
          else
          {
            for (x = 3; x <= fdmax; x++)
            {
              if (FD_ISSET(x,&master))
              {
                if (x != sd)
                {
                  //send(x,&inc,sizeof inc,0);
                  if (send(x, &inc, sizeof inc, 0) < 0)
                  {
                    perror("Send");
                  }
                }
              }
            }
          }
        }

      }

      /*else// new connection
       { break;

       printf("SERVERBOT: new connection from %s on socket %d\n",inet_ntop(cli_addr.ss_family,get_in_addr((struct sockaddr*)&cli_addr),cli_ip, INET6_ADDRSTRLEN),newsd);
       }////change this to 'username' has joined the room*/

    }
  }

  return 0;
}

2 个答案:

答案 0 :(得分:1)

首先,当您发送收到的数据时,使用sizeof运算符,它会为您提供数组的总大小。您应该发送与收到的字节数一样多的字节,这通常会更少。使用recv的返回值来了解实际接收的字节数。一般来说,C数组没有动态大小,你必须自己跟踪它。

然后关于垃圾,你可能打印缓冲区内容而不终止'\0'字符。因此,在打印或使用其他字符串函数之前添加(确保缓冲区中有1个字节的额外空间!),或者使用接受最大字符串大小的打印函数,以防终止nul。

答案 1 :(得分:0)

首先你的发送必须使用dat作为长度而不是sizeof(inc)