C语言中的多会话聊天服务器

C语言中的多会话聊天服务器,c,network-programming,C,Network Programming,我正在尝试用C语言编写一个多会话聊天服务器。我从一个终端托管服务器,并从其他终端向其发送telnet。 在VMWare player上使用ubuntu 13.04 发生的情况是: 我将循环从3递增到fdmax,以使用sd(侦听器)接受新的连接,newsd表示新的套接字描述符 当我在一个窗口中打印“hi”时,它会在所有窗口中打印,包括我键入的窗口。此外,许多随机垃圾不断出现。 我只希望我键入的内容出现(如何清除垃圾>),并且出现在除我键入的窗口之外的所有窗口中 #include<sys/so

我正在尝试用C语言编写一个多会话聊天服务器。我从一个终端托管服务器,并从其他终端向其发送telnet。 在VMWare player上使用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;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
void*get\u in\u addr(结构sockaddr*sa)
{
如果(sa->sa_族==AF_INET)
{
return&(((struct sockaddr_in*)sa)->sin_addr);
}
返回((结构sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc,字符**argv)
{
//ptr用于遍历,serv用于struct addinfo的链表,getaddrinfo函数的提示
结构addrinfo*ptr,提示,*serv;
int max_cli,dat,x,i;
结构sockaddr\u存储cli\u addr;
袜子尺寸;
char cli_ip[INET_ADDRSTRLEN];
char inc[256];//一个命令行包含80个字符
memset(inc,0,strlen(inc));
国际sd,新闻D;
fd_集主控器;
fd_设置读取_fds;
字符值[256];
FD_零和主设备;
FD_零(&read_fds);
//argv[1]-服务器ip argv[2]-服务器端口argv[3]-最大客户端数
int-fdmax;
int opt=1;
/*如果(argc!=4)
{
printf(“请重新输入数据。数据不足\n”);
出口(1);
}
if(atoi(argv[2])ai_next)
{
无效*地址;
如果(ptr->ai_系列==AF_INET)
{
*ipv4中的struct sockaddr_=(struct sockaddr_*)ptr->ai_addr;
地址=&(ipv4->sin_addr);
}
inet_ntop(ptr->ai_系列、地址、值、大小值);
//printf(“%s\n”,值);
//与其中一个IP地址建立连接
sd=插座(ptr->ai_系列,ptr->ai_插座类型,ptr->ai_协议);
如果(sd<0)
继续;
setsockopt(sd、SOL_插槽、SO_REUSEADDR和opt、opt大小);
如果(绑定(sd,ptr->ai_addr,ptr->ai_addrlen)<0)
{
关闭(sd);
继续;
}
break;//表示找到并绑定了一个工作套接字
}//结束
如果(ptr==NULL)
{
fprintf(stderr,“绑定失败\n”);
出口(2);
}
freeaddrinfo(serv);
如果(听(sd,15)=-1)
{
printf(“侦听时出错\n”);
出口(3);
}
/*套接字已找到、绑定并正在侦听活动连接*/
FD_集(sd和master);
fdmax=sd;//最新的活动套接字描述符
而(1)
{
read_fds=master;//复制主列表,使原始列表不会损坏
如果(选择(fdmax+1,&read_-fds,NULL,NULL,NULL)=-1)
{
perror(“选择失败。\n”);
出口(4);
}
对于(i=3;i为假,现有一个->真
{
//printf(“开始读取描述符!\n”);
如果(i==sd)//主连接存在,则接受新的文件描述符
{//printf(“读取第一个连接!\n”);
地址大小=cli地址的大小;
newsd=accept(sd,(struct sockaddr*)和cli\u addr,以及addr\u size);
printf(“已接受的新连接套接字%d\n”,newsd);
FD_集(新闻和主文件);
如果(newsd==-1)
{
佩罗(“接受”);
}
如果(新闻>fdmax)
{
fdmax=newsd;
}
printf(“%d%d\n”,newsd,fdmax);
继续;
}
否则,如果(i!=sd)//现有连接,则接受数据
{

如果(dat=recv(i,&inc,sizeof inc,0)首先,在发送接收到的数据时,使用
sizeof
运算符,它给出了数组的总大小。发送的字节数应该与接收到的字节数相同,通常会更少。使用
recv
的返回值可以知道实际接收到的字节数。一般来说,C数组没有动态大小,您必须保留你自己追踪一下


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

作为开始,您的发送必须使用dat作为长度,而不是sizeof(inc)

没有看到来源-不太可能。@viraptor我很抱歉:|刚刚打印出来it@goldilocks,谢谢!疏忽。你有什么问题,你想让消息显示在所有的连接中,还是只显示在不发送消息的连接中?什么是“随机垃圾”?我不能向上投票,但这对我来说很好。谢谢。顺便说一句,你不喜欢我吗“\0”?修正了斜杠。是的,升级需要一点声誉,不用担心。