ns3 source code reading (4)-Tcp Accept function processing flow

 In actual network programming, the server side will listen on the port. Whenever a new request arrives, the application layer can call the accept function to obtain the file descriptor of a new socket. The server side can communicate with the client through this new socketfd.
 When I read the PacketSink class before, the PacketSink::HandleAccept function puzzled me for a long time. What confuses me is when exactly a new Socket object is created that can be placed in m_socketList.I thought that the socket created when the port was listening did not change.This idea is wrong, and it is unreasonable to think about it. Even in the simulation state, if all clients share a Socket object, there is no way to deal with the state change of 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 is called back in NotifyNewConnectionCreated.

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

 After a new request arrives, a new Socket object is indeed created. The Socket object that describes the connection to the client will execute the TCP state machine. The initial listening Socket is always in the Listen state.
 Several states of the TCP state machine:

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
};

 The Socket monitored by the port will call the Fork function to create a new Socket when processing a new connection, and copy some parameters in the monitored Socket to the new 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这个对象。
}

 Then the second question arises. How does m_tcp defined by TcpL4Protocol talk about routing packets to the corresponding Socket object (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);
}

 The corresponding function of m_rxCallback is 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 processes the state machine of TCP.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325426989&siteId=291194637