采用UDP的echo服務(wù)器示例

同步UDP客戶端

UDP是面向無連接的,使用起來比較簡(jiǎn)單,打開socke之后,指定目標(biāo)端口,直接進(jìn)行接收和發(fā)送:

void test_udp_echo_client()
{
    try
    {
        io_service io;
        udp::endpoint remote_ep(ip::address_v4::from_string("127.0.0.1"), 1024);

        udp::socket socket(io);
        socket.open(udp::v4());

        char line[1024];
        while (std::cin.getline(line, 1024))
        {
            socket.send_to(boost::asio::buffer(line, std::strlen(line)),remote_ep);
            auto size = socket.receive_from(boost::asio::buffer(line),remote_ep);
            std::cout.write(line,size);
        }
        socket.close();
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
}

socket本身提供了一些接口:

  • socket.send_to 同步發(fā)送接口
  • socket.receive_from 同步接收接口
    Boost.Asio也有一些接口用來進(jìn)行發(fā)送和接收,可以參見后續(xù)的發(fā)送/接收函數(shù)組;

需要注意的是,boost.asio.buffer是一種接口適配器,通過接口進(jìn)行發(fā)送和接收,必須有對(duì)應(yīng)的數(shù)據(jù)緩沖區(qū)提供數(shù)據(jù)或者存儲(chǔ)空間。

同步UDP服務(wù)器

同步接收同步發(fā)送的UDP服務(wù)器也比較簡(jiǎn)單,創(chuàng)建一個(gè)綁定到本地端口的socket,然后就是接收及發(fā)送動(dòng)作:

void test_udp_echo_server()
{
    try
    {
        io_service io;
        ip::udp::socket socket(io, udp::endpoint(udp::v4(), 1024));
        for (;;)
        {
            std::array<char,1024> recv_buf;
            ip::udp::endpoint remote_socket;
            boost::system::error_code error;

            //同步接收
            auto size = socket.receive_from(boost::asio::buffer(recv_buf),remote_socket,0,error);
            if (error && error!= boost::asio::error::message_size)
            {
                throw boost::system::system_error(error);
            }
            std::cout.write(recv_buf.data(),size);
            //發(fā)送回去
            socket.send_to(boost::asio::buffer(recv_buf,size),remote_socket);
        }
    }
    catch (std::exception& e)
    {
        std::cerr<<e.what()<<std::endl;
    }
}

同步操作是不需要運(yùn)行IO服務(wù)的,以最常規(guī)的方式來進(jìn)行發(fā)送和接收,注意接收時(shí)如果接收到全部消息,即EOF也是通過報(bào)錯(cuò)形式,錯(cuò)誤碼為error::message_size

異步UDP服務(wù)器的實(shí)現(xiàn)問題

實(shí)現(xiàn)異步的UDP服務(wù)器就略顯復(fù)雜,需要保證IO服務(wù)運(yùn)行,發(fā)起異步操作時(shí)要注意數(shù)據(jù)緩沖區(qū)生命周期:

  1. 啟動(dòng)IO服務(wù)
    啟動(dòng)IO服務(wù)可以直接執(zhí)行io_service.run,由于IO服務(wù)的多線程安全特性,也可以啟動(dòng)線程來執(zhí)行,譬如:
boost::asio::io_service io_;
std::thread task([&](){ io_.run();});
task.detach();
  1. 停止IO服務(wù)
    停止IO服務(wù)可以直接執(zhí)行io_service.stop,會(huì)立即從運(yùn)行狀態(tài)退出,直到reset之后才能重新啟動(dòng)。

  2. 保證IO服務(wù)執(zhí)行
    IO服務(wù)的run方法只有在有異步操作未完成的時(shí)候才能一直運(yùn)行,一旦沒有異步操作就會(huì)退出,因而需要在run之前保證有異步操作發(fā)起,在過程中不斷發(fā)起異步操作就能夠保證IO服務(wù)一直運(yùn)行。

  3. 數(shù)據(jù)緩沖區(qū)生命周期
    發(fā)起異步操作后,會(huì)立即退出,但是異步操作并沒有執(zhí)行,這就要求提供的數(shù)據(jù)緩沖區(qū)生命周期要足夠長(zhǎng),存活到異步操作執(zhí)行完,即在完成回調(diào)中再釋放數(shù)據(jù)緩沖區(qū),通常可以采用智能指針或者new出來的對(duì)象。

異步UDP服務(wù)器實(shí)現(xiàn)

class async_udp_echo_server
{
public:
    async_udp_echo_server()
        :socket_(io_,udp::endpoint(udp::v4(),1024))
    {
        do_recv();

        std::thread task([&](){ io_.run();});
        task.detach();
    }

    void do_recv()
    {
        //保證發(fā)送完成之前一直有效
        char* recv_buf = new char[1024];

        socket_.async_receive_from(boost::asio::buffer(recv_buf,1024), remote_ep_,
            [recv_buf, this](const boost::system::error_code& error,std::size_t bytes_transferred){
            if (!error || error == boost::asio::error::message_size)
            {
                do_send(recv_buf,bytes_transferred, std::move(remote_ep_));
            }
            else
            {
                std::cout << error.message() << "\n";
            }
            do_recv();
        });
    }

    void do_send(char* send_buf,std::size_t size,udp::endpoint ep)
    {
        socket_.async_send_to(boost::asio::buffer(send_buf,size),ep,
            [send_buf](const boost::system::error_code& error, std::size_t bytes_transferred){
            if (!error)
            {
                std::cout<<"echo finished\n";
            }
            delete[] send_buf;
        });
    }

    void stop()
    {
        io_.stop();
    }
    ~async_udp_echo_server()
    {
        stop();
    }
private:
    boost::asio::io_service io_;
    udp::socket socket_;
    udp::endpoint remote_ep_;
};

可以看到do_recv方法發(fā)起了一個(gè)異步接收操作,在操作完成回調(diào)中再次發(fā)起,構(gòu)造服務(wù)器時(shí)率先調(diào)用了do_recv,從而保證IO服務(wù)一直運(yùn)行。

do_recv方法在發(fā)起異步操作前申請(qǐng)了一塊內(nèi)存,接收的內(nèi)容被保存在這塊內(nèi)存之中,當(dāng)do_send發(fā)起異步發(fā)送操作時(shí)被借用,直到發(fā)送完成才將這段內(nèi)存釋放掉。

在構(gòu)造函數(shù)中啟動(dòng)了一個(gè)線程來執(zhí)行IO服務(wù),并detach掉線程,從而保證服務(wù)器不阻塞,在析構(gòu)函數(shù)停止了IO服務(wù)。

需要注意到的是remote_ep_在執(zhí)行do_send時(shí)被move了,由于remote_ep_標(biāo)識(shí)了遠(yuǎn)程端口,而且被聲明為成員變量,在接受操作中會(huì)被填充遠(yuǎn)程端口內(nèi)容,如果多個(gè)遠(yuǎn)程主機(jī)同時(shí)發(fā)起,單個(gè)remote_ep_是無法正常處理的,所以一旦內(nèi)容被填充后,就會(huì)轉(zhuǎn)移出去給發(fā)送操作使用[個(gè)人理解,沒有實(shí)際測(cè)試和驗(yàn)證]。

使用方法

async_udp_echo_server server_;

char line[1024];
while (std::cin.getline(line, 1024)){
     if(line[0] == 'Q')
          break;
};
server_.stop();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,635評(píng)論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,128評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,365評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,733評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,935評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,172評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評(píng)論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,595評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,908評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容