TCP 三次握手 四次挥手 socket 状态 对应代码


int CSocket::SetSockOpt(int iLevel, int iOptName, const void *pOptVal, socklen_t tOptLen)
{
    if( setsockopt(m_iSocket,iLevel,iOptName,pOptVal,tOptLen)<0 )
    {
        m_strError="fail in function CSocket::SetSockOpt()";
        m_strError =  m_strError +"the error is {" + strerror(errno) + "}";
        return -1;
    }
    return 0;
}
int CSocket::GetSockOpt(int iLevel, int iOptName, void * pOptVal, socklen_t *ptOptLen)
{
    if( getsockopt(m_iSocket,iLevel,iOptName,pOptVal,ptOptLen)<0 )
    {
        m_strError="fail in function CSocket::SetSockOpt()";
        m_strError =  m_strError +"the error is {" + strerror(errno) + "}";
        return -1;
    }
    return 0;
}

int CSocket::SetUnblock()
{
    int iFlags;
    if( (iFlags = fcntl( m_iSocket, F_GETFL ,0 )) < 0 )
    {
        m_strError="fcntl() incurr an error=[";
        m_strError= m_strError + strerror(errno)+"]";
        return -1;
    }

    iFlags |= O_NONBLOCK ;
    if( fcntl( m_iSocket , F_SETFL, iFlags )< 0 )
    {
        m_strError="fcntl() incurr an error=[";
        m_strError= m_strError + strerror(errno)+"]";
        return -1;
    }
    return 0;
}

int CSocket::ResetToBlock()
{
    int iFlags;
    if( (iFlags = fcntl( m_iSocket, F_GETFL ,0 )) < 0 )
    {
        m_strError="fcntl() incurr an error=[";
        m_strError= m_strError + strerror(errno)+"]";
        return -1;
    }

    iFlags &= ~O_NONBLOCK ;
    if( fcntl( m_iSocket , F_SETFL, iFlags )< 0 )
    {
        m_strError="fcntl() incurr an error=[";
        m_strError= m_strError + strerror(errno)+"]";
        return -1;
    }
    return 0;
}


int CTcpWrRdSocket::NeededOptSet()
{
    int i,iRet,iFlag,iLinlen;
    struct linger tSockLinger;

    tSockLinger.l_onoff=1;
    tSockLinger.l_linger=0;
    iFlag=1;   // 互联网业务繁忙,read, receive is always going, so default 2 hour is ok..
    iLinlen=0;
    iLinlen=sizeof(struct linger);

    if( SetSockOpt(SOL_SOCKET,SO_LINGER,&tSockLinger,iLinlen)<0 )
    { return -1; }
    if( SetSockOpt(SOL_SOCKET,SO_KEEPALIVE,(char *)&iFlag,sizeof(iFlag))<0 )
    { return -1; }
    if( SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &iFlag, sizeof(iFlag))<0 )
    { return -1; }
    return 0;
}



int CTcpWrRdSocket::ReceiveByLen(int iNeededRecvLen)
{
    int iRecvedBytes=0,iTmpBytes;
    char * pTmpPtr=NULL;

    if( 0==iNeededRecvLen )
    {      m_strError=" para error=[TCP socket 要接收的字节数为0";      return -1;    }
    
    pTmpPtr=m_pRecvBuf;
    if( iNeededRecvLen>m_iRecvBufSize )   //接收的数据字节数不应超过缓冲区的大小
    {
        m_strError="error =[要接收的字节数超过缓冲区的大小], in function CTcpSocket::RecvByLen()";
        return -1;
    }
    while( iRecvedBytes<iNeededRecvLen )
    {
             if( (iTmpBytes=read(m_iSocket,pTmpPtr+iRecvedBytes,iNeededRecvLen-iRecvedBytes))>0 )
             {
                 iRecvedBytes=iRecvedBytes+iTmpBytes;
        }
        else if(iTmpBytes==0)  //对方关闭连接
        {
            m_strError="接收数据时对方关闭连接";
            break;
        }
        else if( errcode != EINTR && errcode != EAGAIN && errcode != EWOULDBLOCK )
        {        //中断引起的数据接收异常 EAGAIN=EWOULDBLOCK 非阻塞socket的问题
                continue;
        }
        else if( errno==EAGAIN )
        { break; } //非阻塞socket,此时没有数据可读

        else                     //处理ECONNRESET,ETIMEOUT,SO_RCVTIMEO的错
        {
            char buf[20];
            memset(buf,0x00,sizeof(buf));
            sprintf(buf,"errno=%d", errno);
            m_strError="read() incur an error in CTcpWrRdSocket::Receive()";
            m_strError =  m_strError +"the error is [" + strerror(errno) + "]" + buf;
            return -1;
        }
    }
//    *(pTmpPtr+iRecvedBytes+1)=0x00;
    return iRecvedBytes;
}


第一次握手:建立连接时,客户端发送syn包和一个随机序列号seq=x到服务器,并进入SYN_SEND状态,等待服务器进行确认。(syn,同 步序列编号)。第二次握手,服务器收到syn包,必须确认客户的SYN,然后服务器发送一个ACK=1, SYN=1, seq=y的随机数和ack=x+1的确认数的包发送回去。第三次握手是客户端收到服务器端的SYN+ACK包,然后向服务器端发送确认包 ack=y+1, seq=x+1, ACK=1,客户端和服务器端进入ESTABLISHED状态,完成三次握手。具体图示如下:

 TCP 三次握手 四次挥手 socket 状态 对应代码_第1张图片TCP 三次握手 四次挥手 socket 状态 对应代码_第2张图片

这里多说一点,既然提到了连接时的三次握手,就顺便把断开连接时的四次挥手也复习一下。首先客户端主动发送Fin=1,seq=u,它等于前面已传 送过去的最后一个字节的序号加1.这是A进入FIN-WAIT-1状态,等待B的确认。B收到连接后立即发出确认,确认号是ack=u+1,而这个报文段 自己的序号是v,等于B前面已传送过的数据的最后一个字节的序号加1.然后B即进入CLOSE-WAIT状态。因而A到B的这个链接现在已经断开了,这时 的TCP连接处于半关闭状态,即A已经没有数据需要发送了。但B若发送数据,A还是要接受的。A收到来自B的确认之后就进入了FIN-WAIT-2状态等 待B发出连接释放报文段。若B已经没有要向A发送数据,其应用进程就通知TCP释放连接。这是B发出的连接释放报文段必须使用FIN=1.现在假定B的序 号为w,B还必须重复上次已发送过的确认号ack=u+1.这时B就进入了LAST-ACK状态,等待A确认。A在收到B的连接释放之后必须对此发出确 认。在确认号中把ACK置1,确认号ack=w+1,而自己的序号是seq=u+1。接着A进入TIME-WAIT状态。为了保证B可以收到确认释放报文 段。如上图:

 


是不是所有执行主动关闭的socket都会进入TIME_WAIT状态呢?
有没有什么情况使主动关闭的socket直接进入CLOSED状态呢?

主动关闭的一方在发送最后一个 ack 后
就会进入 TIME_WAIT 状态 停留2MSL(max segment lifetime)时间
这个是TCP/IP必不可少的,也就是“解决”不了的。

也就是TCP/IP设计者本来是这么设计的
主要有两个原因
1。防止上一次连接中的包,迷路后重新出现,影响新连接
(经过2MSL,上一次连接中所有的重复包都会消失)
2。可靠的关闭TCP连接
在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发
fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以
主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。


你可能感兴趣的:(tcp,socket,close_wait,TIME_WAIT,状态)