网络编程:select通信demo

本文介绍了如何使用C++实现一个TCP服务器,通过SelectServer类展示了Socket、Bind、Listen和Accept等关键操作,并利用select函数实现了多路复用,处理客户端连接和数据传输。
摘要由CSDN通过智能技术生成

tcpServer

#pragma once
#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<typeinfo>
#include<exception>
#include<stdexcept>

#include<arpa/inet.h>
#include<netinet/ip.h>
#include<signal.h>
#include<sys/select.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>

using namespace std;
#define BackLog 3

class TcpServer
{
    public:
      TcpServer() = delete;
      TcpServer(const TcpServer& t) = delete;
      TcpServer& operator=(const TcpServer& t) = delete;
      static int Socket()
      {
          int lsock = socket(AF_INET,SOCK_STREAM,0);
          if( lsock < 0 )
          {
             throw runtime_error(string("socket error"));
          }
          cout<<"Socket:lsock:"<<lsock<<endl;
          return lsock;
      }
      static void Bind(int lsock,int port)
      {
          sockaddr_in local;
          local.sin_family = AF_INET;
          local.sin_port = htons(port);
          local.sin_addr.s_addr = INADDR_ANY;

          if( bind(lsock,(struct sockaddr*)&local,sizeof(local)) < 0 )
          {
             throw runtime_error(string("bind error"));
          }
          cout<<"Bind success..."<<endl;

      }
      static void Listen(int lsock)
      {
          if( listen(lsock,BackLog) < 0 )
          {
              throw runtime_error(string("listen error")); 
          }
          cout<<"Listen success..."<<endl;
      }
      static int Accept(int lsock)
      {
          sockaddr_in end_point;
          socklen_t len = sizeof(end_point);
          int sock = accept(lsock,(struct sockaddr*)&end_point,&len);
          if( sock < 0 )
          {
              cout<<"this time accept fail ..."<<endl;
              return -1;
          }
          else{
              return sock;
          }
      }
      ~TcpServer()
      {}
    private:

};


SelectServer

#pragma once
#include "tcpServer.hpp"
#define NUM (sizeof(fd_set)*8)
#define DFL -1

class SelectServer
{
    public:
      SelectServer(int port= 8080)
        :_port(port)
      {
          for(int i=0; i < static_cast<int>NUM; i++) _rfd_array[i] = _wfd_array[i] = DFL;
      }
      void SelectServerInit()
      {
        try{
             _lsock = TcpServer::Socket();
             TcpServer::Bind(_lsock,_port);
             TcpServer::Listen(_lsock);
             _rfd_array[0] = _lsock;
             cout<<"_lsock"<<_lsock<<endl;
        }  
        catch(exception& e)
        {
            cout<<"SelectServerInit"<<" "<<e.what()<<endl;
        }
      }
      void SelectServerStart()
      {
        while(1)
        {
          int nfds = 0;
          /*由于输入型和输出型参数合二为一,要每次进行select的*readfds和*writefds的重新绑定以及新文件描述符的绑定*/
          fd_set rfds,wfds;
          FD_ZERO(&rfds);FD_ZERO(&wfds);
          for(int i = 0 ; i < static_cast<int>NUM; i++)
          {
              if( _rfd_array[i] != DFL )
              {
                 FD_SET(_rfd_array[i],&rfds);
                 nfds = max(nfds, _rfd_array[i]);
              }
              if( _wfd_array[i] != DFL )
              {
                 FD_SET(_wfd_array[i],&wfds);
                 nfds = max(nfds,_wfd_array[i]);
              }
          }
          struct timeval timeout;
          timeout.tv_sec = 10;
          timeout.tv_usec = 0;
          cout<<"nfds="<<nfds<<endl; 
          cout<<endl;
          cout<<"select begin..."<<endl;
          switch(select(nfds+1,&rfds,&wfds,nullptr,&timeout))
          {
             case 0:
               cout<<" timeout..." << endl;
               break;
             case -1:
               cerr<<"select error..."<<endl;
               break;
             default:
               cout<<"Rervice()"<<endl;
               Rervice(&rfds , &wfds);
               break;
          }
        }
      }
      void Rervice( fd_set* rfds , fd_set* wfds )
      {
           for(int i =0 ; i < static_cast<int>NUM ; i++ )
           {
              if( _rfd_array[i] == DFL ) continue;
              if( FD_ISSET ( _rfd_array[i] , rfds ) )
              {
                  if( _rfd_array[i] == _lsock )
                  {
                      int sock = TcpServer::Accept(_lsock);
                      if( sock >=0 )
                      {
                          cout<<"get a new link..."<<endl;
                      }
                      Add2ReadFD(sock);
                  }
                  else//给读使用的,此时数据已经就绪不会发生阻塞,直接进行拷贝,要考虑到recv收到的字节数符合应用层协议
                  {
                      //考虑应用层协议的在epoll的demo中实现
                      char buf[10240];
                      //bug!
                      int recnum = recv(_rfd_array[i] , buf , (int)sizeof(buf)-1 , 0 );
                      
                      if(recnum > 0)
                      {
                          cout<<"echo #:receive "<<recnum<<"bytes   :"<< buf << endl;
                      }
                      else if( recnum == 0 )
                      {
                          cout<<"client quit..."<<endl;
                      }
                      Add2WriteFD(_rfd_array[i]);//修改为写事件
                      DelFromReadFD(i);
                  }
              }
              else if( FD_ISSET(_wfd_array[i],wfds) )
              {
                    char buf[10240] = {" I am received . connect is over . good bye\n"};
                    send(_wfd_array[i] , buf, sizeof(buf) , 0);
                    close(_wfd_array[i]);
                    DelFromWriteFD(i);
              }
           }
      }
      void Add2ReadFD(int fd)
      {
           int i = 0;
           for( ; i < static_cast<int>NUM ; i++)
           {
             if( _rfd_array[i] == DFL){
                 break;
             }
           }
           if( i >= static_cast<int>NUM )
           {
              cerr <<"fd array is full"<<endl;
              close(fd);
           }
           else{
              _rfd_array[i] = fd; 
              cout<<"readfd :"<<fd<<endl;
           }
      }
      void Add2WriteFD(int fd)
      {
            int i = 0;
            for( ; i< static_cast<int>NUM ; i++ )
            {
                if( _wfd_array[i] == DFL )
                {
                    break; 
                }
            }
            if( i>= static_cast<int> NUM )
            {
                cout<<"_wfd_array is full ..."<<endl;
                close(fd);
            }
            else{
               _wfd_array[i] = fd;
               cout<<"writefd :"<<fd<<endl;
            } 
      }
      void DelFromReadFD(int idx)
      {
           if( idx >=0 && idx < static_cast<int>NUM ) _rfd_array[idx] = DFL; 
      }
      void DelFromWriteFD(int idx)
      {
           if( idx >=0 && idx < static_cast<int>NUM ) _wfd_array[idx] = DFL;
      }
      ~SelectServer()
      {}
    private:
      int _lsock;
      int _port;
      int _rfd_array[NUM];
      int _wfd_array[NUM];
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值