在实际网络编程中,服务器端会监听在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的状态机的处理。
[1] TCP 的那些事儿
[2] TCP在NS3中的实现