tvishwa107
tvishwa107

Reputation: 311

Multi session chat server in C

I am trying to write a multi session chat server in C. I host the server from one terminal and telnet to it from other terminals. Using ubuntu 13.04 on VMWare player.

What happens is this:
I'm incrementing a loop from 3 onward to fdmax, to accept new connections using sd ( the listener) and newsd represents the new socket descriptor.

When I print 'hi' in one window, it prints in ALL windows including the one I typed in. Plus, a lot of random junk keeps appearing. I want only what I type to appear(how do i get rid of the junk>), and in all the windows except the one I typed it in!

#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;
}

Upvotes: 0

Views: 1524

Answers (2)

hyde
hyde

Reputation: 62797

First of all, when you send received data, you use sizeof operator, which gives you total size of array. You should send just as many bytes as you received, which will often be less. Use return value of recv to know how many bytes were actually received. In general, C arrays do not have dynamic size, you have to keep track of that yourself.

Then about garbage, you probably print buffer contents without terminating '\0' character. So, either add that (make sure there is the 1 byte of extra space in the buffer!) before printing or using other string functions, or use a printing function which accepts maximum size of string in case that terminating nul is missing.

Upvotes: 1

pm100
pm100

Reputation: 50180

for a start your send must use dat as length not sizeof(inc)

Upvotes: 0

Related Questions