電子發(fā)燒友App

硬聲App

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示
創(chuàng)作
電子發(fā)燒友網(wǎng)>電子資料下載>電子資料>基于asio的網(wǎng)絡(luò)通信框架asio2

基于asio的網(wǎng)絡(luò)通信框架asio2

2022-06-22 | zip | 20.50 MB | 次下載 | 2積分

資料介紹

授權(quán)協(xié)議 GPL
開發(fā)語言 C/C++
操作系統(tǒng) 跨平臺(tái)
軟件類型 開源軟件
所屬分類 程序開發(fā)、 高性能網(wǎng)絡(luò)開發(fā)庫

軟件簡(jiǎn)介

?

開源基于asio的網(wǎng)絡(luò)通信框架asio2,支持TCP,UDP,HTTP,RPC,SSL,跨平臺(tái),支持可靠UDP,支持TCP自動(dòng)拆包,TCP數(shù)據(jù)報(bào)模式等?

C++開發(fā)網(wǎng)絡(luò)通信程序時(shí)用asio是個(gè)不錯(cuò)的選擇,但asio本身是一套函數(shù)集,自己還要處理諸如“通信線程池管理、連接及生命周期管理、多線程收發(fā)數(shù)據(jù)的同步保護(hù)等”。因此這里對(duì)asio進(jìn)行了一層封裝,大大簡(jiǎn)化了對(duì)asio的使用。代碼使用了C++17相關(guān)功能,所以只能用在C++17以上。

其中http和websocket部分用的是boost::beast,因此如果需要用到http或websocket的功能,則必須使用boost庫,如果用不到http則直接使用獨(dú)立的asio即可。在config.hpp中通過對(duì)ASIO_STANDALONE這個(gè)宏定義的開關(guān),即可設(shè)置是使用boost::asio還是使用asio standalone.

代碼大量使用了CRTP模板編程實(shí)現(xiàn)(沒有使用virtual而用CRTP實(shí)現(xiàn)的靜態(tài)多態(tài)),因此編譯比較耗時(shí),但執(zhí)行效率相對(duì)較好一點(diǎn)。


github地址https://github.com/zhllxt/asio2

碼云地址https://gitee.com/zhllxt/asio2

A open source cross-platform c++ library for network programming based on asio,support for tcp,udp,http,rpc,ssl and so on.

  • 支持TCP,UDP,HTTP,WEBSOCKET,RPC,ICMP,SERIAL_PORT等;
  • 支持可靠UDP(基于KCP),支持SSL,支持從內(nèi)存字符串加載SSL證書;
  • TCP支持?jǐn)?shù)據(jù)拆包功能(按指定的分隔符對(duì)數(shù)據(jù)自動(dòng)進(jìn)行拆包,保證用戶收到的數(shù)據(jù)是一個(gè)完整的數(shù)據(jù)包);實(shí)現(xiàn)了TCP的數(shù)據(jù)報(bào)模式(類似WEBSOCKET);
  • 支持windows,linux,32位,64位;
  • 依賴asio(boost::asio或獨(dú)立asio均可,若需要HTTP功能必須使用boost::asio),依賴C++17;
  • 代碼采用hpp頭文件方式,以源碼級(jí)鏈入,無需編譯,只需在工程的Include包含目錄中添加asio2路徑,然后在源碼中#include 包含頭文件即可;
  • demo目錄包含大量的示例工程(工程基于VS2017創(chuàng)建),各種使用方法請(qǐng)參考示例代碼;

TCP:

服務(wù)端:

asio2::tcp_server server;
server.bind_recv([&server](std::shared_ptr & session_ptr, std::string_view s)
{
????session_ptr->no_delay(true);
????printf("recv : %u %.*s\n", (unsigned)s.size(), (int)s.size(), s.data());
????// 異步發(fā)送(所有發(fā)送操作都是異步且線程安全的)
    session_ptr->send(s);
    // 發(fā)送時(shí)指定一個(gè)回調(diào)函數(shù),當(dāng)發(fā)送完成后會(huì)調(diào)用此回調(diào)函數(shù),bytes_sent表示實(shí)際發(fā)送的字節(jié)數(shù),
    // 發(fā)送是否有錯(cuò)誤可以用asio2::get_last_error()函數(shù)來獲取錯(cuò)誤碼
    // session_ptr->send(s, [](std::size_t bytes_sent) {});
}).bind_connect([&server](auto & session_ptr)
{
????printf("client enter : %s %u %s %u\n",
????session_ptr->remote_address().c_str(), session_ptr->remote_port(),
????????session_ptr->local_address().c_str(), session_ptr->local_port());
????// 可以用session_ptr這個(gè)會(huì)話啟動(dòng)一個(gè)定時(shí)器,這個(gè)定時(shí)器是在這個(gè)session_ptr會(huì)話的數(shù)據(jù)收
????// 發(fā)線程中執(zhí)行的,這對(duì)于連接狀態(tài)的判斷或其它需求很有用(尤其在UDP這種無連接的協(xié)議中,有
????// 時(shí)需要在數(shù)據(jù)處理過程中使用一個(gè)定時(shí)器來延時(shí)做某些操作,而且這個(gè)定時(shí)器還需要和數(shù)據(jù)處理
????// 在同一個(gè)線程中安全觸發(fā))
????//session_ptr->start_timer(1, std::chrono::seconds(1), []() {});
}).bind_disconnect([&server](auto & session_ptr)
{
	printf("client leave : %s %u %s\n",
		session_ptr->remote_address().c_str(),
		session_ptr->remote_port(), asio2::last_error_msg().c_str());
});
server.start("0.0.0.0", "8080");
//server.start("0.0.0.0", "8080", '\n'); // 按\n自動(dòng)拆包(可以指定任意字符)
//server.start("0.0.0.0", "8080", "\r\n"); // 按\r\n自動(dòng)拆包(可以指定任意字符串)
//server.start("0.0.0.0", "8080", match_role('#')); // 按match_role指定的規(guī)則自動(dòng)拆包(match_role請(qǐng)參考demo代碼)(用于對(duì)用戶自定義的協(xié)議拆包)
//server.start("0.0.0.0", "8080", asio::transfer_exactly(100)); // 每次接收固定的100字節(jié)
//server.start("0.0.0.0", "8080", asio2::use_dgram); // 數(shù)據(jù)報(bào)模式的TCP,無論發(fā)送多長的數(shù)據(jù),雙方接收的一定是相應(yīng)長度的整包數(shù)據(jù)

客戶端:

asio2::tcp_client client;
client.bind_connect([&](asio::error_code ec)
{
	if (asio2::get_last_error())
		printf("connect failure : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
	else
		printf("connect success : %s %u\n", client.local_address().c_str(), client.local_port());

	client.send("");
}).bind_disconnect([](asio::error_code ec)
{
	printf("disconnect : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
}).bind_recv([&](std::string_view sv)
{
	printf("recv : %u %.*s\n", (unsigned)sv.size(), (int)sv.size(), sv.data());

	client.send(sv);
})
	//.bind_recv(on_recv) // 綁定全局函數(shù)
	//.bind_recv(std::bind(&listener::on_recv, &lis, std::placeholders::_1)) // 綁定成員函數(shù)(具體請(qǐng)查看demo代碼)
	//.bind_recv(&listener::on_recv, lis) // 按lis對(duì)象的引用來綁定成員函數(shù)(具體請(qǐng)查看demo代碼)
	//.bind_recv(&listener::on_recv, &lis) // 按lis對(duì)象的指針來綁定成員函數(shù)(具體請(qǐng)查看demo代碼)
	;
client.async_start("0.0.0.0", "8080"); // 異步連接服務(wù)端
//client.start("0.0.0.0", "8080"); // 同步連接服務(wù)端
//client.async_start("0.0.0.0", "8080", '\n'); // 按\n自動(dòng)拆包(可以指定任意字符)
//client.async_start("0.0.0.0", "8080", "\r\n"); // 按\r\n自動(dòng)拆包(可以指定任意字符串)
//client.async_start("0.0.0.0", "8080", match_role); // 按match_role指定的規(guī)則自動(dòng)拆包(match_role請(qǐng)參考demo代碼)(用于對(duì)用戶自定義的協(xié)議拆包)
//client.async_start("0.0.0.0", "8080", asio::transfer_exactly(100)); // 每次接收固定的100字節(jié)
//client.start("0.0.0.0", "8080", asio2::use_dgram); // 數(shù)據(jù)報(bào)模式的TCP,無論發(fā)送多長的數(shù)據(jù),雙方接收的一定是相應(yīng)長度的整包數(shù)據(jù)
// 發(fā)送時(shí)也可以指定use_future參數(shù),然后通過返回值future來阻塞等待直到發(fā)送完成,發(fā)送結(jié)果的錯(cuò)誤碼和發(fā)送字節(jié)數(shù)
// 保存在返回值future中(注意,不能在通信線程中用future去等待,這會(huì)阻塞通信線程進(jìn)而導(dǎo)致死鎖)
// std::future> future = client.send("abc", asio::use_future); 

UDP:

服務(wù)端:

asio2::udp_server server;
// ... 綁定監(jiān)聽器(請(qǐng)查看demo代碼)
server.start("0.0.0.0", "8080"); // 常規(guī)UDP
//server.start("0.0.0.0", "8080", asio2::use_kcp); // 可靠UDP

客戶端:

asio2::udp_client client;
// ... 綁定監(jiān)聽器(請(qǐng)查看demo代碼)
client.start("0.0.0.0", "8080");
//client.async_start("0.0.0.0", "8080", asio2::use_kcp); // 可靠UDP

RPC:

服務(wù)端:

asio2::rpc_server server;
// ... 綁定監(jiān)聽器(請(qǐng)查看demo代碼)
A a; // A的定義請(qǐng)查看demo代碼
server.bind("add", add); // 綁定RPC全局函數(shù)
server.bind("mul", &A::mul, a); // 綁定RPC成員函數(shù)
server.bind("cat", [&](const std::string& a, const std::string& b) { return a + b; }); // 綁定lambda表達(dá)式
server.bind("get_user", &A::get_user, a); // 綁定成員函數(shù)(按引用)
server.bind("del_user", &A::del_user, &a); // 綁定成員函數(shù)(按指針)
//server.start("0.0.0.0", "8080", asio2::use_dgram); // 使用TCP數(shù)據(jù)報(bào)模式作為RPC通信底層支撐,啟動(dòng)服務(wù)端時(shí)必須要使用use_dgram參數(shù)
server.start("0.0.0.0", "8080"); // 使用websocket作為RPC通信底層支撐(需要到rcp_server.hpp文件末尾代碼中選擇使用websocket)

客戶端:

asio2::rpc_client client;
// ... 綁定監(jiān)聽器(請(qǐng)查看demo代碼)
//client.start("0.0.0.0", "8080", asio2::use_dgram); // 使用TCP數(shù)據(jù)報(bào)模式作為RPC通信底層支撐,啟動(dòng)服務(wù)端時(shí)必須要使用use_dgram參數(shù)
client.start("0.0.0.0", "8080"); // 使用websocket作為RPC通信底層支撐
asio::error_code ec;
// 同步調(diào)用RPC函數(shù)
int sum = client.call(ec, std::chrono::seconds(3), "add", 11, 2);
printf("sum : %d err : %d %s\n", sum, ec.value(), ec.message().c_str());
// 異步調(diào)用RPC函數(shù),第一個(gè)參數(shù)是回調(diào)函數(shù),當(dāng)調(diào)用完成或超時(shí)會(huì)自動(dòng)調(diào)用該回調(diào)函數(shù),如果超時(shí)或其它錯(cuò)誤,
// 錯(cuò)誤碼保存在ec中,這里async_call沒有指定返回值類型,則lambda表達(dá)式的第二個(gè)參數(shù)必須要指定類型
client.async_call([](asio::error_code ec, int v)
{
	printf("sum : %d err : %d %s\n", v, ec.value(), ec.message().c_str());
}, "add", 10, 20);
// 這里async_call指定了返回值類型,則lambda表達(dá)式的第二個(gè)參數(shù)可以為auto類型
client.async_call([](asio::error_code ec, auto v)
{
	printf("sum : %d err : %d %s\n", v, ec.value(), ec.message().c_str());
}, "add", 12, 21);
// 返回值為用戶自定義數(shù)據(jù)類型(user類型的定義請(qǐng)查看demo代碼)
user u = client.call(ec, "get_user");
printf("%s %d ", u.name.c_str(), u.age);
for (auto &[k, v] : u.purview)
{
	printf("%d %s ", k, v.c_str());
}
printf("\n");

u.name = "hanmeimei";
u.age = ((int)time(nullptr)) % 100;
u.purview = { {10,"get"},{20,"set"} };
// 如果RPC函數(shù)的返回值為void,則用戶回調(diào)函數(shù)只有一個(gè)參數(shù)即可
client.async_call([](asio::error_code ec)
{
}, "del_user", std::move(u));

HTTP:

服務(wù)端:

asio2::http_server server;
server.bind_recv([&](std::shared_ptr & session_ptr, http::request& req)
{
    // 在收到http請(qǐng)求時(shí)嘗試發(fā)送一個(gè)文件到對(duì)端
    {
        // 如果請(qǐng)求是非法的,直接發(fā)送錯(cuò)誤信息到對(duì)端并返回
        if (req.target().empty() ||
        req.target()[0] != '/' ||
        req.target().find("..") != beast::string_view::npos)
        {
            session_ptr->send(http::make_response(http::status::bad_request, "Illegal request-target"));
            session_ptr->stop(); // 同時(shí)直接斷開這個(gè)連接
            return;
        }

        // Build the path to the requested file
        std::string path(req.target().data(), req.target().size());
        path.insert(0, std::filesystem::current_path().string());
        if (req.target().back() == '/')
            path.append("index.html");

        // 打開文件
        beast::error_code ec;
        http::file_body::value_type body;
        body.open(path.c_str(), beast::file_mode::scan, ec);

        // 如果打開文件失敗,直接發(fā)送錯(cuò)誤信息到對(duì)端并直接返回
        if (ec == beast::errc::no_such_file_or_directory)
        {
            session_ptr->send(http::make_response(http::status::not_found,
            std::string_view{ req.target().data(), req.target().size() }));
            return;
        }

        // Cache the size since we need it after the move
        auto const size = body.size();

        // 生成一個(gè)文件形式的http響應(yīng)對(duì)象,然后發(fā)送給對(duì)端
        http::response res{
        std::piecewise_construct,
        std::make_tuple(std::move(body)),
        std::make_tuple(http::status::ok, req.version()) };
        res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
        res.set(http::field::content_type, http::extension_to_mimetype(path));
        res.content_length(size);
        res.keep_alive(req.keep_alive()); 
        res.chunked(true);
        // Specify a callback function when sending
        //session_ptr->send(std::move(res));
        session_ptr->send(std::move(res), [&res](std::size_t bytes_sent)
        {
            auto opened = res.body().is_open(); std::ignore = opened;
            auto err = asio2::get_last_error(); std::ignore = err;
        });
        //session_ptr->send(std::move(res), asio::use_future);
        return;
    }

    std::cout << req << std::endl;
    if (true)
    {
        // 用make_response生成一個(gè)http響應(yīng)對(duì)象,狀態(tài)碼200表示操作成功,"suceess"是HTTP消息的body部分內(nèi)容
        auto rep = http::make_response(200, "suceess");
        session_ptr->send(rep, []()
        {
            auto err = asio2::get_last_error(); std::ignore = err;
        });
    }
    else
    {
        // 也可以直接發(fā)送一個(gè)http標(biāo)準(zhǔn)響應(yīng)字符串,內(nèi)部會(huì)將這個(gè)字符串自動(dòng)轉(zhuǎn)換為http響應(yīng)對(duì)象再發(fā)送出去
        std::string_view rep =
            "HTTP/1.1 404 Not Found\r\n"\
            "Server: Boost.Beast/181\r\n"\
            "Content-Length: 7\r\n"\
            "\r\n"\
            "failure";
        // test send string sequence, the string will automatically parsed into a standard http request
        session_ptr->send(rep, [](std::size_t bytes_sent)
        {
            auto err = asio2::get_last_error(); std::ignore = err;
        });
    }
});
server.start(host, port);

客戶端:

asio2::error_code ec;
auto req1 = http::make_request("http://www.baidu.com/get_user?name=a"); // 通過URL字符串生成一個(gè)http請(qǐng)求對(duì)象
auto req2 = http::make_request("GET / HTTP/1.1\r\nHost: 127.0.0.1:8443\r\n\r\n"); // 通過http協(xié)議字符串生成一個(gè)http請(qǐng)求對(duì)象
req2.set(http::field::timeout, 5000); // 給請(qǐng)求設(shè)置一個(gè)超時(shí)時(shí)間
auto rep1 = asio2::http_client::execute("http://www.baidu.com/get_user?name=a", ec); // 通過URL字符串直接請(qǐng)求某個(gè)網(wǎng)址,返回結(jié)果在rep1中,如果有錯(cuò)誤,錯(cuò)誤碼保存在ec中
auto rep2 = asio2::http_client::execute("127.0.0.1", "8080", req2); // 通過IP端口以及前面生成的req2請(qǐng)求對(duì)象來發(fā)送一個(gè)http請(qǐng)求
std::cout << rep2 << std::endl; // 顯示http請(qǐng)求結(jié)果
std::stringstream ss;
ss << rep2;
std::string result = ss.str(); // 通過這種方式將http請(qǐng)求結(jié)果轉(zhuǎn)換為字符串

 

其它的HTTP使用方式以及WEBSOCKET使用方式請(qǐng)參考demo代碼

?

ICMP:

class ping_test // 模擬在一個(gè)類對(duì)象中使用ping組件(其它所有如TCP/UDP/HTTP等組件一樣可以在類對(duì)象中使用)
{
    asio2::ping ping;
public:
    ping_test() : ping(10) // 構(gòu)造函數(shù)傳入的10表示只ping 10次后就結(jié)束,傳入-1表示一直ping
    {
        ping.timeout(std::chrono::seconds(3)); // 設(shè)置ping超時(shí)
        ping.interval(std::chrono::seconds(1)); // 設(shè)置ping間隔
        ping.body("0123456789abcdefghijklmnopqrstovuxyz");
        ping.bind_recv(&ping_test::on_recv, this) // 綁定當(dāng)前這個(gè)類的成員函數(shù)作為監(jiān)聽器
            .bind_start(std::bind(&ping_test::on_start, this, std::placeholders::_1)) // 也是綁定成員函數(shù)
            .bind_stop([this](asio::error_code ec) { this->on_stop(ec); }); // 綁定lambda
    }
    void on_recv(asio2::icmp_rep& rep)
    {
        if (rep.lag.count() == -1) // 如果延時(shí)的值等于-1表示超時(shí)了
            std::cout << "request timed out" << std::endl;
        else
            std::cout << rep.total_length() - rep.header_length()
                << " bytes from " << rep.source_address()
                << ": icmp_seq=" << rep.sequence_number()
                << ", ttl=" << rep.time_to_live()
                << ", ms"
                << std::endl;
    }
    void on_start(asio::error_code ec)
    {
        printf("start : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
    }
    void on_stop(asio::error_code ec)
    {
        printf("stop : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
    }
    void run()
    {
        if (!ping.start("127.0.0.1"))
        //if (!ping.start("123.45.67.89"))
        //if (!ping.start("stackoverflow.com"))
            printf("start failure : %s\n", asio2::last_error_msg().c_str());
        while (std::getchar() != '\n');
        ping.stop();
        // ping結(jié)束后可以輸出統(tǒng)計(jì)信息,包括丟包率,平均延時(shí)時(shí)長等
        printf("loss rate : %.0lf%% average time : %lldms\n", ping.plp(),
            std::chrono::duration_cast(ping.avg_lag()).count());
    }
};

?

SSL:?

TCP/HTTP/WEBSOCKET均支持SSL功能(需要在config.hpp中將#define ASIO2_USE_SSL宏定義放開)

asio2::tcps_server server;
// 從內(nèi)存字符串加載SSL證書(具體請(qǐng)查看demo代碼)
server.set_cert("test", cer, key, dh); // cer,key,dh這三個(gè)字符串的定義請(qǐng)查看demo代碼
// 從文件加載SSL證書
//server.set_cert_file("test", "server.crt", "server.key", "dh512.pem");

  

TCP/HTTP/WEBSOCKET服務(wù)端、客戶端等SSL功能請(qǐng)到DEMO代碼中查看。

  

串口:

請(qǐng)查看demo示例代碼serial port 部分

?

其它:

定時(shí)器

// 框架中提供了定時(shí)器功能,使用非常簡(jiǎn)單,如下:
asio2::timer timer;
// 參數(shù)1表示定時(shí)器ID,參數(shù)2表示定時(shí)器間隔,參數(shù)3為定時(shí)器回調(diào)函數(shù)
timer.start_timer(1, std::chrono::seconds(1), [&]()
{
    printf("timer 1\n");
    if (true) // 滿足某個(gè)條件時(shí)關(guān)閉定時(shí)器,當(dāng)然也可以在其它任意地方關(guān)閉定時(shí)器
        timer.stop_timer(1);
});

  

還有其它一些輔助類的功能,請(qǐng)?jiān)谠创a或使用中去體會(huì)吧.

?

?

下載該資料的人也在下載 下載該資料的人還在閱讀
更多 >

評(píng)論

查看更多

下載排行

本周

  1. 1山景DSP芯片AP8248A2數(shù)據(jù)手冊(cè)
  2. 1.06 MB  |  532次下載  |  免費(fèi)
  3. 2RK3399完整板原理圖(支持平板,盒子VR)
  4. 3.28 MB  |  339次下載  |  免費(fèi)
  5. 3TC358743XBG評(píng)估板參考手冊(cè)
  6. 1.36 MB  |  330次下載  |  免費(fèi)
  7. 4DFM軟件使用教程
  8. 0.84 MB  |  295次下載  |  免費(fèi)
  9. 5元宇宙深度解析—未來的未來-風(fēng)口還是泡沫
  10. 6.40 MB  |  227次下載  |  免費(fèi)
  11. 6迪文DGUS開發(fā)指南
  12. 31.67 MB  |  194次下載  |  免費(fèi)
  13. 7元宇宙底層硬件系列報(bào)告
  14. 13.42 MB  |  182次下載  |  免費(fèi)
  15. 8FP5207XR-G1中文應(yīng)用手冊(cè)
  16. 1.09 MB  |  178次下載  |  免費(fèi)

本月

  1. 1OrCAD10.5下載OrCAD10.5中文版軟件
  2. 0.00 MB  |  234315次下載  |  免費(fèi)
  3. 2555集成電路應(yīng)用800例(新編版)
  4. 0.00 MB  |  33566次下載  |  免費(fèi)
  5. 3接口電路圖大全
  6. 未知  |  30323次下載  |  免費(fèi)
  7. 4開關(guān)電源設(shè)計(jì)實(shí)例指南
  8. 未知  |  21549次下載  |  免費(fèi)
  9. 5電氣工程師手冊(cè)免費(fèi)下載(新編第二版pdf電子書)
  10. 0.00 MB  |  15349次下載  |  免費(fèi)
  11. 6數(shù)字電路基礎(chǔ)pdf(下載)
  12. 未知  |  13750次下載  |  免費(fèi)
  13. 7電子制作實(shí)例集錦 下載
  14. 未知  |  8113次下載  |  免費(fèi)
  15. 8《LED驅(qū)動(dòng)電路設(shè)計(jì)》 溫德爾著
  16. 0.00 MB  |  6656次下載  |  免費(fèi)

總榜

  1. 1matlab軟件下載入口
  2. 未知  |  935054次下載  |  免費(fèi)
  3. 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
  4. 78.1 MB  |  537798次下載  |  免費(fèi)
  5. 3MATLAB 7.1 下載 (含軟件介紹)
  6. 未知  |  420027次下載  |  免費(fèi)
  7. 4OrCAD10.5下載OrCAD10.5中文版軟件
  8. 0.00 MB  |  234315次下載  |  免費(fèi)
  9. 5Altium DXP2002下載入口
  10. 未知  |  233046次下載  |  免費(fèi)
  11. 6電路仿真軟件multisim 10.0免費(fèi)下載
  12. 340992  |  191187次下載  |  免費(fèi)
  13. 7十天學(xué)會(huì)AVR單片機(jī)與C語言視頻教程 下載
  14. 158M  |  183279次下載  |  免費(fèi)
  15. 8proe5.0野火版下載(中文版免費(fèi)下載)
  16. 未知  |  138040次下載  |  免費(fèi)