1蛹批、前言
- 本文通過學習google quic默認客戶端的網絡IO事件讀取實現(xiàn),來學習在google quic中數(shù)據(jù)的發(fā)送和接收的處理/分發(fā)機制
- 同時通過本節(jié)學習默認IO讀寫實現(xiàn)和其他模塊的關系几迄,為后續(xù)深入分析google quic源碼做鋪墊
2、QuicClientDefaultNetworkHelper定義
在google quic中蔓榄,默認實現(xiàn)QuicClientDefaultNetworkHelper模塊用于往socket中寫入數(shù)據(jù)详恼,以及從socket中讀取數(shù)據(jù)
-
其中QuicClientDefaultNetworkHelper的派生關系如下:
quic_client_socket_read_write_01.png QuicClientDefaultNetworkHelper模塊分別由QuicClientBase::NetWorkHelper、QuicSocketEventListener坤溃、ProcessPacketInterface接口派生,其中1)QuicClientBase::NetWorkHelper用于創(chuàng)建socket拍霜,往QuicEventLoop注冊和注銷IO讀寫事件;2)QuicSocketEventListener用于接收處理socket IO事件如可讀和可寫事件的處理薪介;3)ProcessPacketInterface接口提供ProcessPacket()函數(shù)完成對QuicReceivedPacket報文傳遞到QuicClientDefaultNetworkHelper模塊
同時QuicClientDefaultNetworkHelper由三大成員變量QuicEventLoop祠饺、QuicPacketReader、QuicClientBase組成汁政,1)成員event_loop_指針由其他模塊傳入用于關聯(lián)socket io框架監(jiān)聽socket 讀寫事件道偷;2)packet_reader成員用于讀取Udp報文,并將udp包轉成QuicReceivedPacket包记劈,并最終通過ProcessPacketInterface接口將QuicReceivedPacket報文回調到QuicClientDefaultNetworkHelper模塊勺鸦,然后再將該報文交給成員client_進行處理
3、QuicClientBase::NetworkHelper接口的定義及實現(xiàn)
class QuicClientBase : public QuicSession::Visitor {
public:
// An interface to various network events that the QuicClient will need to
// interact with.
class NetworkHelper {
public:
virtual ~NetworkHelper();
// Runs one iteration of the event loop.
// 用于socket io 循環(huán)loop
virtual void RunEventLoop() = 0;
// Used during initialization: creates the UDP socket FD, sets socket
// options, and binds the socket to our address.
// 創(chuàng)建一個udp socket,并將socket fd 向事件循環(huán)注冊目木,通過IO監(jiān)聽該socket
virtual bool CreateUDPSocketAndBind(QuicSocketAddress server_address,
QuicIpAddress bind_to_address,
int bind_to_port) = 0;
// Unregister and close all open UDP sockets.
// 關閉一個socket,并將socket從io 事件循環(huán)中注銷
virtual void CleanUpAllUDPSockets() = 0;
// If the client has at least one UDP socket, return address of the latest
// created one. Otherwise, return an empty socket address.
virtual QuicSocketAddress GetLatestClientAddress() const = 0;
// Creates a packet writer to be used for the next connection.
// 創(chuàng)建一個socket writer换途,用于往socket寫入數(shù)據(jù)
virtual QuicPacketWriter* CreateQuicPacketWriter() = 0;
};
....
}
- RunEventLoop函數(shù)的視線如下
void QuicClientDefaultNetworkHelper::RunEventLoop() {
quiche::QuicheRunSystemEventLoopIteration();
event_loop_->RunEventLoopOnce(QuicTime::Delta::FromMilliseconds(50));
}
- 該函數(shù)實現(xiàn)較為簡單,就是直接調用```QuicEventLoop::RunEventLoopOnce()``,對IO時間循環(huán)就行迭代,并且在50ms后假設無其他時間觸發(fā),將退出IO迭代
bool QuicClientDefaultNetworkHelper::CreateUDPSocketAndBind(
QuicSocketAddress server_address, QuicIpAddress bind_to_address,
int bind_to_port) {
SocketFd fd = CreateUDPSocket(server_address, &overflow_supported_);
if (fd == kInvalidSocketFd) {
return false;
}
auto closer = absl::MakeCleanup([fd] { (void)socket_api::Close(fd); });
......
if (event_loop_->RegisterSocket(
fd, kSocketEventReadable | kSocketEventWritable, this)) {
fd_address_map_[fd] = client_address;
std::move(closer).Cancel();
return true;
}
return false;
}
- CreateUDPSocketAndBind函數(shù)的核心作用是根據(jù)一個server_address創(chuàng)建一個udp端口军拟,并將該fd往時間循環(huán)中注冊讀寫事件
- 在QuicClientBase::Initialize()函數(shù)中有使用該函數(shù)創(chuàng)建socket句柄
void QuicClientDefaultNetworkHelper::CleanUpAllUDPSockets() {
for (std::pair<int, QuicSocketAddress> fd_address : fd_address_map_) {
CleanUpUDPSocketImpl(fd_address.first);
}
fd_address_map_.clear();
}
void QuicClientDefaultNetworkHelper::CleanUpUDPSocketImpl(SocketFd fd) {
if (fd != kInvalidSocketFd) {
bool success = event_loop_->UnregisterSocket(fd);
QUICHE_DCHECK(success || fds_unregistered_externally_);
absl::Status rc = socket_api::Close(fd);
QUICHE_DCHECK(rc.ok()) << rc;
}
}
- 以上為關閉socket fd的實現(xiàn),同時注銷事件循環(huán)
QuicPacketWriter* QuicClientDefaultNetworkHelper::CreateQuicPacketWriter() {
if (event_loop_->SupportsEdgeTriggered()) {
return new QuicDefaultPacketWriter(GetLatestFD());
} else {
return new LevelTriggeredPacketWriter(GetLatestFD(), event_loop_);
}
}
- 創(chuàng)建QuicPacketWriter指針剃执,用于往socket fd中寫入數(shù)據(jù)
4、QuicSocketEventListener接口的定義及實現(xiàn)
class QUICHE_NO_EXPORT QuicSocketEventListener {
public:
virtual ~QuicSocketEventListener() = default;
virtual void OnSocketEvent(QuicEventLoop* event_loop, SocketFd fd,
QuicSocketEventMask events) = 0;
};
在上節(jié)中有提到通過CreateUDPSocketAndBind()函數(shù)創(chuàng)建socket fd懈息,并將fd向QuicEventLoop注冊讀寫事件肾档,當IO檢測到該fd有可讀或者可寫事件的時候,就會觸發(fā)對應的回調辫继,而該回調就是OnSocketEvent(...)函數(shù)
對應的實現(xiàn)如下:
void QuicClientDefaultNetworkHelper::OnSocketEvent(
QuicEventLoop* /*event_loop*/, QuicUdpSocketFd fd,
QuicSocketEventMask events) {
if (events & kSocketEventReadable) {
QUIC_DVLOG(1) << "Read packets on kSocketEventReadable";
int times_to_read = max_reads_per_event_loop_;
bool more_to_read = true;
QuicPacketCount packets_dropped = 0;
while (client_->connected() && more_to_read && times_to_read > 0) {
// 讀取udp包,并將包轉換成QuicReceivedPacket包阁最,然后通過ProcessPacketInterface::ProcessPacket
// 對報文進行處理
more_to_read = packet_reader_->ReadAndDispatchPackets(
fd, GetLatestClientAddress().port(), *client_->helper()->GetClock(),
this, overflow_supported_ ? &packets_dropped : nullptr);
--times_to_read;
}
....
}
if (client_->connected() && (events & kSocketEventWritable)) {
client_->writer()->SetWritable();
client_->session()->connection()->OnCanWrite();
}
}
- OnSocketEvent函數(shù)為IO事件監(jiān)聽函數(shù)印颤,當socket可讀的時候睡雇,通過QuicPacketReader模塊讀取Udp包,并將包轉換成QuicReceivedPacket同時將QuicReceivedPacket通過ProcessPacketInterface::ProcessPacket()由傳遞到QuicClientDefaultNetworkHelper模塊
- 當socket有可寫事件觸發(fā)的時候,通過成員client_將QuicPacketWriter置成可寫哭当,同時也對對應的QuicConnection模塊調用**OnCanWrite()處理低千,后面進行詳細分析
5配阵、ProcessPacketInterface接口的定義及實現(xiàn)
// A class to process each incoming packet.
class QUIC_NO_EXPORT ProcessPacketInterface {
public:
virtual ~ProcessPacketInterface() {}
virtual void ProcessPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) = 0;
};
void QuicClientDefaultNetworkHelper::ProcessPacket(
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address, const QuicReceivedPacket& packet) {
client_->session()->ProcessUdpPacket(self_address, peer_address, packet);
}
- 由QuicClientDefaultNetworkHelper::ProcessPacket()函數(shù)的視線可以看出,最終對QuicReceivedPacket報文的處理是在QuicSession中進行處理的
- 而QuicSession最終包報文解析工作交給QuicConnection模塊
void QuicSession::ProcessUdpPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
QuicConnectionContextSwitcher cs(connection_->context());
connection_->ProcessUdpPacket(self_address, peer_address, packet);
}
- 在google quic應用中無論是服務端還是客戶端端示血,都是以QuicSession為一次會話單位棋傍,而每個QuicSession一定會有一個QuicConnection成員,可以稱作為連接
- 此處無論是服務端還是客戶端對QuicReceivedPacket的解析工作最終都是在*QuicConnection模塊中完成
總結
- 通過本文學習google quiche開源項目中客戶端socket發(fā)送數(shù)據(jù)和接收數(shù)據(jù)的基本模型
- 在上文的分析中最終引出了QuicSession难审、QuicConnection瘫拣、以及QuicPacketWriter模塊,這些模塊是google quic中的核心模塊
- 通過本文的學習對后續(xù)深入研究google quic做一個簡單鋪墊工作