Socket通信(TCP)非阻塞模式-select模型

来源:互联网 发布:淘宝网店怎么设置客服 编辑:程序博客网 时间:2024/06/06 05:24
原文地址:Socket通信(TCP)非阻塞模式-select模型
 
这个范例是个基于TCP协议的非阻塞模式下的SOCKET通信。
应该非常具有代表性了,分为服务器端和客户端。
非阻塞类型: Select模型
 
////////////////////////////////////////////
//
//   TCP Server  select非阻塞模式
//   IP: 127.0.0.1
//   PORT: 1207
////////////////////////////////////////////
#define LISTEN_IP    "127.0.0.1"
#define LISTEN_PORT  1207
#define DEFAULT_BUFF 256
#define MAX_LISTEN   2    //最多可同时连接的客户端数量
int g_fd_ArrayC[MAX_LISTEN] = {0}; //处理所有的待决连接
int _tmain(int argc, _TCHAR* argv[])
{
    WSAData        wsData;
    SOCKET         sListen;
    SOCKET         sClient;
    SOCKADDR_IN    addrListen;
    SOCKADDR_IN    addrClient;
    int            addrClientLen = sizeof(addrClient);
    char           recvBuff[DEFAULT_BUFF] = {0};
    char           responseBuff[DEFAULT_BUFF] = {"Server Has Received"};
    char           noresponseBuff[DEFAULT_BUFF] = {"服务器端连接数已满,无法连接"};
    int            nRes = 0;
    printf(">>>>>TCP 服务器端启动<<<<<<n");
    WSAStartup(MAKEWORD(2,2), &wsData);
    printf("-创建一个SOCKETn");
    sListen = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    if(sListen==INVALID_SOCKET)
    {
        printf("!!! socket failed: %dn", WSAGetLastError());
        WSACleanup();
        return -1;
    }
    printf("-设定服务器监听端口n");
    addrListen.sin_family = AF_INET;
    addrListen.sin_addr.S_un.S_addr = inet_addr( LISTEN_IP );
    addrListen.sin_port = htons( LISTEN_PORT );
    printf("-绑定SOCKET与指定监听端口: %s:%dn", inet_ntoa(addrListen.sin_addr), ntohs(addrListen.sin_port));
    nRes = bind( sListen, (const sockaddr*)&addrListen, sizeof(addrListen) );
    if( nRes == SOCKET_ERROR )
    {
        printf("!!! bind failed: %dn", WSAGetLastError());
        closesocket( sListen );
        WSACleanup();
        return -1;
    }
    printf("-监听端口n");
    nRes = listen( sListen, MAX_LISTEN );
    if( nRes == SOCKET_ERROR )
    {
        printf("!!! listen failed: %dn", WSAGetLastError());
        closesocket( sListen );
        WSACleanup();
        return -1;
    }
    /////////////////////////////
    //  非阻塞模式设定
    //
    /////////////////////////////
    DWORD nMode = 1;
    nRes = ioctlsocket( sListen, FIONBIO, &nMode );
    if( nRes == SOCKET_ERROR )
    {
        printf("!!! ioctlsocket failed: %dn", WSAGetLastError());
        closesocket( sListen );
        WSACleanup();
        return -1;
    }
    printf("-设置服务器端模式: %sn", nMode==0? "阻塞模式":"非阻塞模式");

    printf("-开始准备接受连接n");
    fd_set fdRead;
    fd_set fdWrite;
    timeval tv={10,0};
    int     nLoopi = 0;
    int     nConnNum = 0;
    while(true)
    {
        printf("-select 开始n");
        FD_ZERO(&fdRead, &fdWrite);
        FD_SET( sListen, &fdRead ); 
        //将待决的连接SOCKET放入fdRead集中进行select监听
        for( nLoopi=0; nLoopi<MAX_LISTEN; ++nLoopi )
        {
            if( g_fd_ArrayC[nLoopi] !=0 )
            {
                printf("-LOOPI: 待决SOCKET: %dn",g_fd_ArrayC[nLoopi] );
                FD_SET( g_fd_ArrayC[nLoopi], &fdRead );
            }
        }
        //调用select模式进行监听
        nRes = select( 0, &fdRead, NULL, NULL, &tv );
        ;;;;;if( nRes == 0 )
        {
            printf("-!!! select timeout: %d secn",tv.tv_sec);
            continue; //继续监听
        }
        else if( nRes < 0 )
        {
            printf("!!! select failed: %dn", WSAGetLastError());
            break;
        }

        //检查所有的可用SOCKET
        printf("-查找可用的SOCKETn");
        for( nLoopi=0; nLoopi<MAX_LISTEN; ++nLoopi )
        {
            if( FD_ISSET(g_fd_ArrayC[nLoopi], &fdRead) )
            {
                memset( recvBuff, 0 ,sizeof(recvBuff) );
                nRes = recv( g_fd_ArrayC[nLoopi], recvBuff, sizeof(recvBuff)-1, 0 );
                if( nRes <= 0 )
                {
                    printf("-Client Has Closed.n");
                    closesocket( g_fd_ArrayC[nLoopi] );
                    //将已经关闭的SOCKET从FD集中删除
                    FD_CLR( g_fd_ArrayC[nLoopi], &fdRead );
                    g_fd_ArrayC[nLoopi] = 0;
                    --nConnNum;
                }
                else
                {
                    recvBuff[nRes] = '';
                    printf("-Recvied: %sn", recvBuff);
                    send( g_fd_ArrayC[nLoopi], responseBuff, strlen(responseBuff), 0 );
                }
            }
        }//for( nLoopi=0; nLoopi<MAX_LISTEN; ++nLoopi )

        //检查是否为新的连接进入
        if( FD_ISSET( sListen, &fdRead) )
        {
            printf("-发现一个新的客户连接n");
            sClient = accept( sListen, (sockaddr*)&addrClient, &addrClientLen );
            ;;;;;if( sClient == WSAEWOULDBLOCK )
            {
                printf("!!! 非阻塞模式设定 accept调用不正n");
                continue;
            }
            else if( sClient == INVALID_SOCKET  )
            {
                printf("!!! accept failed: %dn", WSAGetLastError());
                continue;
            }
            //新的连接可以使用,查看待决处理队列
            if( nConnNum<MAX_LISTEN )
            {
                for(nLoopi=0; nLoopi<MAX_LISTEN; ++nLoopi)
                {
                    if( g_fd_ArrayC[nLoopi] == 0 )
                    {//添加新的可用连接
                        g_fd_ArrayC[nLoopi] = sClient;
                        break;
                    }
                }
                ++nConnNum;
                printf("-新的客户端信息:[%d] %s:%dn", sClient, inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));
            }
            else
            {
                printf("-服务器端连接数已满: %dn", sClient);
                send( sClient, noresponseBuff, strlen(noresponseBuff), 0 );
                closesocket( sClient );
            }
        }//if( FD_ISSET( sListen, &fdRead) )
    }//while(true)
    printf("-关闭服务器端SOCKETn");
    closesocket( sListen );
    WSACleanup();
 return 0;
}

////////////////////////////////////////////
//
//  TCP Client  非阻塞模式设定
//
////////////////////////////////////////////
#define CONNECT_IP        "127.0.0.1"
#define CONNECT_PORT      1207
#define DEFAULT_BUFF_LEN  256
int _tmain(int argc, _TCHAR* argv[])
{
   WSAData       wsData;
   SOCKET        sServer;
   SOCKADDR_IN   addrServer;
   SOCKADDR_IN   addrLocal;
   char          sendBuff[DEFAULT_BUFF_LEN]={0};
   char          recvBuff[DEFAULT_BUFF_LEN]={0};
   int           nError;
   printf(">>>>>TCP 客户端启动<<<<<<n");
   WSAStartup( MAKEWORD(2,2), &wsData );
   printf("-创建客户端用SOCKETn");
   sServer = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
   if( sServer == INVALID_SOCKET )
   {
       printf("!!! socket failed: %dn", WSAGetLastError());
       WSACleanup();
       return -1;
   }
   addrServer.sin_family = AF_INET;
   addrServer.sin_addr.S_un.S_addr = inet_addr( CONNECT_IP );
   addrServer.sin_port = htons(CONNECT_PORT);
   printf("-设定服务器地址信息: %s:%dn",inet_ntoa(addrServer.sin_addr), ntohs(addrServer.sin_port));

   //设定本地用地址和端口
   addrLocal.sin_family = AF_INET;
   addrLocal.sin_addr.S_un.S_addr = inet_addr( CONNECT_IP );
   addrLocal.sin_port = htons( 2701 );
   printf("-绑定本地用地址和端口: %s:%dn", inet_ntoa(addrLocal.sin_addr), ntohs(addrLocal.sin_port));
   nError = bind( sServer, (const sockaddr*)&addrLocal, sizeof(addrLocal) );
   if( nError == SOCKET_ERROR )
   {
       printf("!!! bind failed: %dn", WSAGetLastError());
       WSACleanup();
       return -1;
   }

   printf("-连接指定服务器n");
   nError = connect( sServer, (const sockaddr*)&addrServer, sizeof(addrServer) );
   if( nError == SOCKET_ERROR )
   {
       printf("!!! connect failed: %dn", WSAGetLastError());
       closesocket( sServer );
       WSACleanup();
       return -1;
   }
   ///////////////////////////////////
   //  客户端 SOCKET为非阻塞模式
   //
   ///////////////////////////////////
   DWORD nMode = 1;
   nError = ioctlsocket( sServer, FIONBIO, &nMode );
   if( nError == SOCKET_ERROR )
   {
       printf("!!! ioctlsocket failed: %dn", WSAGetLastError());
       closesocket( sServer );
       WSACleanup();
       return -1;
   }
   printf("-SOCKET模式设定: %sn", (nMode==0?"阻塞模式设定":"非阻塞模式设定"));

   printf("-开始准备送信受信n");
   int nInputLen = 0;
   int nIndex    = 0;
   int nLeft     = 0;
   fd_set  fdRead;
   fd_set  fdWrite;
   timeval tv={10,0};
   while(true)
   {
       FD_ZERO( &fdRead );
       FD_ZERO( &fdWrite );
       FD_SET( sServer, &fdRead );
       FD_SET( sServer, &fdWrite );
       nError = select( 0, &fdRead, &fdWrite, NULL, &tv );
       ;;;;;if( nError == 0 )
       {
           printf("!!! select time outn");
           continue;
       }
       else if( nError < 0 )
       {
           printf("!!! select failed: %dn", WSAGetLastError());
           break;
       }
     
 
       //发现SOCKET可读/可写
       if( FD_ISSET( sServer, &fdRead) )
       {
           memset( recvBuff, 0, sizeof(recvBuff) );
           nError = recv( sServer, recvBuff, sizeof(recvBuff)-1, 0 );
           ;;;;;if( nError == SOCKET_ERROR )
           {
               printf("!!! recv failed: %dn", WSAGetLastError());
               break;
           }
           else if( nError == 0 )
           {
               printf("-Server Has Closed.n");
               break;
           }
           recvBuff[nError] = '';
           printf("-Received: %snn", recvBuff);
       }
       if( FD_ISSET(sServer, &fdWrite) )
       {
           printf("Input Message You Want to Send( Quit ):n");
           fflush( stdin );
           memset( sendBuff, 0, sizeof(sendBuff) );
           fgets( sendBuff, sizeof(sendBuff)-1, stdin );
           nInputLen = strlen( sendBuff );
           sendBuff[nInputLen-1] = '';//去掉回车符 
           --nInputLen;
           nIndex = 0;
           nLeft  = nInputLen;
           if(strcmp(sendBuff, "Quit")==0)
           {
               break;
           }
           //将输入的数据发送过去
           while( nLeft > 0)
           {
               nError = send( sServer, &sendBuff[nIndex], nLeft, 0 );
               ;;;;if( nError == SOCKET_ERROR )
               {
                   printf("!!! send failed: %dn",WSAGetLastError());
                   closesocket( sServer );
                   WSACleanup();
                   return -1;
               }
               else if( nError == 0 )
               {
                   printf("-Send OK.n");
                   break;
               }
             
               nLeft  -= nError;
               nIndex += nError;
           }
           printf("-Has send: %sn",sendBuff);
       }
   }//while(true)

   printf("-关闭SOCKETn");
   closesocket(sServer);
   WSACleanup();
   return 0;
}
0 0
原创粉丝点击