NETWORK


/* TCP通信过程中,服务器端实现的流程
   1>socket 函数创建一个用于连接的套接字文件描述符
   2>bind 函数绑定IP和端口号
   3>listen函数将套接字设置为被动监听模式,准备接收客户请求
   4>accecpt函数;等待客户请求到来;当请求到来以后,接受连接请求,
   返回一个新的对应于此次连接的套接字。
   5>用返回的套接字和客户端进行通信,(send/recv函数)。
   6>返回:等待另一客户请求*/



//  select函数的使用


int main(int argc, const char *argv[])
{

    //1 为通信创建一个端点
    int sfd=socket(AF_INET,SOCK_STREAM,0);
    //参数1 说明使用的是IPv4网络
    //参数2 说明使用的是TCP面向连接的通讯方式
    //参数3,由于参数2已经指定通信方式,直接为0
    if(sfd ==-1)
    {
        perror("socket error");
        return -1;
    }
    printf("socket success sfd=%d\n",sfd);  // 3 (0 stdin ,stdout 1,stderr 2)

    //  设置地址能够快速重用

    int reuse =1;
    if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    // 2  绑定IP地址和端口号 (服务端)

    // 2.1  准备地址信息结构体

    struct sockaddr_in sin;
    sin.sin_family =AF_INET;           //通信域
    sin.sin_port =htons(SER_PORT);     //端口号
    sin.sin_addr.s_addr =inet_addr(SER_IP);   // ip地址
    // 2.2  绑定工作
    if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)))
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
                                                                                                                                                                                                                                                        
    //  3  将套接字设置成被动监听状态;
    //
    if(listen(sfd,128)==-1)
    {
        perror("listen error");
        return -1;
    }
    printf("listen success\n");
    //  4  阻塞等待客户端的连接
    //  4.1 定义用于接受客户端信息的容器
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);

    //  11 准备检测文件描述符的集合
    fd_set readfds,tempfds;

    // 22 清空集合中的内容
    FD_ZERO(&readfds);

    // 33 将要检测的文件描述符放入集合中
    FD_SET(sfd,&readfds);
    FD_SET(0,&readfds);

    int newfd=0;  //定义用于存放客户端套接字的变量
    while(1)
    {
        //执行阻塞函数之前现将文件描述符集合备份一份
        tempfds=readfds;
        //使用阻塞函数,检测文件描述符集合中是否有事件发生
        int res=select(sfd+1,&tempfds,NULL,NULL,NULL);
        if(res ==-1)
        {
            perror("select error");
            return -1;
        }
        else if(res ==0)
        {
            printf("time out\n");
            return -1;
        }

        //当程序执行至此,表示检测容器已经中已经有一个或者多个事件发生
        //只需要判断哪些文件描述符还在集合中,还在集合中的一定发生了事件,直接取相关逻辑

        //判断sfd是否触发了上面的操作
        if(FD_ISSET(sfd,&tempfds))
        {
            newfd =accept(sfd,(struct sockaddr*)&cin,&addrlen);
            if(newfd ==-1)
            {
                perror("accept error");
                return -1;
            }
            printf("accept success\n");
            printf("[%s %d]发送来连接请求\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
        }

        //判断0号文件描述符(stdin)是否触发了操作
        if(FD_ISSET(0,&tempfds))
        {

            char buf[128]={};   //用于读取消息内容的容器
            fgets(buf,sizeof(buf),stdin);
            buf[strlen(buf)-1]=0;    // 读取结束
            printf("键盘完成输入事件:%s\n",buf);

        }
    }
    //  6  关闭套接字

    close(sfd);
    return 0;
}
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                        
 //  4  阻塞等待客户端的连接
 //  4.1 定义用于接受客户端信息的容器
     struct sockaddr_in cin;
     socklen_t addrlen = sizeof(cin);
 
     // 11 准备检测文件描述符的集合
     fd_set readfds,tempfds;
 
     // 22 清空集合中的内容
     FD_ZERO(&readfds);
 
     // 33 将要检测的文件描述符放入集合中
     FD_SET(sfd,&readfds);
     FD_SET(0,&readfds);
 
     int newfd=0; //存放客户端套接字变量
     int maxfd=sfd; //存放容器中最大的文件描述符
 
     struct sockaddr_in cin_arr[1024];  //存放客户端地址信息结构体的容器
 
     while(1)
     {
         //在执行阻塞函数之前将文件描述符集合先备份一份
         tempfds=readfds;
 
         //使用阻塞函数,检测文件描述符集合中是否有事件产生
         int res =select(maxfd+1,&tempfds,NULL,NULL,NULL);
         if(res ==-1)
         {
             perror("select error");
             return -1;
         }
         else if(res ==0)
         {
             printf("time out\n");
             return -1;
         }
     }
     //当执行至次行代码,说明文件描述集合中已经有一个或者多个事件产生
      //此时只需要判断还有哪些文件描述符还在集合中,还在集合的描述符一定产生了事件,直接去执行
      //将所有的文件描述符全部遍历一遍
      for(int i=0;i<maxfd;i++)
      {
      //判断当前的i文件描述符是否还没有触发事件
      if(!FD_ISSET(i,&tempfds))   //文件描述符集合还没有触发事件
      {
          continue;    //退出本层循环
      }
      //若 执行至此处,则说明文件描述符已经触发事件
      //那么就要判断一下是触发了哪一个事件
 
      //判断sfd是否触发了相关事件
      if(i==sfd)
      {
          newfd=accept(i, (struct sockaddr*)&cin,&addrlen);
         if(newfd ==-1)
         {
             perror("accept error");
             return -1;
         }
     printf("accept success\n");
     printf("[%s %d]发送来连接请求\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
 
 //将newfd放入到readfds容器当中,以便检测是否有客户端发来数据
     FD_SET(newfd,&tempfds);
     //更新maxfd
     if(newfd>maxfd)
     {
         maxfd=newfd;
     }
 
     //将套接字地址信息结构体放入数组当中
     cin_arr[newfd]=cin;
      }
      else if(i==0)
      {
          //判断是否是0号文件描述符解除阻塞
          char buf[128]="";
          fgets(buf,sizeof(buf),stdin);
          buf[strlen(buf)-1]=0;
          printf("键盘完成事件输入:%s\n",buf);
 
          //循环将消息发送给所有客户端
          for(int k=4;k<maxfd;k++)
          {
              send(k,buf,strlen(buf),0);
          }
      }else
      {
     // 5 与客户端进行相互通信
 
     char rbuf[128]={};   //用于读取消息内容的容器
     while(1)
     {
         // 清空容器
         bzero(rbuf,sizeof(rbuf));
 
         //从套接字读取数据
         int res =read(newfd,rbuf,sizeof(rbuf));
         if(res == 0)
         {
             printf("客户端已经下线\n");
 
 
     //  6  关闭套接字
 
       close(i);
 
       //将当前的套接字文件描述符从容器中移除
       FD_CLR(i,&tempfds);
 
           //更新maxfd
         for(int j=maxfd;j>sfd;j--)
         {
             if(FD_ISSET(j,&readfds))
             {
                 maxfd=j;
                 break;
             }
         }
       continue;
         }
         //将读取的消息展示出来
         printf("[%s %d]:%s\n",inet_ntoa(cin_arr[i].sin_addr),ntohs(cin_arr[i].sin_port),rbuf);
 
         //将消息转发给所有客户端
         for(int k=4;k<maxfd;k++)
         {
             if(k!=i)
             {
                 send(k,rbuf,strlen(rbuf),0);
             }
          }
         }
      }
 }
     close(sfd);
       return 0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值