一个简单的tcp非阻塞connect的客户端

我们知道,tcp客户端要与服务端通信,必须先建立连接,即调用connect函数完成三次握手,而默认情况下connect是阻塞方式的,也就是说调用connect函数会发生阻塞,超时时间可能在75s至几分钟之间。当然同一主机除外,同一主机上调用connect通常会立即成功。

为避免长时间的connect阻塞,可以使用如下非阻塞connect方式来处理:
一、 创建socket,返回套接口描述符
二、 调用fcntl把套接口描述符设置成非阻塞
三、 调用connect开始建立连接
四、 判断连接是否成功建立                
        A: 如果connect返回0,表示连接成功(服务器和客户端在同一台机器上时就有可能发生这种情况)                
        B: 调用select来等待连接建立成功完成                                
    (Berkeley的实现(和Posix.1g)有两条与select和非阻塞IO相关的规则: 
     1.当连接建立成功时,套接口描述符变成可写;
     2.当连接出错时,套接口描述符变成既可读又可写;
     注意:当一个套接口出错时,它会被select调用标记为既可读又可写;)
五、 继续判断select返回值
         如果select返回0,则表示建立连接超时; 我们返回超时错误给用户,同时关闭连接,以防止三路握手操作继续进行下去
         如果select返回大于0的值,则需要检查套接口描述符是否可读或可写;如果套接口描述符可读或可写,则我们可以通过调用getsockopt来得到套接口上待处理的错误(SO_ERROR),如果连接建立成功,这个错误值将是0,如果建立连接时遇到错误,则这个值是连接错误所对应的errno值(比如:ECONNREFUSED,ETIMEDOUT等).


#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include    
#include   
#include   
#include   
#include   

#define DSTADDR     "192.168.1.44"
#define DSTPORT     30040

int main(int argc, char* argv[])
{  
    int ret = 0;
    int sockfd = 0;  
    char buffer[1024] = {0};  
    struct sockaddr_in servAddr;  
    socklen_t addr_size = 0;  
    char destAddr[16] = {0};
    int destPort = 0;
    int recv_len = 0;
    char who[64] = {0};
    char sndbuffer[256] = {0};
    struct timeval tvBegin;
    struct timeval tvEnd;

    if(argc < 3)
    {
	strcpy(destAddr, DSTADDR);
      	destPort = DSTPORT;
    }
    else
    {
      	strcpy(destAddr, argv[1]);
      	destPort = atoi(argv[2]);
    }
  
    /*---- Create socket ----*/  
    if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
    	perror("socket");
    	exit(-1);
    }

    /*---- Set non-block ----*/
    int options = fcntl(sockfd, F_GETFL, 0); 
    fcntl(sockfd, F_SETFL, options | O_NONBLOCK); 
    
    /*---- Configure settings of connection ----*/  
    bzero(&servAddr, sizeof(servAddr));
    servAddr.sin_family = AF_INET;  
    servAddr.sin_port = htons(destPort);  
    servAddr.sin_addr.s_addr = inet_addr(destAddr);  
    /* Set padding field to 0 */  
    //memset(servAddr.sin_zero, 0, sizeof(servAddr.sin_zero));    
  
    /*---- Connect ----*/  
    ret = connect(sockfd, (struct sockaddr *)&servAddr, sizeof(servAddr));  
    if(ret == 0)
    {
      	printf("connected.\n");
    }
    else if(ret < 0)
    {
      	//perror("connect"); 
      	if(errno == EINPROGRESS)
      	{
            printf("connecting...%s:%d\n", destAddr,destPort);

            fd_set writefds;
            FD_ZERO(&writefds);
            FD_SET(sockfd, &writefds);                 

            struct timeval timeout;         
            timeout.tv_sec = 6; 
            timeout.tv_usec = 0;     

            ret = select(sockfd + 1, NULL, &writefds, NULL, &timeout );
            if(ret < 0)
            {
                perror("select");
                close(sockfd);
                exit(-1);
            }
            if (ret == 0)                         
            {               
                printf( "connection timeout\n" );                
                close(sockfd);
                exit(-1);
            }
            else
            {
                if(!FD_ISSET(sockfd, &writefds))
                {
                     printf("err, no events found!\n");        
                     close(sockfd);
                     exit(-1);
                }
                else
                {   
                    int err = 0;
                    socklen_t elen = sizeof(err);
                    ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&err, &elen);
                    if(ret < 0)
                    {
                        perror("getsockopt");
                        close(sockfd);
                        exit(-1);
                    }
                    if(err != 0)
                    {
                        printf("connect failed with the error: (%d)%s\n", err, strerror(err));
                        close(sockfd);
                        exit(-1);
                    }
                    else
                    {
                        printf("connected.\n");
                    }
                }
            }
        }
    }
  
    /*---- Send ----*/
    strcpy(sndbuffer,"Hello!\r\n");  
    if((ret = send(sockfd, sndbuffer, strlen(sndbuffer), 0)) > 0)
    {
      	printf(" >>: %s", sndbuffer);
    }
    else
    {
      	perror("send");
      	close(sockfd);
      	exit(-1);
    }

    gettimeofday(&tvBegin, NULL);

    while(1)
    {
      	/*---- Recv ----*/  
      	recv_len = recv(sockfd, buffer, 1024, 0);  
        if(recv_len < 0)
        {
            if((errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN))
            {
                //printf("again..\n");
            	usleep(100);
            	//continue;
            }
            else
            {
            	printf("recv err1!\n");
            	break;
            }
        }
        else if(recv_len == 0)
        {
            printf("recv err2!\n");
            break;
        }
        else
        {
            // print recv msg
            buffer[recv_len] = '\0';
            printf(" <<: %s\n",buffer);
            char *p = NULL;
            if(p = strstr(buffer, "Login OK."))
            {
             	strncpy(who, p+9);
             	printf("== login success[%s] ==\n\n", who);
            }
            else if(strstr(buffer, "heartbeat"))
            {
             	//printf("heartbeat.\n");

                // do sth response heartbeat and other.
             	memset(sndbuffer, 0, sizeof(sndbuffer));
             	sprintf(sndbuffer, "%s response heartbeat\r\n", who);
             	if((ret = send(sockfd,sndbuffer,strlen(sndbuffer),0)) > 0)
             	{
                    printf(" >>: %s", sndbuffer);
                }

                usleep(100);
                memset(sndbuffer, 0, sizeof(sndbuffer));
             	sprintf(sndbuffer, "%s request other...\r\n", who);
             	if((ret = send(sockfd,sndbuffer,strlen(sndbuffer),0)) > 0)
             	{
                    printf(" >>: %s", sndbuffer);
             	}
            }
        }
       
        //
        gettimeofday(&tvEnd, NULL); 
        unsigned int subTime = tvEnd.tv_sec-tvBegin.tv_sec;
        //printf(".............%d....\n", subTime);
        if(subTime > 3)
        { 
            gettimeofday(&tvBegin, NULL);
            memset(sndbuffer, 0, sizeof(sndbuffer));
            sprintf(sndbuffer, "I am %s,still alive.\r\n", who);
            if((ret = send(sockfd,sndbuffer,strlen(sndbuffer),0)) > 0)
            {
              	printf("->>: %s", sndbuffer);
            } 
        }


        usleep(1000);
    }
  
    return 0;  
}  


你可能感兴趣的:(linux,开发,计算机网络)