九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

ASIO—下一代C++标准可能接纳的网络库(3)UDP网络应用


ASIO—下一代C++标准可能接纳的网络库(3UDP网络应用

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

一、   综述

接着前面

ASIO—下一代C++标准可能接纳的网络库(1)简单的应用

ASIO—下一代C++标准可能接纳的网络库(2TCP网络应用

继续,讲了简单应用,讲了TCP,自然而然是UDP了。其实个人感觉UDPTCP的接口假如经过封装是可以做到接口比较一致的,但是遗憾的是asio没有遵循这样的接口设计方案。

      

二、   Tutorial

1.      Daytime.4 - A synchronous UDP daytime client(同步UDP daytime客户端)

还是先看看普通的socket API的情况:

#include <stdio.h>

#include <string.h>

#include "Winsock2.h"

#include "errno.h"

#include "stdlib.h"

 

#define MAXLINE 1000

void str_cli(SOCKET sockfd, const struct sockaddr* pservaddr, int servlen)

{

    int n;

    char recvline[MAXLINE] = {0};

    char sendline[2] = {0};

    sendto(sockfd, sendline, 2, 0, pservaddr, servlen);

 

    n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);

    recvline[n] = 0;

    printf("%s", recvline);

}

 

 

int main(int argc, char **argv)

{

    WORD wVersionRequested = 0;

    WSADATA wsaData;

    int err;

 

    wVersionRequested = MAKEWORD( 2, 2 );

 

    // windows下此初始化为必须,实际是初始化WinsockDLL的过程

    err = WSAStartup( wVersionRequested, &wsaData );

    if ( err != 0 ) {

       return -1;

    }

    SOCKET               sockfd;

    struct sockaddr_in   servaddr;

 

    if (argc != 2)

    {

       printf("usage: tcpcli <IPaddress>");

       exit(1);

    }

 

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

 

    ZeroMemory(&servaddr, sizeof(servaddr));

    servaddr.sin_family = AF_INET;

    servaddr.sin_port = htons(13);

    servaddr.sin_addr.s_addr = inet_addr(argv[1]);

 

    str_cli(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr));

 

    system("pause");

    WSACleanup();

    exit(0);

}

 

相对来说,假如用了socket API,会发现UDP的程序编写逻辑是比TCP要简单的,起码省略了connect的过程,但是难就难在当网络情况不好时UDP程序的处理。这么简单的程序不再加更多说明了。

看看asio的例子:

#include <iostream>

#include <boost/array.hpp>

#include <boost/asio.hpp>

using boost::asio::ip::udp;

int main(int argc, char* argv[])

{

    try

    {

       if (argc != 2)

       {

           std::cerr << "Usage: client <host>" << std::endl;

           return 1;

       }

       boost::asio::io_service io_service;

       udp::resolver resolver(io_service);

       udp::resolver::query query(udp::v4(), argv[1], "daytime");

       udp::endpoint receiver_endpoint = *resolver.resolve(query);

       udp::socket socket(io_service);

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

       boost::array<char, 1> send_buf = { 0 };

       socket.send_to(boost::asio::buffer(send_buf), receiver_endpoint);

       boost::array<char, 128> recv_buf;

       udp::endpoint sender_endpoint;

       size_t len = socket.receive_from(

           boost::asio::buffer(recv_buf), sender_endpoint);

       std::cout.write(recv_buf.data(), len);

    }

    catch (std::exception& e)

    {

       std::cerr << e.what() << std::endl;

    }

    return 0;

}

 

甚至没有感觉到有任何简化。一大堆的resolver(为了适应ipv6),一大堆的array,其实并不优雅,在很简单的程序中,会发现,似乎asio是简单的为socket进行了非常浅的封装一样,你还得了解一大堆本来可以不了解的东西,asio内在的高效率,异步啊,用的那些模式啊,都看不到。。。。。。。。。。呵呵, 也许socket API本来就是Make the simple things simple吧,而asio就是为了应付绝对复杂的情况而做出相对复杂设计的吧。这样的例子没有任何说服力能让人放弃socket API而使用asio。。。。。。。不知道asio的文档中加入这些干啥。。。仅仅为了说明?-_-!

 

2.      A synchronous UDP daytime server(同步的UDP daytime服务器)

还是先来个原始的socket API写的版本:

#include <time.h>

#include "Winsock2.h"

#include "errno.h"

#include "stdlib.h"

 

#define MAXLINE 1000

void str_svr(SOCKET sockfd, struct sockaddr* pcliaddr, int clilen)

{

    int n = 0;

    time_t ticks = 0;

    int len;

    char recvline[2] = {0};

    char sendline[MAXLINE] = {0};

    for(;;)

    {

       len = clilen;

       if( (n = recvfrom(sockfd, recvline, 2, 0, pcliaddr, &len)) == INVALID_SOCKET)

       {

            printf("recvfrom failed: %d/n", WSAGetLastError());

            return;

       }

 

       ticks = time(NULL);

       _snprintf(sendline, sizeof(sendline), "%.24s/r/n", ctime(&ticks));

 

       sendto(sockfd, sendline, strlen(sendline), 0, pcliaddr, len);

    }

}

 

 

int main(int argc, char **argv)

{

    WORD wVersionRequested = 0;

    WSADATA wsaData;

    int err;

 

    wVersionRequested = MAKEWORD( 2, 2 );

 

    // windows下此初始化为必须,实际是初始化WinsockDLL的过程

    err = WSAStartup( wVersionRequested, &wsaData );

    if ( err != 0 ) {

       return -1;

    }

 

    SOCKET               sockfd;

    struct sockaddr_in   servaddr,cliaddr;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

 

    ZeroMemory(&servaddr, sizeof(servaddr));

    servaddr.sin_family      = AF_INET;

    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    servaddr.sin_port        = htons(13);  /* daytime server */

 

    if( bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))

       == SOCKET_ERROR)

    {

       printf("bind failed: %d/n", WSAGetLastError());

       closesocket(sockfd);

       WSACleanup();

       return 1;

    }

 

    str_svr(sockfd, (struct sockaddr*)&cliaddr, sizeof(cliaddr));

 

    closesocket(sockfd);

    WSACleanup();

    return 1;

}

 

与上篇文章中tcp服务器的例子很像,基本上来说,用socket写客户端还是相对简单一些,但是写个服务器就相对要复杂很多,这个例子还没有精细的判断每个返回值(比如send函数),但是已经比较复杂了。

接着是asio版本:

 

#include <ctime>

#include <iostream>

#include <string>

#include <boost/array.hpp>

#include <boost/asio.hpp>

using boost::asio::ip::udp;

std::string make_daytime_string()

{

    using namespace std; // For time_t, time and ctime;

    time_t now = time(0);

    return ctime(&now);

}

int main()

{

    try

    {

       boost::asio::io_service io_service;

       udp::socket socket(io_service, udp::endpoint(udp::v4(), 13));

       for (;;)

       {

           boost::array<char, 1> recv_buf;

           udp::endpoint remote_endpoint;

           boost::system::error_code error;

           socket.receive_from(boost::asio::buffer(recv_buf),

              remote_endpoint, 0, error);

           if (error && error != boost::asio::error::message_size)

              throw boost::system::system_error(error);

           std::string message = make_daytime_string();

           boost::system::error_code ignored_error;

           socket.send_to(boost::asio::buffer(message),

              remote_endpoint, 0, ignored_error);

       }

    }

    catch (std::exception& e)

    {

       std::cerr << e.what() << std::endl;

    }

    return 0;

}

 

我甚至觉得在asio中写服务器比写客户端更加简单-_-!也许一开始asio就是为了写高性能服务器而设计的,所以导致写客户端相对那么麻烦,但是写服务器却又简单很多吧。不过,熟悉socket API对于使用asio也是有意义的,比如这里的receive_from,send_to不过是对应的socket API函数换汤不换药的版本而已,使用起来除了参数传递方式上的变化,最后效果一致。

 

3.      An asynchronous UDP daytime server(异步 UDP daytime 服务器)

又是有点意思了的程序了,asio的命名就是表示异步的io,所以展现异步的程序才能体现asio的实力及其简化了底层操作的本事。TCP的应用是这样,这里也不例外。

不过出于UDP应用相对于TCP应用本身的简单性,所以这个示例程序比对应的TCP版本就要简化很多,只是,不知道asioUDP实现了更丰富的UDP特性没有,比如超时重发等机制。。。。

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

分类:  网络技术 
标签:  ASIO  Boost  C++ 

Posted By 九天雁翎 at 九天雁翎的博客 on 2009年06月09日

前一篇: ASIO—下一代C++标准可能接纳的网络库(2)TCP网络应用 后一篇: 《数据结构与算法分析C++描述》 分离链接(separate chaining)哈希表的C++实现