`
webcenterol
  • 浏览: 913812 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

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

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

讨论新闻组及文件

一、 综述

接着前面

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) -- blog.csdn.net/vagrxie

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics