ns3源码阅读(四)-Tcp的Accept函数处理流程

 在实际网络编程中,服务器端会监听在port,每当有新的请求到来的时候,应用层可以调用accept函数,从而获取一个新的socket的文件描述符。服务侧可以通过这个新的socketfd同客户端进行通信。
 在之前阅读PacketSink这个类的时候,PacketSink::HandleAccept这个函数让我困惑很久。让我困惑的是到底什么时候创建了新的Socket对象,可以被放置在m_socketList中。我原以为还是端口监听时,创建的socket,没有发生变化。这个想法是错误的,想想也觉得不合理,即使在仿真状态下,所有的客户端如果公用一个Socket对象,这是没有办法处理TCP的状态变化的。

void PacketSink::StartApplication ()  
{
  m_socket->SetAcceptCallback (
    MakeNullCallback<bool, Ptr<Socket>, const Address &> (),
    MakeCallback (&PacketSink::HandleAccept, this));
}
void PacketSink::HandleAccept (Ptr<Socket> s, const Address& from)
{
  NS_LOG_FUNCTION (this << s << from);
  s->SetRecvCallback (MakeCallback (&PacketSink::HandleRead, this));
  m_socketList.push_back (s);
}

 HandleAccept在 NotifyNewConnectionCreated中被回调。

void
TcpSocketBase::ProcessSynRcvd (Ptr<Packet> packet, const TcpHeader& tcpHeader,
                               const Address& fromAddress, const Address& toAddress)
{
      NotifyNewConnectionCreated (this, fromAddress);
}

 在新的请求到来之后,确实是创建了新的Socket对象。描述与客户端连接的Socket对象,会执行TCP的状态机。而最初监听的Socket则一直处于Listen的状态。
 TCP状态机的几个状态:

enum tcp_state {
  CLOSED      = 0,
  LISTEN      = 1,
  SYN_SENT    = 2,
  SYN_RCVD    = 3,
  ESTABLISHED = 4,
  FIN_WAIT_1  = 5,
  FIN_WAIT_2  = 6,
  CLOSE_WAIT  = 7,
  CLOSING     = 8,
  LAST_ACK    = 9,
  TIME_WAIT   = 10
};

 端口监听的Socket,在处理新的连接时,会调用Fork函数创建新的Socket,将监Socket中的一些参数复制到新的Socket中。

void
TcpSocketBase::ProcessListen (Ptr<Packet> packet, const TcpHeader& tcpHeader,
                              const Address& fromAddress, const Address& toAddress)
{
  Ptr<TcpSocketBase> newSock = Fork ();//创建新的Socket,但是会复制当前Socket的一些参数
  NS_LOG_LOGIC ("Cloned a TcpSocketBase " << newSock);
  Simulator::ScheduleNow (&TcpSocketBase::CompleteFork, newSock,
                          packet, tcpHeader, fromAddress, toAddress);

}
void
TcpSocketBase::CompleteFork (Ptr<Packet> p, const TcpHeader& h,
                             const Address& fromAddress, const Address& toAddress)
{
  //创建新的IPv4EndPoint;
  if (InetSocketAddress::IsMatchingType (toAddress))
    {
      m_endPoint = m_tcp->Allocate (GetBoundNetDevice (),
                                    InetSocketAddress::ConvertFrom (toAddress).GetIpv4 (),
                                    InetSocketAddress::ConvertFrom (toAddress).GetPort (),
                                    InetSocketAddress::ConvertFrom (fromAddress).GetIpv4 (),
                                    InetSocketAddress::ConvertFrom (fromAddress).GetPort ());
      m_endPoint6 = nullptr;
    }
  m_tcp->AddSocket (this);//此时的this指针描述的是newSock这个对象。
}

 那么出现了第二个问题。TcpL4Protocol定义的m_tcp是怎么讲数据包路由给相应的Socket对象(TcpSocketBase)呢?

enum IpL4Protocol::RxStatus
TcpL4Protocol::Receive (Ptr<Packet> packet,
                        Ipv4Header const &incomingIpHeader,
                        Ptr<Ipv4Interface> incomingInterface)
{
  //根据数据包的地址信息,查找endPoints,此对象是个列表,这个就与socket的匹配相关了,比如服务端注册的是rawsocket。
  endPoints = m_endPoints->Lookup (incomingIpHeader.GetDestination (),
                                   incomingTcpHeader.GetDestinationPort (),
                                   incomingIpHeader.GetSource (),
                                   incomingTcpHeader.GetSourcePort (),
                                   incomingInterface);
  (*endPoints.begin ())->ForwardUp (packet, incomingIpHeader,
                                    incomingTcpHeader.GetSourcePort (),
                                    incomingInterface);
}
void 
Ipv4EndPoint::ForwardUp (Ptr<Packet> p, const Ipv4Header& header, uint16_t sport,
                         Ptr<Ipv4Interface> incomingInterface)
{
m_rxCallback (p, header, sport, incomingInterface);
}

 m_rxCallback这个对应的函数为TcpSocketBase::ForwardUp。

void
TcpSocketBase::ForwardUp (Ptr<Packet> packet, Ipv4Header header, uint16_t port,
                          Ptr<Ipv4Interface> incomingInterface)
{
 DoForwardUp (packet, fromAddress, toAddress);
}
void
TcpSocketBase::DoForwardUp (Ptr<Packet> packet, const Address &fromAddress,
                            const Address &toAddress)
{
 // TCP state machine code in different process functions
  // C.f.: tcp_rcv_state_process() in tcp_input.c in Linux kernel
  switch (m_state)
    {
    case ESTABLISHED:
      ProcessEstablished (packet, tcpHeader);
      break;
    case LISTEN:
      ProcessListen (packet, tcpHeader, fromAddress, toAddress);
      break;
    case TIME_WAIT:
      // Do nothing
      break;
    case CLOSED:
      // Send RST if the incoming packet is not a RST
      if ((tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG)) != TcpHeader::RST)
        { // Since m_endPoint is not configured yet, we cannot use SendRST here
          TcpHeader h;
          Ptr<Packet> p = Create<Packet> ();
          h.SetFlags (TcpHeader::RST);
          h.SetSequenceNumber (m_tcb->m_nextTxSequence);
          h.SetAckNumber (m_rxBuffer->NextRxSequence ());
          h.SetSourcePort (tcpHeader.GetDestinationPort ());
          h.SetDestinationPort (tcpHeader.GetSourcePort ());
          h.SetWindowSize (AdvertisedWindowSize ());
          AddOptions (h);
          m_txTrace (p, h, this);
          m_tcp->SendPacket (p, h, toAddress, fromAddress, m_boundnetdevice);
        }
      break;
    case SYN_SENT:
      ProcessSynSent (packet, tcpHeader);
      break;
    case SYN_RCVD:
      ProcessSynRcvd (packet, tcpHeader, fromAddress, toAddress);
      break;
    case FIN_WAIT_1:
    case FIN_WAIT_2:
    case CLOSE_WAIT:
      ProcessWait (packet, tcpHeader);
      break;
    case CLOSING:
      ProcessClosing (packet, tcpHeader);
      break;
    case LAST_ACK:
      ProcessLastAck (packet, tcpHeader);
      break;
    default: // mute compiler
      break;
    }
}

 DoForwardUp进行TCP的状态机的处理。

猜你喜欢

转载自blog.csdn.net/u010643777/article/details/79936653