asio kcp代码走读

(1)kcp_client_wrap类

a 提供方法接口如下:
  1. send_msg
  2. kcp_client_.send_msg(msg);
  3. stop //等待工作线程退出
  4. set_event_callback
  5. connect //main函数中调用connect进行kcp client的初始化
  6. kcp_client_.connect_async
  7. do_asio_kcp_connect_loop
  8. connect_async
  9. connect_result
b 包含的私有方法:
  1. start_workthread
  2. client_event_callback_func
  3. handle_client_event_callback
  4. do_asio_kcp_connect_loop
  5. while(){kcp_client_.update();} //主线程中kcp的tick
  6. workthread_loop //工作线程入口函数
  7. do_workthread_loop
  8. while(){kcp_client_.update();} //工作线程中kcp的tick,kcp update时间间隔是KCP_UPDATE_INTERVAL 5ms
c 封装的成员变量有:
  1. kcp_client kcp_client_;
  2. 相关状态变量
d kcp client创建实例:
  1. asio_kcp::kcp_client_wrap net;
  2. Client client;
  3. net.set_event_callback(Client::client_event_callback, (void*)(&client));
  4. int ret = net.connect(0, "127.0.0.1", 32323);
  5. asio_kcp::millisecond_sleep(10);
  6. net.send_msg(std::string("1234567890"));
  7. asio_kcp::millisecond_sleep(510);

(2)kcp_client类

a 提供方法接口如下:
  1. connect_async
  2. init_udp_connect
  3. //设置udp连接的状态,in_connect_stage_置为true,获得当前时间作为connect_start_time_
  4. send_msg //主线程调用此函数将待发送消息push到发送队列中,供工作线程使用
  5. send_msg_queue_.push(msg);
  6. update
  7. do_asio_kcp_connect //向udp server发送“标志连接”的消息,并请求udp server端分配kcp conv
  8. //若udp连接成功 (connect_succeed_字段为true,当client的kcp初始化完成)
  9. do_send_msg_in_queue //一次全部取出发送队列中的消息,并调用ikcp_send,交给kcp模块进行处理
  10. while(){ikcp_send();}
  11. do_recv_udp_packet_in_loop
  12. recv
  13. handle_udp_packet
  14. is_disconnect_packet //是否断连消息
  15. ikcp_input //交给kcp将收到的udp消息由kcp消息格式转化为应用层消息格式
  16. while(1){recv_udp_package_from_kcp();}//
  17. //recv_udp_package_from_kcp()的内容:
  18. ikcp_recv //利用ikcp_recv获取经kcp解析(去掉头)后的应用层消息
  19. ikcp_update
  20. //距上次ts_flush超过一定阈值,则调用ikcp_flush,并更新kcp->ts_flush
  21. ikcp_flush //利用ikcp_output调用注册的回调函数将各类kcp消息(ack/数据/请求窗口/告知窗口)发送给udp server
b 包含的私有方法:
  1. init_udp_connect
  2. //servaddr_内容填充,udp服务端ip、监听端口号、
  3. socket //SOCK_DGRAM,建立udp socket,设置为非阻塞模式
  4. bind //填充struct sockaddr_in bind_addr,利用bind绑定本地端口号
  5. connect(udp_socket_, &servaddr_,...) //
  6. connect_timeout //kcp重连时间间隔 500ms
  7. need_send_connect_packet
  8. do_asio_kcp_connect
  9. do_send_connect_packet
  10. try_recv_connect_back_packet //
  11. udp_output //静态方法,在kcp模块中被调用,下面方法所属的应用层类实例指针为kcp_client*
  12. ((kcp_client*)user)->send_udp_package(buf, len); //
  13. send//利用应用层创建的udp网络连接将kcp封装后的udp消息发送出去
  14. send_udp_package
  15. send//利用应用层创建的udp网络连接将kcp封装后的udp消息发送出去
  16. do_send_connect_packet
  17. making_connect_packet //udp client向udp server发送特定“标志连接”的消息,用于udp服务端记录udp client信息
  18. send
  19. do_recv_udp_packet_in_loop
  20. do_send_msg_in_queue
  21. handle_udp_packet
  22. try_recv_connect_back_packet
  23. recv //等待接收“send_back_conv_packet”,消息体内容:"asio_kcp_connect_back_package get_conv:"
  24. grab_conv_from_send_back_conv_packet //从udp服务端接收到上面消息后,解析出消息中包含的conv字段值(udp server分配)
  25. init_kcp(conv);//利用服务端发来的conv来初始化client端的kcp
  26. //状态置位,in_connect_stage_置为false,connect_succeed_置为true
  27. init_kcp
  28. p_kcp_ = ikcp_create(conv, (void*)this); //创建kcp对象,并将应用层的上下文信息指针传递给kcp模块
  29. //注册应用层的udp发送消息的回调kcp_client::udp_output
  30. ikcp_nodelay(p_kcp_, 1, 2, 1, 1);//为kcp设置特定模式
c 封装的成员变量有:
  1. in_connect_stage_; //udp连接状态
  2. threadsafe_queue_mutex<std::string> send_msg_queue_;//线程安全的发送队列,主线程生产,工作线程消费
  3. int udp_port_bind_;//本地udp端口号
  4. std::string server_ip_;//udp服务器ip
  5. int server_port_;//udp服务器监听端口号
  6. int udp_socket_;//fd
  7. char udp_data_[1024 * 4];//缓存区
  8. ikcpcb* p_kcp_;//本client对应的kcp信息

(3)client_with_asio类,继承自: private boost::noncopyable基类 =========使用asio库的示例

a 提供方法接口如下:
b 包含的私有方法:
c 封装的成员变量有:
  1. asio_kcp::kcp_client kcp_client_;
  2. //几个定时器
  3. boost::asio::deadline_timer client_timer_;
  4. boost::asio::deadline_timer client_timer_send_msg_;
  5. //几个整数vector (包含接收包的间隔的vector)

总结:udp客户端的逻辑

整体,udp连接的建立过程,和,kcp的初始化和创建过程:

1 udp client的主线程

  1. connect //main函数中调用connect进行kcp client的初始化
  2. kcp_client_.connect_async
  3. init_udp_connect
  4. //servaddr_内容填充,udp服务端ip、监听端口号、
  5. socket //SOCK_DGRAM,建立udp socket,设置为非阻塞模式
  6. bind //填充struct sockaddr_in bind_addr,利用bind绑定本地端口号
  7. connect(udp_socket_, &servaddr_,...) //
  8. //设置udp连接的状态,in_connect_stage_置为true,获得当前时间作为connect_start_time_
  9. do_asio_kcp_connect_loop
  10. while(){kcp_client_.update();} //主线程中kcp的tick
  11. //kcp_client_.update()执行的操作
  12. do_asio_kcp_connect //向udp server发送“标志连接”的消息,并请求udp server端分配kcp conv
  13. //若udp连接成功 (connect_succeed_字段为true,当client的kcp初始化完成)
  14. do_send_msg_in_queue //一次全部取出发送队列中的消息,并调用ikcp_send,交给kcp模块进行处理
  15. while(){ikcp_send();}
  16. do_recv_udp_packet_in_loop
  17. recv
  18. handle_udp_packet
  19. is_disconnect_packet //是否断连消息
  20. ikcp_input //交给kcp将收到的udp消息由kcp消息格式转化为应用层消息格式
  21. while(1){recv_udp_package_from_kcp();}//
  22. //recv_udp_package_from_kcp()的内容:
  23. ikcp_recv //利用ikcp_recv获取经kcp解析(去掉头)后的应用层消息
  24. //while中调用recv_udp_package_from_kcp后,又调用下面函数
  25. (*pevent_func_)(p_kcp_->conv, eRcvMsg, msg, event_callback_var_); //pevent_func_在set_event_callback()中被注册,
  26. //这里的示例是Client::client_event_callback(上层业务处理函数,传入的参数列表是:{kcp conv,udp消息类型eEventType类型,消息内容,event_func_var_(上层注册时传递的指针)})
  27. //eEventType在udp client定义在kcp_client.hpp中
  28. ikcp_update
  29. //距上次ts_flush超过一定阈值,则调用ikcp_flush,并更新kcp->ts_flush
  30. ikcp_flush //利用ikcp_output调用注册的回调函数将各类kcp消息(ack/数据/请求窗口/告知窗口)发送给udp server
  31. start_workthread //创建工作线程

2 udp client的主线程

  1. workthread_loop
  2. do_workthread_loop
  3. while(){kcp_client_.update();} //工作线程中kcp的tick,kcp update时间间隔是KCP_UPDATE_INTERVAL 5ms

3 udp_output //静态方法,在kcp模块中被调用,下面方法所属的应用层类实例指针为kcp_client*

  1. ((kcp_client*)user)->send_udp_package(buf, len); //
  2. send//利用应用层创建的udp网络连接将kcp封装后的udp消息发送出去

udp服务端

(1)connection_manager类,udp server管理多个udp client的管理类,继承自private boost::noncopyable, public std::enable_shared_from_this<connection_manager>这两个基类,

a 提供方法接口如下:
  1. connection_manager //构造函数,在udp server的main函数中系统初始化时进行调用
  2. hook_udp_async_receive //为udp server对应的监听udp socket创建回调connection_manager::handle_udp_receive_from(利用boost::bind进行绑定),和udp接收缓存区,以及udp网元信息udp_remote_endpoint_
  3. //udp_socket_用boost::asio::io_service io_service, udp::endpoint(boost::asio::ip::address::from_string(address), udp_port)实参进行初始化
  4. hook_kcp_timer //创建kcp定时器kcp_timer_
  5. stop_all //断开所有udp client的连接
  6. force_disconnect
  7. call_event_callback_func //调用该函数,将特定消息:"server force disconnect"传递给上层注册的回掉函数event_callback_
  8. connections_.remove_connection //断开
  9. set_callback //将来自应用层上层的业务回调函数保存在该类的event_callback_成员变量中
  10. send_msg //从connections_中根据kcp conv找到对应udp client的连接指针的智能指针管理类connection_ptr,并调用send_kcp_msg
  11. connections_.find_by_conv
  12. connection::send_kcp_msg
  13. ikcp_send //调用kcp接口将应用层消息传递给kcp模块,交给kcp模块进行kcp头的封装和其他处理
  14. call_event_callback_func
  15. send_udp_packet
  16. get_cur_clock
b 包含的私有方法:
  1. handle_udp_receive_from
  2. is_connect_packet //调用公共库中函数判断是否连接包,消息内容是:"asio_kcp_connect_package get_conv"
  3. handle_connect_packet //若是连接包,则调用该函数,
  4. udp_socket_.send_to //向udp client发送连接包的响应消息:"asio_kcp_connect_back_package get_conv: %kcp conv"
  5. connections_.add_new_connection //并在此时,将udp client的网元信息保存在udp server中
  6. handle_kcp_packet //其他非连接包的udp client发来的包的处理
  7. ikcp_get_conv //从应用层收到的udp消息中解析出kcp conv
  8. connections_.find_by_conv //利用conv找到udp client的网元连接信息
  9. connection::input//获取当前时间戳,更新last_packet_recv_time_,并更新udp client的网元信息udp_remote_endpoint_;并调用kcp接口将应用层接收到的udp消息,交给kcp模块处理,
  10. ikcp_input //交给kcp将收到的udp消息由kcp消息格式转化为应用层消息格式
  11. //调用ikcp_recv从kcp模块获取解析后的应用层消息,并进行相关处理
  12. ikcp_recv
  13. hook_udp_async_receive
  14. handle_kcp_time //该函数是kcp定时器kcp_timer_的超时回调,每隔5ms调用一次
  15. hook_kcp_timer
  16. connections_.update_all_kcp(cur_clock_); //更新所有udp client的kcp tick
  17. hook_kcp_timer //设置kcp定时器的超时时间5ms和异步回调函数connection_manager::handle_kcp_time
  18. handle_connect_packet
  19. handle_kcp_packet
c 封装的成员变量有:
  1. stopped_ //udp server正常工作的标志,bool类型
  2. std::function<event_callback_t> event_callback_; //应用层回调
  3. udp::endpoint udp_remote_endpoint_; //udp server测的udp连接的网元信息
  4. udp::socket udp_socket_; //udp server测的udp socket信息
  5. char udp_data_[1024 * 32];//udp server测的udp应用层接收缓存区,udp_packet_max_length = 1080((576-8-20 - 8) * 2)
  6. connection_container connections_; //包含了各个udp client的kcp、udp连接网元等信息

(2)connection_container类,继承自private boost::noncopyable

a 提供方法接口如下:
  1. get_new_conv //udp server为多个udp client分配全局唯一kcp conv,从1000递增一个static全局变量
  2. update_all_kcp//遍历多个udp client的map,对每个调用下面函数
  3. connection_manager::update_kcp
  4. ikcp_update
  5. //距上次ts_flush超过一定阈值,则调用ikcp_flush,并更新kcp->ts_flush
  6. ikcp_flush //利用ikcp_output调用注册的回调函数将各类kcp消息(ack/数据/请求窗口/告知窗口)发送给udp server
  7. connection_manager::do_timeout //超时后将该udp client从map中删掉
  8. add_new_connection //利用传入的kcp conv和udp client信息udp::endpoint udp_sender_endpoint来本地维护多个udp client的信息
  9. connection::create //传入connection_manager指针manager_ptr、kcp conv、udp_sender_endpoint,将manager_ptr维护在智能指针中
  10. connection::init_kcp
  11. connection::set_udp_remote_endpoint
b 包含的私有方法:
c 封装的成员变量有:
  1. 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 提供方法接口如下:
  1. is_timeout
  2. do_timeout
  3. udp_output //kcp直接调用的应用层注册的回调
  4. ((connection*)user)->send_udp_package(buf, len);
  5. send_kcp_msg
  6. ikcp_send //调用kcp接口将应用层消息传递给kcp模块,交给kcp模块进行kcp头的封装和其他处理
  7. input //获取当前时间戳,更新last_packet_recv_time_,并更新udp client的网元信息udp_remote_endpoint_;并调用kcp接口将应用层接收到的udp消息,交给kcp模块处理,
  8. ikcp_input
  9. //调用ikcp_recv从kcp模块获取解析后的应用层消息,并进行相关处理
  10. ikcp_recv
  11. update_kcp
  12. ikcp_update
b 包含的私有方法:
  1. init_kcp //同udp client的init_kcp的实现
  2. ikcp_create
  3. //注册的回调函数是connection::udp_output
  4. ikcp_nodelay(p_kcp_, 1, 5, 1, 1);
  5. udp_output
  6. send_udp_package
  7. connection_manager.send_udp_packet
  8. get_cur_clock
  9. get_timeout_time
c 封装的成员变量有:
  1. std::weak_ptr<connection_manager> connection_manager_weak_ptr_;
  2. kcp_conv_t conv_; //本udp连接的标志:kcp conv
  3. ikcpcb* p_kcp_;
  4. udp::endpoint udp_remote_endpoint_; //udp client的网元信息
  5. uint32_t last_packet_recv_time_; //上次从udp client收到包的时间戳

(4)server类

a 提供方法接口如下:
  1. server //构造函数,
  2. //添加各个信号SIGINT、SIGTERM、SIGQUIT到signals_,并注册信号处理的回调函数server::handle_stop
  3. //注册应用层的正常事件回调server::event_callback
  4. connection_manager::set_callback //将来自应用层上层的业务回调函数server::event_callback保存在该类的event_callback_成员变量中
  5. run
  6. io_service_.run();//阻塞,直到所有的异步操作完成,
b 包含的私有方法:
  1. handle_stop
  2. event_callback //被调用的地方:connection_manager::call_event_callback_func
  3. //若消息类型是kcp_svr::eRcvMsg,则调用下面函数
  4. kcp_server_.send_msg(conv, msg);
  5. connection_manager::send_msg //从connections_中根据kcp conv找到对应udp client的连接指针的智能指针管理类connection_ptr,并调用send_kcp_msg
  6. connections_.find_by_conv
  7. connection::send_kcp_msg
  8. ikcp_send //调用kcp接口将应用层消息传递给kcp模块,交给kcp模块进行kcp头的封装和其他处理
  9. hook_test_timer
  10. handle_test_timer
  11. 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函数

  1. main
  2. server s(argv[1], argv[2]); //初始化udp server,构造函数,
  3. //添加各个信号SIGINT、SIGTERM、SIGQUIT到signals_,并注册信号处理的回调函数server::handle_stop
  4. //注册应用层的正常事件回调server::event_callback
  5. new connection_manager //调用构造函数connection_manager,在udp server的main函数中系统初始化时进行调用
  6. hook_udp_async_receive //为udp server对应的监听udp socket创建回调connection_manager::handle_udp_receive_from(利用boost::bind进行绑定),和udp接收缓存区,以及udp网元信息udp_remote_endpoint_
  7. //udp_socket_用boost::asio::io_service io_service, udp::endpoint(boost::asio::ip::address::from_string(address), udp_port)实参进行初始化
  8. hook_kcp_timer //创建kcp定时器kcp_timer_
  9. connection_manager::set_callback //将来自应用层上层的业务回调函数server::event_callback保存在该类的event_callback_成员变量中
  10. event_callback_=//保存在本地
  11. s.run();
  12. handle_udp_receive_from //当udp server socket收到消息时触发调用,从接收包中解析出kcp conv,进而判断是属于哪个udp client的消息
  13. is_connect_packet //调用公共库中函数判断是否连接包,消息内容是:"asio_kcp_connect_package get_conv"
  14. handle_connect_packet //若是连接包,则调用该函数,
  15. udp_socket_.send_to //向udp client发送连接包的响应消息:"asio_kcp_connect_back_package get_conv: %kcp conv"
  16. connections_.add_new_connection //并在此时,将udp client的网元信息保存在udp server中
  17. handle_kcp_packet //其他非连接包的udp client发来的包的处理
  18. ikcp_get_conv //从应用层收到的udp消息中解析出kcp conv
  19. connections_.find_by_conv //利用conv找到udp client的网元连接信息
  20. connection::input//获取当前时间戳,更新last_packet_recv_time_,并更新udp client的网元信息udp_remote_endpoint_;并调用kcp接口将应用层接收到的udp消息,交给kcp模块处理,
  21. ikcp_input //交给kcp将收到的udp消息由kcp消息格式转化为应用层消息格式
  22. //调用ikcp_recv从kcp模块获取解析后的应用层消息,并进行相关处理
  23. ikcp_recv
  24. connection_manager::call_event_callback_func //调用上层业务注册的回调,这里的示例是,
  25. event_callback_ //被调用的地方:connection_manager::call_event_callback_func
  26. //若消息类型是kcp_svr::eRcvMsg,则调用下面函数
  27. kcp_server_.send_msg(conv, msg);
  28. connection_manager::send_msg //从connections_中根据kcp conv找到对应udp client的连接指针的智能指针管理类connection_ptr,并调用send_kcp_msg
  29. connections_.find_by_conv
  30. connection::send_kcp_msg
  31. ikcp_send //调用kcp接口将应用层消息传递给kcp模块,交给kcp模块进行kcp头的封装和其他处理

在udp client和udp server之间的几种不同udp消息类型:

  1. 服务端定义在kcp_typedef.hpp /server_lib/
  2. enum eEventType
  3. {
  4. eConnect,
  5. eDisconnect,
  6. eRcvMsg,
  7. eLagNotify,
  8. eCountOfEventType
  9. };

asio kcp源码分析的更多相关文章

  1. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  2. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  3. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  4. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  5. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  6. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  7. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  8. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

  9. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

随机推荐

  1. 100天搞定机器学习|Day9-12 支持向量机

    机器学习100天|Day1数据预处理 100天搞定机器学习|Day2简单线性回归分析 100天搞定机器学习|Day3多元线性回归 100天搞定机器学习|Day4-6 逻辑回归 100天搞定机器学习|D ...

  2. ubuntu防火墙规则之ufw

    前言 因公司项目的需求,需要对客户端机器简便使用防火墙的功能,所以可在页面进行简便设置防护墙规则,当然,这个功能需求放到我手上我才有机会学到.因为客户端机器都是ubuntu的,所以当然用了ubuntu ...

  3. Maven中央仓库发布历程

    一.前言 最近自己在学习Spring boot的过程中开发了一个组件 multithreadpool-spring-boot-starter,通过这个组件,我们可以动态根据配置文件进行多个线程池的初始 ...

  4. git删除分支步骤

    在本地删除一个分支: git branch -D <本地分支> 在github远程端删除一个分支: git push origin :<远程端分支> 唯一不同的就是冒号代表了删 ...

  5. java流压缩图片

    整理文档,搜刮出一个Java做图片压缩的代码,稍微整理精简一下做下分享.首先,要压缩的图片格式不能说动态图片,你可以使用bmp.png.gif等,至于压缩质量,可以通过BufferedImage来指定 ...

  6. Linux - 查看端口的占用情况、找出并杀死占用进程的方法

    目录 1 lsof查看端口的占用情况 1.1 命令使用示例 1.2 查看某一端口的占用情况 1.3 杀死某个端口的所有进程 2 netstat查看端口占用情况 2.1 命令使用示例 2.2 查看占用某 ...

  7. Java Grammer:数据类型

    Java的数据类型 我们知道,Java是一种强类型语言,类型对于Java语言来说非常的重要不言而喻,在Java中,分为基础数据类型和引用数据类型,其中基础数据类型分为了四类八种: 下面,我们来分别说一 ...

  8. 如何保证FPGA PCIe唤醒能满足PC的100ms 的时间要求(Autonomous Mode)?

    原创By DeeZeng [ Intel FPGA笔记 ]  PC 需要PCIe设备在 100ms 内启动,这样PC 才能扫描到PCIe 设备.对于 FPGA PCIe 板卡,同样也需要满足这个时间要 ...

  9. ABAP_增强点查找

    *&---------------------------------------------------------------------* *& Report Z_FIND_EN ...

  10. 《机器学习技法》---对偶SVM

    1.对偶问题的推导 为什么要求解对偶问题?一是对偶问题往往更容易求解,二是可以自然的引入核函数. 1.1 用拉格朗日函数将原问题转化为“无约束”等价问题 原问题是: 写出它的拉格朗日函数: 然后我们的 ...