asio-kcp源碼分析

  • 2019 年 10 月 3 日
  • 筆記

asio kcp代碼走讀

(1)kcp_client_wrap類

a 提供方法接口如下:
    send_msg          kcp_client_.send_msg(msg);      stop //等待工作線程退出      set_event_callback        connect //main函數中調用connect進行kcp client的初始化          kcp_client_.connect_async          do_asio_kcp_connect_loop        connect_async      connect_result
b 包含的私有方法:
start_workthread  client_event_callback_func  handle_client_event_callback    do_asio_kcp_connect_loop      while(){kcp_client_.update();} //主線程中kcp的tick    workthread_loop //工作線程入口函數    do_workthread_loop      while(){kcp_client_.update();} //工作線程中kcp的tick,kcp update時間間隔是KCP_UPDATE_INTERVAL 5ms      
c 封裝的成員變量有:
kcp_client kcp_client_;  相關狀態變量
d kcp client創建實例:
asio_kcp::kcp_client_wrap net;  Client client;  net.set_event_callback(Client::client_event_callback, (void*)(&client));    int ret = net.connect(0, "127.0.0.1", 32323);  asio_kcp::millisecond_sleep(10);    net.send_msg(std::string("1234567890"));  asio_kcp::millisecond_sleep(510);

(2)kcp_client類

a 提供方法接口如下:
connect_async      init_udp_connect      //設置udp連接的狀態,in_connect_stage_置為true,獲得當前時間作為connect_start_time_    send_msg //主線程調用此函數將待發送消息push到發送隊列中,供工作線程使用      send_msg_queue_.push(msg);    update      do_asio_kcp_connect //向udp server發送「標誌連接」的消息,並請求udp server端分配kcp conv        //若udp連接成功 (connect_succeed_字段為true,當client的kcp初始化完成)      do_send_msg_in_queue //一次全部取出發送隊列中的消息,並調用ikcp_send,交給kcp模塊進行處理          while(){ikcp_send();}        do_recv_udp_packet_in_loop          recv          handle_udp_packet              is_disconnect_packet //是否斷連消息              ikcp_input //交給kcp將收到的udp消息由kcp消息格式轉化為應用層消息格式              while(1){recv_udp_package_from_kcp();}//                  //recv_udp_package_from_kcp()的內容:                  ikcp_recv //利用ikcp_recv獲取經kcp解析(去掉頭)後的應用層消息        ikcp_update          //距上次ts_flush超過一定閾值,則調用ikcp_flush,並更新kcp->ts_flush          ikcp_flush //利用ikcp_output調用註冊的回調函數將各類kcp消息(ack/數據/請求窗口/告知窗口)發送給udp server      
b 包含的私有方法:
init_udp_connect      //servaddr_內容填充,udp服務端ip、監聽端口號、      socket  //SOCK_DGRAM,建立udp socket,設置為非阻塞模式      bind    //填充struct sockaddr_in bind_addr,利用bind綁定本地端口號      connect(udp_socket_, &servaddr_,...) //    connect_timeout //kcp重連時間間隔 500ms  need_send_connect_packet  do_asio_kcp_connect      do_send_connect_packet      try_recv_connect_back_packet //      udp_output //靜態方法,在kcp模塊中被調用,下面方法所屬的應用層類實例指針為kcp_client*      ((kcp_client*)user)->send_udp_package(buf, len); //          send//利用應用層創建的udp網絡連接將kcp封裝後的udp消息發送出去    send_udp_package      send//利用應用層創建的udp網絡連接將kcp封裝後的udp消息發送出去    do_send_connect_packet      making_connect_packet //udp client向udp server發送特定「標誌連接」的消息,用於udp服務端記錄udp client信息          send  do_recv_udp_packet_in_loop    do_send_msg_in_queue    handle_udp_packet    try_recv_connect_back_packet      recv //等待接收「send_back_conv_packet」,消息體內容:"asio_kcp_connect_back_package get_conv:"      grab_conv_from_send_back_conv_packet //從udp服務端接收到上面消息後,解析出消息中包含的conv字段值(udp server分配)      init_kcp(conv);//利用服務端發來的conv來初始化client端的kcp      //狀態置位,in_connect_stage_置為false,connect_succeed_置為true    init_kcp      p_kcp_ = ikcp_create(conv, (void*)this); //創建kcp對象,並將應用層的上下文信息指針傳遞給kcp模塊      //註冊應用層的udp發送消息的回調kcp_client::udp_output      ikcp_nodelay(p_kcp_, 1, 2, 1, 1);//為kcp設置特定模式
c 封裝的成員變量有:
in_connect_stage_; //udp連接狀態    threadsafe_queue_mutex<std::string> send_msg_queue_;//線程安全的發送隊列,主線程生產,工作線程消費  int udp_port_bind_;//本地udp端口號  std::string server_ip_;//udp服務器ip  int server_port_;//udp服務器監聽端口號  int udp_socket_;//fd  char udp_data_[1024 * 4];//緩存區    ikcpcb* p_kcp_;//本client對應的kcp信息

(3)client_with_asio類,繼承自: private boost::noncopyable基類 =========使用asio庫的示例

a 提供方法接口如下:
b 包含的私有方法:
c 封裝的成員變量有:
asio_kcp::kcp_client kcp_client_;  //幾個定時器  boost::asio::deadline_timer client_timer_;  boost::asio::deadline_timer client_timer_send_msg_;    //幾個整數vector (包含接收包的間隔的vector)

總結:udp客戶端的邏輯

整體,udp連接的建立過程,和,kcp的初始化和創建過程:

1 udp client的主線程

connect //main函數中調用connect進行kcp client的初始化        kcp_client_.connect_async            init_udp_connect              //servaddr_內容填充,udp服務端ip、監聽端口號、              socket  //SOCK_DGRAM,建立udp socket,設置為非阻塞模式              bind    //填充struct sockaddr_in bind_addr,利用bind綁定本地端口號              connect(udp_socket_, &servaddr_,...) //            //設置udp連接的狀態,in_connect_stage_置為true,獲得當前時間作為connect_start_time_        do_asio_kcp_connect_loop          while(){kcp_client_.update();} //主線程中kcp的tick              //kcp_client_.update()執行的操作                do_asio_kcp_connect //向udp server發送「標誌連接」的消息,並請求udp server端分配kcp conv                //若udp連接成功 (connect_succeed_字段為true,當client的kcp初始化完成)              do_send_msg_in_queue //一次全部取出發送隊列中的消息,並調用ikcp_send,交給kcp模塊進行處理                  while(){ikcp_send();}                do_recv_udp_packet_in_loop                  recv                  handle_udp_packet                      is_disconnect_packet //是否斷連消息                      ikcp_input //交給kcp將收到的udp消息由kcp消息格式轉化為應用層消息格式                      while(1){recv_udp_package_from_kcp();}//                          //recv_udp_package_from_kcp()的內容:                          ikcp_recv //利用ikcp_recv獲取經kcp解析(去掉頭)後的應用層消息                            //while中調用recv_udp_package_from_kcp後,又調用下面函數                          (*pevent_func_)(p_kcp_->conv, eRcvMsg, msg, event_callback_var_); //pevent_func_在set_event_callback()中被註冊,                          //這裡的示例是Client::client_event_callback(上層業務處理函數,傳入的參數列表是:{kcp conv,udp消息類型eEventType類型,消息內容,event_func_var_(上層註冊時傳遞的指針)})                          //eEventType在udp client定義在kcp_client.hpp中                    ikcp_update                  //距上次ts_flush超過一定閾值,則調用ikcp_flush,並更新kcp->ts_flush                  ikcp_flush //利用ikcp_output調用註冊的回調函數將各類kcp消息(ack/數據/請求窗口/告知窗口)發送給udp server        start_workthread //創建工作線程

2 udp client的主線程

workthread_loop      do_workthread_loop          while(){kcp_client_.update();} //工作線程中kcp的tick,kcp update時間間隔是KCP_UPDATE_INTERVAL 5ms          

3 udp_output //靜態方法,在kcp模塊中被調用,下面方法所屬的應用層類實例指針為kcp_client
((kcp_client
)user)->send_udp_package(buf, len); //

        send//利用應用層創建的udp網絡連接將kcp封裝後的udp消息發送出去              

udp服務端

(1)connection_manager類,udp server管理多個udp client的管理類,繼承自private boost::noncopyable, public std::enable_shared_from_this這兩個基類,

a 提供方法接口如下:
connection_manager //構造函數,在udp server的main函數中系統初始化時進行調用        hook_udp_async_receive //為udp server對應的監聽udp socket創建回調connection_manager::handle_udp_receive_from(利用boost::bind進行綁定),和udp接收緩存區,以及udp網元信息udp_remote_endpoint_      //udp_socket_用boost::asio::io_service io_service, udp::endpoint(boost::asio::ip::address::from_string(address), udp_port)實參進行初始化      hook_kcp_timer //創建kcp定時器kcp_timer_    stop_all //斷開所有udp client的連接  force_disconnect      call_event_callback_func //調用該函數,將特定消息:"server force disconnect"傳遞給上層註冊的回掉函數event_callback_      connections_.remove_connection //斷開      set_callback //將來自應用層上層的業務回調函數保存在該類的event_callback_成員變量中    send_msg //從connections_中根據kcp conv找到對應udp client的連接指針的智能指針管理類connection_ptr,並調用send_kcp_msg      connections_.find_by_conv      connection::send_kcp_msg          ikcp_send //調用kcp接口將應用層消息傳遞給kcp模塊,交給kcp模塊進行kcp頭的封裝和其他處理    call_event_callback_func    send_udp_packet    get_cur_clock      
b 包含的私有方法:
handle_udp_receive_from      is_connect_packet //調用公共庫中函數判斷是否連接包,消息內容是:"asio_kcp_connect_package get_conv"      handle_connect_packet //若是連接包,則調用該函數,          udp_socket_.send_to //向udp client發送連接包的響應消息:"asio_kcp_connect_back_package get_conv: %kcp conv"            connections_.add_new_connection //並在此時,將udp client的網元信息保存在udp server中        handle_kcp_packet //其他非連接包的udp client發來的包的處理          ikcp_get_conv //從應用層收到的udp消息中解析出kcp conv          connections_.find_by_conv //利用conv找到udp client的網元連接信息            connection::input//獲取當前時間戳,更新last_packet_recv_time_,並更新udp client的網元信息udp_remote_endpoint_;並調用kcp接口將應用層接收到的udp消息,交給kcp模塊處理,                ikcp_input  //交給kcp將收到的udp消息由kcp消息格式轉化為應用層消息格式                //調用ikcp_recv從kcp模塊獲取解析後的應用層消息,並進行相關處理              ikcp_recv    hook_udp_async_receive    handle_kcp_time //該函數是kcp定時器kcp_timer_的超時回調,每隔5ms調用一次      hook_kcp_timer      connections_.update_all_kcp(cur_clock_); //更新所有udp client的kcp tick    hook_kcp_timer //設置kcp定時器的超時時間5ms和異步回調函數connection_manager::handle_kcp_time    handle_connect_packet    handle_kcp_packet
c 封裝的成員變量有:
stopped_ //udp server正常工作的標誌,bool類型  std::function<event_callback_t> event_callback_; //應用層回調    udp::endpoint udp_remote_endpoint_;  //udp server測的udp連接的網元信息  udp::socket udp_socket_; //udp server測的udp socket信息    char udp_data_[1024 * 32];//udp server測的udp應用層接收緩存區,udp_packet_max_length = 1080((576-8-20 - 8) * 2)    connection_container connections_; //包含了各個udp client的kcp、udp連接網元等信息

(2)connection_container類,繼承自private boost::noncopyable

a 提供方法接口如下:
get_new_conv //udp server為多個udp client分配全局唯一kcp conv,從1000遞增一個static全局變量    update_all_kcp//遍歷多個udp client的map,對每個調用下面函數      connection_manager::update_kcp          ikcp_update              //距上次ts_flush超過一定閾值,則調用ikcp_flush,並更新kcp->ts_flush              ikcp_flush //利用ikcp_output調用註冊的回調函數將各類kcp消息(ack/數據/請求窗口/告知窗口)發送給udp server        connection_manager::do_timeout //超時後將該udp client從map中刪掉    add_new_connection //利用傳入的kcp conv和udp client信息udp::endpoint udp_sender_endpoint來本地維護多個udp client的信息      connection::create //傳入connection_manager指針manager_ptr、kcp conv、udp_sender_endpoint,將manager_ptr維護在智能指針中          connection::init_kcp          connection::set_udp_remote_endpoint
b 包含的私有方法:
c 封裝的成員變量有:
std::unordered_map<kcp_conv_t, connection::shared_ptr> connections_; //每個kcp標誌(conv)和每個udp client連接之間的哈希map

(3)connection類,udp server用於管理每一個udp client的信息,繼承自private boost::noncopyable

a 提供方法接口如下:
is_timeout  do_timeout    udp_output //kcp直接調用的應用層註冊的回調      ((connection*)user)->send_udp_package(buf, len);    send_kcp_msg      ikcp_send //調用kcp接口將應用層消息傳遞給kcp模塊,交給kcp模塊進行kcp頭的封裝和其他處理    input //獲取當前時間戳,更新last_packet_recv_time_,並更新udp client的網元信息udp_remote_endpoint_;並調用kcp接口將應用層接收到的udp消息,交給kcp模塊處理,      ikcp_input        //調用ikcp_recv從kcp模塊獲取解析後的應用層消息,並進行相關處理      ikcp_recv    update_kcp      ikcp_update
b 包含的私有方法:
init_kcp //同udp client的init_kcp的實現      ikcp_create      //註冊的回調函數是connection::udp_output      ikcp_nodelay(p_kcp_, 1, 5, 1, 1);    udp_output  send_udp_package      connection_manager.send_udp_packet    get_cur_clock  get_timeout_time
c 封裝的成員變量有:
std::weak_ptr<connection_manager> connection_manager_weak_ptr_;  kcp_conv_t conv_; //本udp連接的標誌:kcp conv  ikcpcb* p_kcp_;  udp::endpoint udp_remote_endpoint_; //udp client的網元信息  uint32_t last_packet_recv_time_; //上次從udp client收到包的時間戳

(4)server類

a 提供方法接口如下:
server //構造函數,  //添加各個信號SIGINT、SIGTERM、SIGQUIT到signals_,並註冊信號處理的回調函數server::handle_stop  //註冊應用層的正常事件回調server::event_callback      connection_manager::set_callback //將來自應用層上層的業務回調函數server::event_callback保存在該類的event_callback_成員變量中    run      io_service_.run();//阻塞,直到所有的異步操作完成,
b 包含的私有方法:
handle_stop  event_callback //被調用的地方:connection_manager::call_event_callback_func      //若消息類型是kcp_svr::eRcvMsg,則調用下面函數      kcp_server_.send_msg(conv, msg);          connection_manager::send_msg //從connections_中根據kcp conv找到對應udp client的連接指針的智能指針管理類connection_ptr,並調用send_kcp_msg              connections_.find_by_conv              connection::send_kcp_msg                  ikcp_send //調用kcp接口將應用層消息傳遞給kcp模塊,交給kcp模塊進行kcp頭的封裝和其他處理  hook_test_timer  handle_test_timer  test_force_disconnect
c 封裝的成員變量有:

boost::asio::io_service io_service_; //用作執行異步操作

boost::asio::signal_set signals_;//用於註冊各種進程終止通知函數

bool stopped_;

kcp_svr::server kcp_server_; //管理了各個udp client連接信息的udp+kcp管理類

boost::asio::deadline_timer test_timer_;

udp server端的示例main函數

main     server s(argv[1], argv[2]); //初始化udp server,構造函數,      //添加各個信號SIGINT、SIGTERM、SIGQUIT到signals_,並註冊信號處理的回調函數server::handle_stop      //註冊應用層的正常事件回調server::event_callback        new connection_manager //調用構造函數connection_manager,在udp server的main函數中系統初始化時進行調用            hook_udp_async_receive //為udp server對應的監聽udp socket創建回調connection_manager::handle_udp_receive_from(利用boost::bind進行綁定),和udp接收緩存區,以及udp網元信息udp_remote_endpoint_          //udp_socket_用boost::asio::io_service io_service, udp::endpoint(boost::asio::ip::address::from_string(address), udp_port)實參進行初始化          hook_kcp_timer //創建kcp定時器kcp_timer_        connection_manager::set_callback //將來自應用層上層的業務回調函數server::event_callback保存在該類的event_callback_成員變量中          event_callback_=//保存在本地       s.run();        handle_udp_receive_from //當udp server socket收到消息時觸發調用,從接收包中解析出kcp conv,進而判斷是屬於哪個udp client的消息            is_connect_packet //調用公共庫中函數判斷是否連接包,消息內容是:"asio_kcp_connect_package get_conv"          handle_connect_packet //若是連接包,則調用該函數,              udp_socket_.send_to //向udp client發送連接包的響應消息:"asio_kcp_connect_back_package get_conv: %kcp conv"                connections_.add_new_connection //並在此時,將udp client的網元信息保存在udp server中            handle_kcp_packet //其他非連接包的udp client發來的包的處理              ikcp_get_conv //從應用層收到的udp消息中解析出kcp conv              connections_.find_by_conv //利用conv找到udp client的網元連接信息                connection::input//獲取當前時間戳,更新last_packet_recv_time_,並更新udp client的網元信息udp_remote_endpoint_;並調用kcp接口將應用層接收到的udp消息,交給kcp模塊處理,                    ikcp_input  //交給kcp將收到的udp消息由kcp消息格式轉化為應用層消息格式                    //調用ikcp_recv從kcp模塊獲取解析後的應用層消息,並進行相關處理                  ikcp_recv                    connection_manager::call_event_callback_func //調用上層業務註冊的回調,這裡的示例是,                      event_callback_ //被調用的地方:connection_manager::call_event_callback_func                            //若消息類型是kcp_svr::eRcvMsg,則調用下面函數                          kcp_server_.send_msg(conv, msg);                              connection_manager::send_msg //從connections_中根據kcp conv找到對應udp client的連接指針的智能指針管理類connection_ptr,並調用send_kcp_msg                                  connections_.find_by_conv                                  connection::send_kcp_msg                                      ikcp_send //調用kcp接口將應用層消息傳遞給kcp模塊,交給kcp模塊進行kcp頭的封裝和其他處理      

在udp client和udp server之間的幾種不同udp消息類型:

服務端定義在kcp_typedef.hpp中 /server_lib/      enum eEventType      {          eConnect,          eDisconnect,          eRcvMsg,          eLagNotify,            eCountOfEventType      };