C++ネットワークプログラミング学習:selectネットワークモデルにアップグレード


ネットワークプログラミング学習レコード
  • 使用言語はC/C+
  • ソースコードサポートプラットフォームは、Windows
  • 笔记一:基础TCP服务端/クライアント 点我跳转笔记二:ネットワークデータメッセージの送受信 点我跳转笔记三:selectネットワークモデル 点我跳转笔记四:プラットフォームをまたいでWindowsをサポート、Linuxシステム 点我跳转笔记5:ソースコードのパッケージ 点我跳转笔记6:バッファオーバーフローと粘着包分包 点我跳转笔记7:サービス端マルチスレッド分离业务処理高负荷 点我跳转笔记
    メモ3
  • ネットワークプログラミング学習記録
  • 一、なぜselectネットワークモデルを使用するのか
  • 二、selectシステム及びその関連
  • ★select関連使用総括と心得
  • 三、selectネットワークモデルにアップグレードする構想
  • 1.サービス側アップグレード(select)
  • 2.クライアントのアップグレード(select+マルチスレッド)
  • 四、コード及びその詳細な注釈
  • 1.サービス側コード
  • 2.クライアントコード

  • 一、なぜselectネットワークモデルを使用するのか.
    前の学習を通じて,簡単なネットワークメッセージ送受信が実現された.しかし,プログラム全体の動作がブロックモードであるという欠点が明らかになった.すなわち、サービス側は、1つのクライアントとsocket接続を行う際に、接続が中断されない限り、新しいクライアントのメッセージを受信できない.クライアントはコマンドが入力されていない場合、ブロック状態であり、サービス側からのメッセージも受信できません.以前にこの問題に遭遇したとき、私の考えはマルチスレッドを通じてプログラムの実行中のブロック問題を解決することですが、最近の学習ではselectネットワークモデルを使用して小型ネットワークプログラムの実行中のブロック問題を簡単かつ迅速に解決できることを知りました.(I/O多重モデル関連)
    二、selectシステムとその関連
    select関数は次のとおりです.
    	WINSOCK_API_LINKAGE int WSAAPI select(
    		int nfds,//                     +1
    		fd_set *readfds,//           
    		fd_set *writefds,//            
    		fd_set *exceptfds,//            
    		const PTIMEVAL timeout);//       NULL        timeval         
    
    	            socket   ,      -10

    上のselect関数のパラメータから,2つの特殊な構造体fd_が存在することが分かった.setとtimeval、その関連内容は以下の通りです.
    	typedef struct fd_set//      socket 
    	{
         
    		u_int	fd_count;//       socket
    		SOCKET	fd_array[FD_SETSIZE];//socket  
    	} fd_set;
    	
    	struct timeval//      
    	{
         
    		long tv_sec;// 
    		long tv_usec;//  
    	};
    

    次にselectの相関関数
    void FD_SET(int fd, fd_set *set);// fd  set  
    void FD_ZERO(fd_set *set);// set          socket
    void FD_CLR(int fd, fd_set *set);// fd set     
    int  FD_ISSET(int fd, fd_set *set);//  fd       0    1  
    

    ★select関連使用まとめと心得
    最初のselect使用では、select関数にfd_を入力すると思います.setアドレス、selectは処理対象イベントのsocketをsetセットに配置しますが、そうではありません.  ネットワーク上の資料の照会と私個人のテストを経て、ユーザーはまず1部のsocket配列をこのsetに伝達する必要があることを発見することができて、select関数の作用はこのsetの中で処理するイベントのsocketがなくて、残りのsocketはすべて処理するイベント(未決I/O操作)が存在します.このプロセスは「選択」のプロセスと言えるが、select関数は操作が必要なsocketを「選択」し、これがselect(選択)の意味かもしれない.次のソースコードでは、接続されているすべてのsocketを格納する必要があるサービス側について、動的配列vectorを使用してsocketの格納を行います.selectフィルタリングを行う前にvectorのsocketをsetにインポートし、その後setで残りの処理対象イベントであるsocketをフィルタリングします. サービス側が自分のsocketプロンプトで処理するイベントがある場合は、新しいクライアントが接続を試みていることを示し、accept操作を行えばよい. クライアントのマルチスレッド問題では、detach()メソッドを使用してプライマリスレッドを新しいスレッドに分類する必要があります.そうしないと、プライマリスレッドが先に終了し、プログラムエラーが発生する可能性があります.スレッドでは、クライアントがまだ接続中であるかどうかを記録するbool変数を導入し、exitコマンドを入力してクライアントを終了すると、このbool変数によってメインスレッドを停止させ、ループから飛び出します.
    三、selectネットワークモデルにアップグレードする考え方
    1.サービス側のアップグレード(select)
    以前、私たちの考えは
    1.  socket
    2.    IP
    3.    
    4.      
    while(true)
    {
         
    	5.    
    	6.    
    }
    7.  socket
    

    これにより、1つのクライアントとしか接続できず、ループに入り、1つのクライアントのメッセージしか受信できません.またsendとrecv関数はいずれもブロック関数であるため,プログラムもブロックモードである.
    次に、selectネットワークモデルに基づいて、サービス側をアップグレードする必要があります.考え方は大体以下の通りである.
    1.  socket
    2.    IP
    3.    
    while(true)
    {
         
    	4.  select            socket
    	5.                
    	6.(     )
    }
    7.  socket
    

    以上のようにして、プログラムをselectネットワークモデルにアップグレードできます.非ブロックモードを実現し、マルチクライアント情報受信を実現することができる.selectに関する詳細とまとめについては、上記のまとめを参照してください.関連コードは以下の通りです.
    2.クライアントのアップグレード(select+マルチスレッド)
    以前、私たちの考えは
    1.  socket
    2.     
    while(true)
    {
         
    	3.    
    	4.    
    }
    5.  socket
    

    これにより、サービス側と接続した後、サーバ側からのメッセージを受動的に受信できません.sendとrecv関数はいずれもブロック関数であるため,プログラムもブロックモードである.クライアントがサービス側から送信されたメッセージを受信できるようにするには、selectモデルを使用します.
    次に、selectネットワークモデルに基づいてクライアントをアップグレードする必要があります.考え方は大体以下の通りである.
    1.  socket
    2.     
    while(true)
    {
         
    	3.  select                
    	4.(  /  )
    }
    5.  socket
    

    以上のようにして、プログラムをselectネットワークモデルにアップグレードできます.非ブロックモードを実現し、サーバ側データの受動受信を実現することができる.
    しかし,scanfなどのデータ受信関数もブロック関数であるため,アクティブにコマンドを入力してサービス側に送信しようとすると,プログラムの実行がブロックされるという欠点も明らかである.これに対して,マルチスレッドを導入して問題を解決することができる.考え方は大体以下の通りである.
    1.  socket
    2.     
    3.            
    while(true)
    {
         
    	4.  select                
    	5.(  /  )
    }
    5.  socket
    
       :
    while(1)
    {
         
    	1.    
    	2.    
    }
    

    以上のようにすれば、プログラムをより完璧にすることができます.データを受動的に受け入れることができ、入力コマンドをサービス側にアクティブに送信することができます.selectに関する詳細とまとめ、スレッドに関する注意事項については、上記のまとめを参照してください.関連コードは以下の通りです.
    四、コードとその詳細な注釈
    1.サービス側コード
    #define WIN32_LEAN_AND_MEAN
    
    #include
    #include
    #include
    
    #pragma comment(lib,"ws2_32.lib")//         windows   
    
    using namespace std; 
     
    //         
    enum cmd 
    {
         
    	CMD_LOGIN,//   
    	CMD_LOGINRESULT,//     
    	CMD_LOGOUT,//   
    	CMD_LOGOUTRESULT,//     
    	CMD_NEW_USER_JOIN,//      
    	CMD_ERROR//   
    };
    //       
    struct DateHeader 
    {
         
    	short cmd;//  
    	short date_length;//     	
    };
    // 1           
    struct Login : public DateHeader 
    {
         
    	Login()//      
    	{
         
    		this->cmd = CMD_LOGIN;
    		this->date_length = sizeof(Login); 
    	}
    	char UserName[32];//    
    	char PassWord[32];//   
    };
    // 2          
    struct LoginResult : public DateHeader 
    {
         
    	LoginResult()//      
    	{
         
    		this->cmd = CMD_LOGINRESULT;
    		this->date_length = sizeof(LoginResult); 
    	}
    	int Result;
    };
    // 3          
    struct Logout : public DateHeader 
    {
         
    	Logout()//      
    	{
         
    		this->cmd = CMD_LOGOUT;
    		this->date_length = sizeof(Logout); 
    	}
    	char UserName[32];//    
    };
    // 4          
    struct LogoutResult : public DateHeader 
    {
         
    	LogoutResult()//      
    	{
         
    		this->cmd = CMD_LOGOUTRESULT;
    		this->date_length = sizeof(LogoutResult); 
    	}
    	int Result;
    };
    // 5            
    struct NewUserJoin : public DateHeader 
    {
         
    	NewUserJoin()//      
    	{
         
    		this->cmd = CMD_NEW_USER_JOIN;
    		this->date_length = sizeof(NewUserJoin); 
    	}
    	char UserName[32];//    
    };
     
    vector<SOCKET> _clients;//     socket 
     
    int _handle(SOCKET _temp_socket)//     
    {
         
    	//           
    	DateHeader _head = {
         }; 
    	int _buf_len = recv(_temp_socket,(char*)&_head,sizeof(DateHeader),0);
    	if(_buf_len<=0)
    	{
         
    		printf("      
    "
    ); return -1; } printf(" , :%d, :%d
    "
    ,_head.cmd,_head.date_length); switch(_head.cmd) { case CMD_LOGIN:// { Login _login; recv(_temp_socket,(char*)&_login+sizeof(DateHeader),sizeof(Login)-sizeof(DateHeader),0); /* */ printf("%s
    :%s
    "
    ,_login.UserName,_login.PassWord); LoginResult _result; _result.Result = 1; send(_temp_socket,(char*)&_result,sizeof(LoginResult),0);// } break; case CMD_LOGOUT:// { Logout _logout; recv(_temp_socket,(char*)&_logout+sizeof(DateHeader),sizeof(Logout)-sizeof(DateHeader),0); /* */ printf("%s
    "
    ,_logout.UserName); LogoutResult _result; _result.Result = 1; send(_temp_socket,(char*)&_result,sizeof(LogoutResult),0);// } break; default:// { _head.cmd = CMD_ERROR; _head.date_length = 0; send(_temp_socket,(char*)&_head,sizeof(DateHeader),0);// } break; } return 0; } int main() { // windows socket 2,x windows WORD ver = MAKEWORD(2,2);//WinSock WSADATA dat;// WSAStartup Socket if(0 != WSAStartup(ver,&dat))// 0 { return 0; } // socket SOCKET _mysocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//IPV4 TCP if(INVALID_SOCKET == _mysocket)// { return 0; } // IP sockaddr_in _myaddr = { };// sockaddr sockaddr_in _myaddr.sin_family = AF_INET;//IPV4 _myaddr.sin_port = htons(8888);// host to net unsigned short _myaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// INADDR_ANY if(SOCKET_ERROR == bind(_mysocket,(sockaddr*)&_myaddr,sizeof(sockaddr_in)))//socket ( )sockaddr { cout<<" "<<endl; } else { //cout< } // if(SOCKET_ERROR == listen(_mysocket,5))// { cout<<" "<<endl; } else { //cout< } while(true) { //select /* WINSOCK_API_LINKAGE int WSAAPI select( int nfds,// +1 fd_set *readfds,// fd_set *writefds,// fd_set *exceptfds,// const PTIMEVAL timeout);// NULL timeval typedef struct fd_set// socket { u_int fd_count;// socket SOCKET fd_array[FD_SETSIZE];//socket } fd_set; struct timeval// { long tv_sec;// long tv_usec;// }; */ fd_set _fdRead;// fd_set _fdWrite; fd_set _fdExcept; FD_ZERO(&_fdRead);// FD_ZERO(&_fdWrite); FD_ZERO(&_fdExcept); FD_SET(_mysocket,&_fdRead);// FD_SET(_mysocket,&_fdWrite); FD_SET(_mysocket,&_fdExcept); timeval _t = { 1,0};//select for(int n=_clients.size()-1; n>=0; --n)// read { FD_SET(_clients[n],&_fdRead); } //select select int _ret = select(_mysocket+1,&_fdRead,&_fdWrite,&_fdExcept,&_t); if(_ret<0) { printf("select
    "
    ); break; } if(FD_ISSET(_mysocket,&_fdRead))// socket { FD_CLR(_mysocket,&_fdRead);// // sockaddr_in _clientAddr = { };// sockadd int _addr_len = sizeof(sockaddr_in);// sockadd SOCKET _temp_socket = INVALID_SOCKET;// _temp_socket = accept(_mysocket,(sockaddr*)&_clientAddr,&_addr_len);// if(INVALID_SOCKET == _temp_socket)// { cout<<" Socket"<<endl; } else { cout<<" "<<endl; printf("IP :%s
    "
    , inet_ntoa(_clientAddr.sin_addr)); // NewUserJoin _user_join; strcpy(_user_join.UserName,inet_ntoa(_clientAddr.sin_addr)); for(int n=0;n<_clients.size();n++) { send(_clients[n],(const char*)&_user_join,sizeof(NewUserJoin),0); } // _clients.push_back(_temp_socket); } } for(int n=0; n<_fdRead.fd_count; ++n)// read { if(-1 == _handle(_fdRead.fd_array[n]))// { vector<SOCKET>::iterator iter = find(_clients.begin(),_clients.end(),_fdRead.fd_array[n]); if(iter != _clients.end())// { _clients.erase(iter); } } } printf("
    "
    ); } // socket for(int n=0; n<_clients.size(); ++n) { closesocket(_clients[n]); } // socket closesocket(_mysocket); // windows socket WSACleanup(); printf(" , "); getchar(); return 0; }

    2.クライアントコード
    #define WIN32_LEAN_AND_MEAN
    
    #include
    #include
    #include
    #include
    
    #pragma comment(lib,"ws2_32.lib")//         windows   
    
    using namespace std; 
     
    //         
    enum cmd 
    {
         
    	CMD_LOGIN,//   
    	CMD_LOGINRESULT,//     
    	CMD_LOGOUT,//   
    	CMD_LOGOUTRESULT,//     
    	CMD_NEW_USER_JOIN,//      
    	CMD_ERROR//   
    };
    //       
    struct DateHeader 
    {
         
    	short cmd;//  
    	short date_length;//     	
    };
    // 1           
    struct Login : public DateHeader 
    {
         
    	Login()//      
    	{
         
    		this->cmd = CMD_LOGIN;
    		this->date_length = sizeof(Login); 
    	}
    	char UserName[32];//    
    	char PassWord[32];//   
    };
    // 2          
    struct LoginResult : public DateHeader 
    {
         
    	LoginResult()//      
    	{
         
    		this->cmd = CMD_LOGINRESULT;
    		this->date_length = sizeof(LoginResult); 
    	}
    	int Result;
    };
    // 3          
    struct Logout : public DateHeader 
    {
         
    	Logout()//      
    	{
         
    		this->cmd = CMD_LOGOUT;
    		this->date_length = sizeof(Logout); 
    	}
    	char UserName[32];//    
    };
    // 4          
    struct LogoutResult : public DateHeader 
    {
         
    	LogoutResult()//      
    	{
         
    		this->cmd = CMD_LOGOUTRESULT;
    		this->date_length = sizeof(LogoutResult); 
    	}
    	int Result;
    };
    // 5            
    struct NewUserJoin : public DateHeader 
    {
         
    	NewUserJoin()//      
    	{
         
    		this->cmd = CMD_NEW_USER_JOIN;
    		this->date_length = sizeof(NewUserJoin); 
    	}
    	char UserName[32];//    
    };
     
    int _handle(SOCKET _temp_socket)//     
    {
         
    	//           
    	DateHeader _head = {
         }; 
    	int _buf_len = recv(_temp_socket,(char*)&_head,sizeof(DateHeader),0);
    	if(_buf_len<=0)
    	{
         
    		printf("        ,    
    "
    ); return -1; } printf(" , :%d, :%d
    "
    ,_head.cmd,_head.date_length); switch(_head.cmd) { case CMD_LOGINRESULT:// { LoginResult _result; recv(_temp_socket,(char*)&_result+sizeof(DateHeader),sizeof(LoginResult)-sizeof(DateHeader),0); printf(" :%d
    "
    ,_result.Result); } break; case CMD_LOGOUTRESULT:// { LogoutResult _result; recv(_temp_socket,(char*)&_result+sizeof(DateHeader),sizeof(LogoutResult)-sizeof(DateHeader),0); printf(" :%d
    "
    ,_result.Result); } break; case CMD_NEW_USER_JOIN:// { NewUserJoin _result; recv(_temp_socket,(char*)&_result+sizeof(DateHeader),sizeof(NewUserJoin)-sizeof(DateHeader),0); printf(" :%s
    "
    ,_result.UserName); } } return 0; } bool _run = true;// void _cmdThread(SOCKET _mysocket)// { while(true) { // char _msg[256] = { }; scanf("%s",_msg); // if(0 == strcmp(_msg,"exit")) { _run = false; printf("
    "
    ); break; } else if(0 == strcmp(_msg,"login")) { // Login _login; strcpy(_login.UserName," "); strcpy(_login.PassWord,"123456"); send(_mysocket,(const char*)&_login,sizeof(_login),0); // select } else if(0 == strcmp(_msg,"logout")) { // Logout _logout; strcpy(_logout.UserName," "); send(_mysocket,(const char*)&_logout,sizeof(_logout),0); // select } else { printf("
    "
    ); } } } int main() { // windows socket 2,x windows WORD ver = MAKEWORD(2,2);//WinSock WSADATA dat;// WSAStartup Socket if(0 != WSAStartup(ver,&dat))// 0 { return 0; } // socket SOCKET _mysocket = socket(AF_INET,SOCK_STREAM,0);//IPV4 if(INVALID_SOCKET == _mysocket)// { return 0; } // sockaddr_in _sin = { };//sockaddr _sin.sin_family = AF_INET;//IPV4 _sin.sin_port = htons(8888);// _sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// IP if(SOCKET_ERROR == connect(_mysocket,(sockaddr*)&_sin,sizeof(sockaddr_in))) { cout<<" "<<endl; closesocket(_mysocket); } else { cout<<" "<<endl; } // thread t1(_cmdThread,_mysocket); t1.detach();// while(_run) { fd_set _fdRead;// FD_ZERO(&_fdRead);// FD_SET(_mysocket,&_fdRead);// timeval _t = { 1,0};//select // seclect int _ret = select(_mysocket+1,&_fdRead,NULL,NULL,&_t); if(_ret<0) { printf("seclect
    "
    ); break; } if(FD_ISSET(_mysocket,&_fdRead))// socket { FD_CLR(_mysocket,&_fdRead);// if(-1 == _handle(_mysocket)) { printf("seclect
    "
    ); break; } } } // socket closesocket(_mysocket); // windows socket WSACleanup(); return 0; }