前言
這篇文章介紹一下Socket發(fā)送Packet的過程眉尸。
NS3的socket類以及子類组去,工廠類很多男窟,調用過程復雜,琢磨了很長時間才算搞明白一點點蒿囤,本文以UDP協(xié)議為基本來說說Socket發(fā)送Packet的過程客们。
客戶端代碼
InternetStackHelper internet;
internet.SetIpv6StackInstall(false);
internet.Install (c);
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer i = ipv4.Assign (devices);
TypeId tid=UdpSocketFactory::GetTypeId();
Ptr<Socket> recvSink = Socket::CreateSocket (c.Get (0), tid);
InetSocketAddress local = InetSocketAddress (Ipv4Address ("10.1.1.1"), 80);
recvSink->Bind (local);
recvSink->SetRecvCallback (MakeCallback (&ReceivePacket));
Ptr<Socket> source = Socket::CreateSocket (c.Get (1), tid);
InetSocketAddress remote = InetSocketAddress (Ipv4Address ("10.1.1.2"), 80);
source->SetAllowBroadcast (false);
source->Bind(remote);
source->Connect (local);
Simulator::ScheduleWithContext (source->GetNode ()->GetId (),
Seconds (startSec), &GenerateTraffic,
source, PpacketSize, numPackets, interPacketInterval);
這是個很簡單的代碼,首先安裝協(xié)議到節(jié)點容器NodeContainer對象c.
然后分配IP地址蟋软,創(chuàng)建socket對象镶摘,一個source 發(fā)送,一個recvSink 接收岳守。
Socket的創(chuàng)建過程
TypeId tid=UdpSocketFactory::GetTypeId();
Ptr<Socket> recvSink = Socket::CreateSocket (c.Get (0), tid);
這兩行代碼完成了什么功能呢凄敢?需要查看 Socket::CreateSocket方法的代碼:
Ptr<Socket>
Socket::CreateSocket (Ptr<Node> node, TypeId tid)
{
NS_LOG_FUNCTION (node << tid);
Ptr<Socket> s;
NS_ASSERT (node != 0);
Ptr<SocketFactory> socketFactory = node->GetObject<SocketFactory> (tid);
NS_ASSERT (socketFactory != 0);
s = socketFactory->CreateSocket ();
NS_ASSERT (s != 0);
return s;
}
從上面的代碼可以看出,以UdpSocketFactory的TypeId創(chuàng)建一個UdpSocketFactory對象湿痢,然后利用UdpSocketFactory對象的CreateSocket ()方法創(chuàng)建一個Socket對象涝缝。
但是,我們看UdpSocketFactory的源代碼譬重,發(fā)現:
namespace ns3 {
NS_OBJECT_ENSURE_REGISTERED (UdpSocketFactory);
TypeId UdpSocketFactory::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::UdpSocketFactory")
.SetParent<SocketFactory> ()
.SetGroupName ("Internet")
;
return tid;
}
}
UdpSocketFactory非常簡單拒逮,并且是一個包含虛函數,不能創(chuàng)建對象臀规。
此時就需要回到Socket::CreateSocket方法的代碼:
Ptr<SocketFactory> socketFactory = node->GetObject<SocketFactory> (tid);
這一行代碼返回的對象socketFactory 對象不是UdpSocketFactory對象滩援,應該是它的子類對象或者父類對象,但是編程思想上看塔嬉,父類對象基本不可能玩徊。只能是其子類對象。
UdpSocketFactory類的子類只有一個:UdpSocketFactoryImpl谨究。
此時再看UdpSocketFactoryImpl的CreateSocket()方法:
Ptr<Socket>
UdpSocketFactoryImpl::CreateSocket (void)
{
NS_LOG_FUNCTION_NOARGS();
return m_udp->CreateSocket ();
}
其中的m_udp對象是Ptr<UdpL4Protocol>恩袱,再看UdpL4Protocol的CreateSocket ():
Ptr<Socket>
UdpL4Protocol::CreateSocket (void)
{
NS_LOG_FUNCTION_NOARGS ();
Ptr<UdpSocketImpl> socket = CreateObject<UdpSocketImpl> ();
socket->SetNode (m_node);
socket->SetUdp (this);
m_sockets.push_back (socket);
return socket;
}
看到這個代碼就知道了,UdpSocketFactory的實現類UdpSocketFactoryImpl利用UdpL4Protocol對象來創(chuàng)建Socket對象胶哲。而UdpL4Protocol對象的CreateSocket 方法通過創(chuàng)建UdpSocket的實現類UdpSocketImpl來創(chuàng)建socket畔塔,并返回。
也就是說,最終創(chuàng)建的socket對象是UdpSocketImpl類型的澈吨。
節(jié)點獲取SocketFactory
看到這里把敢,node->GetObject<SocketFactory> (tid)代碼如何獲取得到SocketFactory的呢?
可以通過我的另一篇文章: NS3 Node聚合對象說明
這個文章里面說明了節(jié)點如何與其他對象之間進行聚合的棚辽。
Packet發(fā)送過程
在客戶端代碼中有一行代碼:
Simulator::ScheduleWithContext (source->GetNode ()->GetId (),
Seconds (startSec), &GenerateTraffic,
source, PpacketSize, numPackets, interPacketInterval);
這個代碼是給源socket綁定發(fā)送packet的方法技竟。
source->GetNode ()->GetId ()是context。
Seconds (startSec)表示經過這個時間之后屈藐,調用GenerateTraffic方法榔组。
source, PpacketSize, numPackets, interPacketInterval表示調用GenerateTraffic方法的參數。
GenerateTraffic方法:
/**
* 發(fā)送分組回調方法
*/
static void GenerateTraffic (Ptr<Socket> socket, uint32_t pktSize,
uint32_t pktCount, Time pktInterval )
{
NS_LOG_FUNCTION(socket<<pktSize<<pktCount<<pktInterval);
if (pktCount > 0)
{
startTime=Simulator::Now();
NS_LOG_LOGIC("start time:"<<startTime);
socket->Send (Create<Packet> (pktSize));
Simulator::Schedule (pktInterval, &GenerateTraffic,
socket, pktSize,pktCount-1, pktInterval);
}
else
{
socket->Close ();
}
}
方法很簡單联逻,調用Socket對象的send方法發(fā)送packet對象搓扯。
以上便是socket從創(chuàng)建到發(fā)送packet的過程的簡單說明。
Socket通信過程
以上內容說明的是socket對象創(chuàng)建到發(fā)送packet的過程包归。
但是可能仍然有不明白socket為什么那樣寫代碼锨推?可能有點不清楚的。
TCP握手與socket通信細節(jié)
這篇文章說明了socket通信的過程公壤。
看懂了上面的文章换可,自然會對文章開始部分給出的客戶端代碼有一個更清晰的認識。
/*
安裝協(xié)議
*/
InternetStackHelper internet;
internet.SetIpv6StackInstall(false);
internet.Install (c);
/*
分配IP地址
*/
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer i = ipv4.Assign (devices);
TypeId tid=UdpSocketFactory::GetTypeId();
/*
創(chuàng)建socket對象厦幅,作為服務端
*/
Ptr<Socket> recvSink = Socket::CreateSocket (c.Get (0), tid);
InetSocketAddress local = InetSocketAddress (Ipv4Address ("10.1.1.1"), 80);
recvSink->Bind (local);//綁定地址沾鳄,等待連接
recvSink->SetRecvCallback (MakeCallback (&ReceivePacket));//設置回調函數地址
/*
創(chuàng)建socket對象,作為客戶端
*/
Ptr<Socket> source = Socket::CreateSocket (c.Get (1), tid);
InetSocketAddress remote = InetSocketAddress (Ipv4Address ("10.1.1.2"), 80);
source->SetAllowBroadcast (false);
source->Bind(remote);//綁定地址
source->Connect (local);//發(fā)起連接
//連接成功确憨,通過GenerateTraffic方法發(fā)送packet译荞。
Simulator::ScheduleWithContext (source->GetNode ()->GetId (),
Seconds (startSec), &GenerateTraffic,
source, PpacketSize, numPackets, interPacketInterval);