博客[1]的分析很好。[2]的代码可以作为测试,免去下载所有chromium代码的麻烦。
本文主要在其基础上分析server端的处理逻辑。
server进行端口监听,并向epoll_server上注册需要监听的socket描述符。
//https://cs.chromium.org/chromium/src/net/third_party/quic/tools/quic_server.cc?l=110
bool QuicServer::CreateUDPSocketAndListen(const QuicSocketAddress& address) {
epoll_server_.RegisterFD(fd_, this, kEpollFlags);
dispatcher_.reset(CreateQuicDispatcher());
dispatcher_->InitializeWithWriter(CreateWriter(fd_));
}
//QuicServer::Run() 不断询问是否有IO事件,若有,epoll_server会回调OnEvent函数
void QuicServer::Run() {
WaitForEvents();
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&QuicServer::Run, weak_factory_.GetWeakPtr()));
}
void QuicServer::OnEvent(int fd, net::EpollEvent* event) {
if (event->in_events & EPOLLIN) {
QUIC_DVLOG(1) << "EPOLLIN";
dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent);
bool more_to_read = true;
while (more_to_read) {
//packet_reader_负责数据包的读取与分发,数据读取完毕后,回调dispatcher_
//中ProcessPacket函数
more_to_read = packet_reader_->ReadAndDispatchPackets(
fd_, port_, QuicEpollClock(&epoll_server_), dispatcher_.get(),
overflow_supported_ ? &packets_dropped_ : nullptr);
}
}
}
在新版本里,新的session在ProcessBufferedChlos被创建,session创建的同时也创建了connection这个对象。以 QuicSimpleDispatcher为例,它继承了QuicDispatcher。
QuicServerSessionBase* QuicSimpleDispatcher::CreateQuicSession(
QuicConnectionId connection_id,
const QuicSocketAddress& client_address,
QuicStringPiece /*alpn*/) {
// The QuicServerSessionBase takes ownership of |connection| below.
QuicConnection* connection = new QuicConnection(
connection_id, client_address, helper(), alarm_factory(),
CreatePerConnectionWriter(),
/* owns_writer= */ true, Perspective::IS_SERVER, GetSupportedVersions());
QuicServerSessionBase* session = new QuicSimpleServerSession(
config(), connection, this, session_helper(), crypto_config(),
compressed_certs_cache(), quic_simple_server_backend_);
session->Initialize();
return session;
}
分析dispatcher中的处理逻辑。之后 framer_的处理逻辑请参考博文[1]。
//https://cs.chromium.org/chromium/src/net/third_party/quic/core/quic_dispatcher.cc?g=0&l=291
void QuicDispatcher::ProcessPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
current_self_address_ = self_address;
current_peer_address_ = peer_address;
// GetClientAddress must be called after current_peer_address_ is set.
current_client_address_ = GetClientAddress();
current_packet_ = &packet;
// ProcessPacket will cause the packet to be dispatched in
// OnUnauthenticatedPublicHeader, or sent to the time wait list manager
// in OnUnauthenticatedHeader.
framer_.ProcessPacket(packet);
// TODO(rjshade): Return a status describing if/why a packet was dropped,
// and log somehow. Maybe expose as a varz.
// TODO(wub): Consider invalidate the current_* variables so processing of the
// next packet does not use them incorrectly.
}
dispatch根据数据包中的connection_id信息,查找相应的session,调用session类中的ProcessUdpPacket函数。
void QuicSession::ProcessUdpPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
//connection_实在QuicSimpleDispatcher中随着session创建的
connection_->ProcessUdpPacket(self_address, peer_address, packet);
}
//https://cs.chromium.org/chromium/src/net/third_party/quic/core/quic_connection.cc?type=cs&g=0&l=1594
void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
//将数据包交给QuicFramer处理
if (!framer_.ProcessPacket(packet)){}
}
bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
rv = ProcessDataPacket(&reader, &header, packet, buffer, kMaxPacketSize);
}
bool QuicFramer::ProcessDataPacket(QuicDataReader* encrypted_reader,
QuicPacketHeader* header,
const QuicEncryptedPacket& packet,
char* decrypted_buffer,
size_t buffer_length)
{
if (!ProcessFrameData(&reader, *header)) {}
}
bool QuicFramer::ProcessFrameData(QuicDataReader* reader,
const QuicPacketHeader& header) {
//这里只是对数据进行了一些简单的读取,主要的处理逻辑在visitor_->OnStreamFrame中
if (!ProcessStreamFrame(reader, frame_type, &frame)) {
return RaiseError(QUIC_INVALID_STREAM_DATA);
}
//此处的visitor_在QuicConnection中注册framer_.set_visitor(this);
if (!visitor_->OnStreamFrame(frame)){}
}
bool QuicFramer::ProcessStreamFrame(QuicDataReader* reader,
uint8_t frame_type,
QuicStreamFrame* frame){
frame->data_buffer = data.data();
frame->data_length = static_cast(data.length());
}
visitor_->OnStreamFrame调用的就是QuicConnection中的OnStreamFrame函数。
bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame)
{
visitor_->OnStreamFrame(frame);
}
此处的visitor_创建QuicConnection时被注册。因此需要分析QuicSession中的OnStreamFrame函数。到这里,处理逻辑只是负责读取了数据包,没有考虑数据包的乱序情况。connection_中就需要处理数据包乱序的情况,当数据包有序的时候,负责通知上层处理。
void QuicSession::Initialize() {
connection_->set_visitor(this);
connection_->SetSessionNotifier(this);
connection_->SetDataProducer(this);
connection_->SetFromConfig(config_);
DCHECK_EQ(kCryptoStreamId, GetMutableCryptoStream()->id());
static_stream_map_[kCryptoStreamId] = GetMutableCryptoStream();
}
void QuicSession::OnStreamFrame(const QuicStreamFrame& frame)
{
//这里体现quic的多流复用的处理逻辑,就是同一个连接上,可以有多个数据流
QuicStreamId stream_id = frame.stream_id;
QuicStream* stream = GetOrCreateStream(stream_id);
stream->OnStreamFrame(frame);
}
void QuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
//考虑数据包的丢包,乱序,这里需要一个缓冲区,可以等效理解为tcp中recv buffer
sequencer_.OnStreamFrame(frame);
}
void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame)
{
stream_->OnDataAvailable();//有数据可以读,比如之前读取了数据包1,2,3,若5先到,则不可读,数据包4到达后,可以一块读取数据包4,5.
}
所以说,要使用quic传输数据,首先需要继承QuicSession,定义出满足自己业务需求的session。
接下来记录下,QuicStream的创建过程。
QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) {
StaticStreamMap::iterator it = static_stream_map_.find(stream_id);
if (it != static_stream_map_.end()) {
return it->second;
}
//map中没有查到,需要创建新的QuicStream
return GetOrCreateDynamicStream(stream_id);
}
QuicStream* QuicSession::GetOrCreateDynamicStream(
const QuicStreamId stream_id) {
return CreateIncomingDynamicStream(stream_id);
}
//CreateIncomingDynamicStream 虚函数,由继承了QuicStream的子类实现。
[1]How to Write a QUIC Endpoint Program