2009年3月23日星期一

Asio 网络库

  搜索: Search Search Titles Search Full Text
 
 
 
 
更多操作 源码  打印视图  输出Docbook格式  删除缓存  ------------  拼写检查  相似网页  本站地图  ------------  改名  删除  ------------  我的网页  订阅  ------------  删除垃圾广告  网页打包  ------------  Visual Site Map 
 
BOOST 绝对实用手册(写作中!!!!!!!)
 
目录
 
1.      序言
2.      编译:VC2005注意
3.      Asio 网络库
 
2.      同步Timer
3.      异步Timer
 
 
张沈鹏     电子科技大学大三        生物医学工程
 
 
更新:2006.10 beta
 
参考:BOOST文档
 
  • -- 欢迎转载,但请保留引用网址以获得更新
 
 
1. 序言
 
现在学的东西很容易忘记,写这篇文章的目的是能让我在需要时快速找回当时的感觉. Let's BOOST THE WORLD .
 
 
2. 编译:VC2005注意
 
在 属性->C/C++->预处理器->预处理定义 中加入
 
_CRT_SECURE_NO_DEPRECATE;
来屏蔽不必要的警告
 
 
3. Asio 网络库
 
Boost.Asio是利用当代C++的先进方法,跨平台,异步I/O模型的C++网络库.
 
 
3.1. 网络库:VC2005注意
 
在 属性->C/C++->命令行 中加入
 
-DBOOST_REGEX_NO_LIB
来防止自动连接.
 
 
3.2. 同步Timer
 
本章介绍asio如何在定时器上进行阻塞等待(blocking wait).
 
实现,我们包含必要的头文件.
 
所有的asio类可以简单的通过include "asio.hpp"来调用.
 
#include <iostream>
#include <boost/asio.hpp>
此外,这个示例用到了timer,我们还要包含Boost.Date_Time的头文件来控制时间.
 
#include <boost/date_time/posix_time/posix_time.hpp>
使用asio至少需要一个boost::asio::io_service对象.该类提供了访问I/O的功能.我们首先在main函数中声明它.
 
int main()
{
boost::asio::io_service io;
下 一步我们声明boost::asio::deadline_timer对象.这个asio的核心类提供I/O的功能(这里更确切的说是定时功能),总是把 一个io_service对象作为他的第一个构造函数,而第二个构造函数的参数设定timer会在5秒后到时(expired).
 
boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
这个简单的示例中我们演示了定时器上的一个阻塞等待.就是说,调用boost::asio::deadline_timer::wait()的在创建后5秒内(注意:不是等待开始后),timer到时之前不会返回任何值.
 
一个deadline_timer只有两种状态:到时,未到时.
 
如果boost::asio::deadline_timer::wait()在到时的timer对象上调用,会立即return.
 
t.wait();
最后,我们输出理所当然的"Hello, world!"来演示timer到时了.
 
std::cout << "Hello, world!\n";

return 0;
}
完整的代码:
 
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

int main()
{
boost::asio::io_service io;

boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
t.wait();

std::cout << "Hello, world!\n";

return 0;
}
 
 
3.3. 异步Timer
 
 
#include <iostream>
#include <asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
asio的异步函数会在一个异步操作完成后被回调.这里我们定义了一个将被回调的函数.
 
void print(const asio::error& /*e*/)
{
  std::cout << "Hello, world!\n";
}

int main()
{
  asio::io_service io;

  asio::deadline_timer t(io, boost::posix_time::seconds(5));
这里我们调用asio::deadline_timer::async_wait()来异步等待
 
  t.async_wait(print);
最后,我们必须调用asio::io_service::run().
 
asio库只会调用那个正在运行的asio::io_service::run()的回调函数.
 
如果asio::io_service::run()不被调用,那么回调永远不会发生.
 
asio::io_service::run()会持续工作到点,这里就是timer到时,回调完成.
 
别 忘了在调用 asio::io_service::run()之前设置好io_service的任务.比如,这里,如果我们忘记先调用 asio::deadline_timer::async_wait()则asio::io_service::run()会在瞬间return.
 
  io.run();

  return 0;
}
完整的代码:
 
#include <iostream>
#include <asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

void print(const asio::error& /*e*/)
{
  std::cout << "Hello, world!\n";
}

int main()
{
  asio::io_service io;

  asio::deadline_timer t(io, boost::posix_time::seconds(5));
  t.async_wait(print);

  io.run();

  return 0;
}
 
 
3.4. 回调函数的参数
 
这里我们将每秒回调一次,来演示如何回调函数参数的含义
 
#include <iostream>
#include <asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
首先,调整一下timer的持续时间,开始一个异步等待.显示,回调函数需要访问timer来实现周期运行,所以我们再介绍两个新参数
 
  • 指向timer的指针
  • 一个int*来指向计数器
 
void print(const asio::error& /*e*/,
    asio::deadline_timer* t, int* count)
{
我 们打算让这个函数运行6个周期,然而你会发现这里没有显式的方法来终止io_service.不过,回顾上一节,你会发现当 asio::io_service::run()会在所有任务完成时终止.这样我们当计算器的值达到5时(0为第一次运行的值),不再开启一个新的异步等 待就可以了.
 
  if (*count < 5)
  {
    std::cout << *count << "\n";
    ++(*count);
然后,我们推迟的timer的终止时间.通过在原先的终止时间上增加延时,我们可以确保timer不会在处理回调函数所需时间内的到期.
 
(原 文:By calculating the new expiry time relative to the old, we can ensure that the timer does not drift away from the whole-second mark due to any delays in processing the handler.)
 
    t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
然后我们开始一个新的同步等待.如您所见,我们用把print和他的多个参数用boost::bind函数合成一个的形为void(const asio::error&)回调函数(准确的说是function object).
 
在 这个例子中, boost::bind的asio::placeholders::error参数是为了给回调函数传入一个error对象.当进行一个异步操作,开始 boost::bind时,你需要使用它来匹配回调函数的参数表.下一节中你会学到回调函数不需要error参数时可以省略它.
 
    t->async_wait(boost::bind(print,
          asio::placeholders::error, t, count));
  }
}

int main()
{
  asio::io_service io;

  int count = 0;
  asio::deadline_timer t(io, boost::posix_time::seconds(1));
和上面一样,我们再一次使用了绑定asio::deadline_timer::async_wait()
 
  t.async_wait(boost::bind(print,
        asio::placeholders::error, &t, &count));

  io.run();
在结尾,我们打印出的最后一次没有设置timer的调用的count的值
 
  std::cout << "Final count is " << count << "\n";

  return 0;
}
完整的代码:
 
#include <iostream>
#include <asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

void print(const asio::error& /*e*/,
    asio::deadline_timer* t, int* count)
{
  if (*count < 5)
  {
    std::cout << *count << "\n";
    ++(*count);

    t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
    t->async_wait(boost::bind(print,
          asio::placeholders::error, t, count));
  }
}

int main()
{
  asio::io_service io;

  int count = 0;
  asio::deadline_timer t(io, boost::posix_time::seconds(1));
  t.async_wait(boost::bind(print,
        asio::placeholders::error, &t, &count));

  io.run();

  std::cout << "Final count is " << count << "\n";

  return 0;
}
 
 
3.5. 成员函数作为回调函数
 
本例的运行结果和上一节类似
 
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
我们先定义一个printer类
 
class printer
{
public:
构造函数有一个io_service参数,并且在初始化timer_时用到了它.用来计数的count_这里同样作为了成员变量
 
  printer(boost::asio::io_service& io)
    : timer_(io, boost::posix_time::seconds(1)),
      count_(0)
  {
boost::bind 同样可以出色的工作在成员函数上.众所周知,所有的非静态成员函数都有一个隐式的this参数,我们需要把this作为参数bind到成员函数上.和上一 节类似,我们再次用bind构造出void(const boost::asio::error&)形式的函数.
 
注意,这里没有指定boost::asio::placeholders::error占位符,因为这个print成员函数没有接受一个error对象作为参数.
 
    timer_.async_wait(boost::bind(&printer::print, this));
  }
在类的折构函数中我们输出最后一次回调的conut的值
 
  ~printer()
  {
    std::cout << "Final count is " << count_ << "\n";
  }
print函数于上一节的十分类似,但是用成员变量取代了参数.
 
  void print()
  {
    if (count_ < 5)
    {
      std::cout << count_ << "\n";
      ++count_;

      timer_.expires_at(timer_.expires_at() + boost::posix_time::seconds(1));
      timer_.async_wait(boost::bind(&printer::print, this));
    }
  }

private:
  boost::asio::deadline_timer timer_;
  int count_;
};
现在main函数清爽多了,在运行io_service之前只需要简单的定义一个printer对象.
 
int main()
{
  boost::asio::io_service io;
  printer p(io);
  io.run();

  return 0;
}
完整的代码:
 
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class printer
{
public:
  printer(boost::asio::io_service& io)
    : timer_(io, boost::posix_time::seconds(1)),
      count_(0)
  {
    timer_.async_wait(boost::bind(&printer::print, this));
  }

  ~printer()
  {
    std::cout << "Final count is " << count_ << "\n";
  }

  void print()
  {
    if (count_ < 5)
    {
      std::cout << count_ << "\n";
      ++count_;

      timer_.expires_at(timer_.expires_at() + boost::posix_time::seconds(1));
      timer_.async_wait(boost::bind(&printer::print, this));
    }
  }

private:
  boost::asio::deadline_timer timer_;
  int count_;
};

int main()
{
  boost::asio::io_service io;
  printer p(io);
  io.run();

  return 0;
}
 
 
3.6. 多线程回调同步
 
本节演示了使用boost::asio::strand在多线程程序中进行回调同步(synchronise).
 
先 前的几节阐明了如何在单线程程序中用boost::asio::io_service::run()进行同步.如您所见,asio库确保 仅当 当前线程调用boost::asio::io_service::run()时产生回调.显然,仅在一个线程中调用 boost::asio::io_service::run() 来确保回调是适用于并发编程的.
 
一个基于asio的程序最好是从单线程入手,但是单线程有如下的限制,这一点在服务器上尤其明显:
 
  • 当回调耗时较长时,反应迟钝.
  • 在多核的系统上无能为力
 
如果你发觉你陷入了这种困扰,可以替代的方法是建立一个boost::asio::io_service::run()的线程池.然而这样就允许回调函数并发执行.所以,当回调函数需要访问一个共享,线程不安全的资源时,我们需要一种方式来同步操作.
 
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
在上一节的基础上我们定义一个printer类,此次,它将并行运行两个timer
 
class printer
{
public:
除了声明了一对boost::asio::deadline_timer,构造函数也初始化了类型为boost::asio::strand的strand_成员.
 
boost::asio::strand 可以分配的回调函数.它保证无论有多少线程调用了boost::asio::io_service::run(),下一个回调函数仅在前一个回调函数完成 后开始,当然回调函数仍然可以和那些不使用boost::asio::strand分配,或是使用另一个boost::asio::strand分配的回 调函数一起并发执行.
 
  printer(boost::asio::io_service& io)
    : strand_(io),
      timer1_(io, boost::posix_time::seconds(1)),
      timer2_(io, boost::posix_time::seconds(1)),
      count_(0)
  {
当 一个异步操作开始时,用boost::asio::strand来 "wrapped(包装)"回调函数.boost::asio::strand::wrap()会返回一个由boost::asio::strand分配 的新的handler(句柄),这样,我们可以确保它们不会同时运行.
 
    timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
    timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
  }

  ~printer()
  {
    std::cout << "Final count is " << count_ << "\n";
  }
 
多线程程序中,回调函数在访问共享资源前需要同步.这里共享资源是std::cout 和count_变量.
 
  void print1()
  {
    if (count_ < 10)
    {
      std::cout << "Timer 1: " << count_ << "\n";
      ++count_;

      timer1_.expires_at(timer1_.expires_at() + boost::posix_time::seconds(1));
      timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
    }
  }

  void print2()
  {
    if (count_ < 10)
    {
      std::cout << "Timer 2: " << count_ << "\n";
      ++count_;

      timer2_.expires_at(timer2_.expires_at() + boost::posix_time::seconds(1));
      timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
    }
  }

private:
  boost::asio::strand strand_;
  boost::asio::deadline_timer timer1_;
  boost::asio::deadline_timer timer2_;
  int count_;
};
main函数中boost::asio::io_service::run()在两个线程中被调用:主线程,一个boost::thread线程.
 
正如单线程中那样,并发的boost::asio::io_service::run()会一直运行直到完成任务.后台的线程将在所有异步线程完成后终结.
 
int main()
{
  boost::asio::io_service io;
  printer p(io);
  boost::thread t(boost::bind(&boost::asio::io_service::run, &io));
  io.run();
  t.join();

  return 0;
}
完整的代码:
 
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class printer
{
public:
  printer(boost::asio::io_service& io)
    : strand_(io),
      timer1_(io, boost::posix_time::seconds(1)),
      timer2_(io, boost::posix_time::seconds(1)),
      count_(0)
  {
    timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
    timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
  }

  ~printer()
  {
    std::cout << "Final count is " << count_ << "\n";
  }

  void print1()
  {
    if (count_ < 10)
    {
      std::cout << "Timer 1: " << count_ << "\n";
      ++count_;

      timer1_.expires_at(timer1_.expires_at() + boost::posix_time::seconds(1));
      timer1_.async_wait(strand_.wrap(boost::bind(&printer::print1, this)));
    }
  }

  void print2()
  {
    if (count_ < 10)
    {
      std::cout << "Timer 2: " << count_ << "\n";
      ++count_;

      timer2_.expires_at(timer2_.expires_at() + boost::posix_time::seconds(1));
      timer2_.async_wait(strand_.wrap(boost::bind(&printer::print2, this)));
    }
  }

private:
  boost::asio::strand strand_;
  boost::asio::deadline_timer timer1_;
  boost::asio::deadline_timer timer2_;
  int count_;
};

int main()
{
  boost::asio::io_service io;
  printer p(io);
  boost::thread t(boost::bind(&boost::asio::io_service::run, &io));
  io.run();
  t.join();

  return 0;
}
 
 
3.7. TCP客户端:对准时间
 
 
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
本程序的目的是访问一个时间同步服务器,我们需要用户指定一个服务器(如time-nw.nist.gov),用IP亦可.
 
(译者注:日期查询协议,这种时间传输协议不指定固定的传输格式,只要求按照ASCII标准发送数据。)
 
using boost::asio::ip::tcp;

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: client <host>" << std::endl;
      return 1;
    }

用asio进行网络连接至少需要一个boost::asio::io_service对象
 
    boost::asio::io_service io_service;
我们需要把在命令行参数中指定的服务器转换为TCP上的节点.完成这项工作需要boost::asio::ip::tcp::resolver对象
 
    tcp::resolver resolver(io_service);
一个resolver对象查询一个参数,并将其转换为TCP上节点的列表.这里我们把argv[1]中的sever的名字和要查询字串daytime关联.
 
    tcp::resolver::query query(argv[1], "daytime");
节点列表可以用 boost::asio::ip::tcp::resolver::iterator 来进行迭代.iterator默认的构造函数生成一个end iterator.
 
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
    tcp::resolver::iterator end;
现在我们建立一个连接的sockert,由于获得节点既有IPv4也有IPv6的.所以,我们需要依次尝试他们直到找到一个可以正常工作的.这步使得我们的程序独立于IP版本
 
    tcp::socket socket(io_service);
    boost::asio::error error = boost::asio::error::host_not_found;
    while (error && endpoint_iterator != end)
    {
      socket.close();
      socket.connect(*endpoint_iterator++, boost::asio::assign_error(error));
    }
    if (error)
      throw error;
连接完成,我们需要做的是读取daytime服务器的响应.
 
我们用boost::array来保存得到的数据,boost::asio::buffer()会自动根据array的大小暂停工作,来防止缓冲溢出.除了使用boost::array,也可以使用char [] 或std::vector.
 
    for (;;)
    {
      boost::array<char, 128> buf;
      boost::asio::error error;

      size_t len = socket.read_some(
          boost::asio::buffer(buf), boost::asio::assign_error(error));
当服务器关闭连接时,boost::asio::ip::tcp::socket::read_some()会用boost::asio::error::eof标志完成, 这时我们应该退出读取循环了.
 
      if (error == boost::asio::error::eof)
        break; // Connection closed cleanly by peer.
      else if (error)
        throw error; // Some other error.

      std::cout.write(buf.data(), len);
    }
如果发生了什么异常我们同样会抛出它
 
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }
运行示例:在windowsXP的cmd窗口下
 
输入:upload.exe time-a.nist.gov
 
输出:54031 06-10-23 01:50:45 07 0 0 454.2 UTC(NIST) *
 
完整的代码:
 
#include <iostream>
#include <boost/array.hpp>
#include <asio.hpp>

using asio::ip::tcp;

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: client <host>" << std::endl;
      return 1;
    }

    asio::io_service io_service;

    tcp::resolver resolver(io_service);
    tcp::resolver::query query(argv[1], "daytime");
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
    tcp::resolver::iterator end;

    tcp::socket socket(io_service);
    asio::error error = asio::error::host_not_found;
    while (error && endpoint_iterator != end)
    {
      socket.close();
      socket.connect(*endpoint_iterator++, asio::assign_error(error));
    }
    if (error)
      throw error;

    for (;;)
    {
      boost::array<char, 128> buf;
      asio::error error;

      size_t len = socket.read_some(
          asio::buffer(buf), asio::assign_error(error));

      if (error == asio::error::eof)
        break; // Connection closed cleanly by peer.
      else if (error)
        throw error; // Some other error.

      std::cout.write(buf.data(), len);
    }
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}
 
 
3.8. TCP同步时间服务器
 
 
#include <ctime>
#include <iostream>
#include <string>
#include <asio.hpp>

using asio::ip::tcp;
我们先定义一个函数返回当前的时间的string形式.这个函数会在我们所有的时间服务器示例上被使用.
 
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
  {
    asio::io_service io_service;
新建一个asio::ip::tcp::acceptor对象来监听新的连接.我们监听TCP端口13,IP版本为V4
 
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
这是一个iterative server,也就是说同一时间只能处理一个连接.建立一个socket来表示一个和客户端的连接, 然后等待客户端的连接.
 
    for (;;)
    {
      tcp::socket socket(io_service);
      acceptor.accept(socket);
当客户端访问服务器时,我们获取当前时间,然后返回它.
 
      std::string message = make_daytime_string();

      asio::write(socket, asio::buffer(message),
          asio::transfer_all(), asio::ignore_error());
    }
  }
最后处理异常
 
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}
运行示例:运行服务器,然后运行上一节的客户端,在windowsXP的cmd窗口下
 
输入:client.exe 127.0.0.1
 
输出:Mon Oct 23 09:44:48 2006
 
完整的代码:
 
#include <ctime>
#include <iostream>
#include <string>
#include <asio.hpp>

using asio::ip::tcp;

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
  {
    asio::io_service io_service;

    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));

    for (;;)
    {
      tcp::socket socket(io_service);
      acceptor.accept(socket);

      std::string message = make_daytime_string();

      asio::write(socket, asio::buffer(message),
          asio::transfer_all(), asio::ignore_error());
    }
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}
 
 
4. Filesystem Library 文件系统
 
 
 
推荐使用支持W3C标准的浏览器进行阅读,获得最佳页面效果,谢绝IE的混乱解析!
 
Searching all CPUG sites and mailist::
 
 
  • Web
 
  • getACL = 0.002s
  • load_multi_cfg = 0.000s
  • run = 0.402s
  • send_page = 0.401s
  • send_page_content = 0.349s
  • total = 0.407s
 
 
 

ACE与ASIO之间关于Socket编程的比较

[收藏]ACE与ASIO之间关于Socket编程的比较
Joomla! 开源天空 作者:joomla 2008-08-05 21:41

* 摘要:[收藏]ACE与ASIO之间关于Socket编程的比较
*

ACE与ASIO之间关于Socket编程的比较

转自:FREE MY SOUL

ACE是一个很成熟的中间件产品,为自适应通讯环境,但它过于宏大,一堆的设计模式,架构是一层又一层,对初学者来说,有点困难。
ASIO是基本Boost开发的异步IO库,封装了Socket,简化基于socket程序的开发。

最近分析ASIO的源代码,让我无不惊呀于它设计。在ACE中开发中的内存管理一直让人头痛,ASIO的出现,让我看到新的曙光,成为我新的好伙伴。简单地与ACE做个比较。

1.层次架构:
ACE底层是C风格的OS适配层,上一层基于C++的wrap类,再上一层是一些框架(Accpetor, Connector,Reactor等),最上一层是框架上服务。
ASIO与之类似,底层是OS的适配层,上一层一些模板类,再上一层模板类的参数化(TCP/UDP),再上一层是服务,它只有一种框架为io_service。

2.涉及范围:
ACE包含了日志,IPC,线程,共享内存,配置服务等。
ASIO只涉及到Socket,提供简单的线程操作。

3.设计模式:
ACE主要应用了Reactor,Proactor等。
而ASIO主要应用了Proactor。

4.线程调度:
ACE的Reactor是单线程调度,Proactor支持多线程调度。
ASIO支持单线程与多线程调度。

5.事件分派处理:
ACE主要是注册handler类,当事件分派时,调用其handler的虚挂勾函数。实现ACE_Handler/ACE_Svc_Handler/ACE_Event_handler等类的虚函数。
ASIO是基于函数对象的hanlder事件分派。任何函数都可能成功hanlder,少了一堆虚表的维护,调度上优于ACE。

6.发布方式:
ACE是开源免费的,不依赖于第3方库, 一般应用使用它时,以动态链接的方式发布动态库。
ASIO是开源免费的,依赖Boost,应用使用时只要include头文件,不需动态库。

7.可移植性:
ACE支持多种平台,可移植性不存在问题,据说socket编程在linux下有不少bugs。
ASIO支持多种平台,可移植性不存在问题。

8.开发难度:
基于ACE开发应用,对程序员要求比较高,要用好它,必须非常了解其框架。在其框架下开发,往往new出一个对象,不知在什么地方释放好。
基于ASIO开发应用,要求程序员熟悉函数对象,函数指针,熟悉boost库中的boost::bind。内存管理控制方便。


我个人觉得,如果应用socket编程,使用ASIO开发比较好,开发效率比较高。ACE适合于理论研究,它本来就是源于Douglas的学术研究。

2009年3月22日星期日

mingw手工编译wxWidgets遇到的奇怪问题,及解决方案收藏

http://blog.csdn.net/DelphiNew/archive/2007/05/11/1605310.aspx
要使用wxWidgets来开发界面,
没有打算省力,所以下载的是源程序,自已编译。。。。

编译器和make程序用的是dev-c++中带的mingw。。。。

然而在make 时,发生这个错误信息
if not exist ..\..\lib\gcc_lib\mswd\wx mkdir ..\..\lib\gcc_lib\mswd\wx
/usr/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [..\..\lib\gcc_lib\mswd\wx] Error 2

明明自己的c:\devc\bin目录已经在path的最前位了呀?how come?
一开始也不明白,明明用的是win32版的mingw,怎么会以用到/usr/bin/sh呢???
现在也真不是看make的源代码的时候,,

瞎猜可能是因为在path中其它的cygwin工具影响了吧。。。那么我们狠一点
set path=c:\devc\bin
make -f makefile.gcc

成功了。。。。
一下子觉得自己又笨又恶心~


安装wxWidgets (http://www.d2school.com/bhcpp_book/2_4.php)

2.4. 安装wxWidgets

多数应用软件,通常需要一个界面与使用者进行交互。典型的交互方式可以分成三种:

  • 纯文字界面
    也称为“控制台”应用。此类应用通过以“一问一答”的形式在屏幕上打出提问的文字,然后等待用户输入后,程序得以继续运行。 这类交互形式称不上友好,但实现起来简单。在我们学习C++语法的一个很长的时期,我们所写的程序,多数采用此类形式。
  • 图形用户界面
    通过“对话框”、“菜单”、“按钮”等典型的图形元素所提供的用户界面。即“图形用户界面/Graphical user interface”,简称为GUI。该形式下,用户通过鼠标或键盘操作,选择所需的功能。 我们常用Office软件、QQ聊天软件、以及Windows操作系统本身,都是典型的GUI程序。
  • 浏览器界面
    浏览器界面是GUI的一种特定应用。它将程序分为后台服务和前台展示两部分。后台服务主要实现程序的业务逻辑,以及产生界面脚本(通常是HTML),真正 的界面展现与交互工作完全交给第三方的浏览器(比如IE,或Firefox)去完成。 当我们上网浏览新闻时,就是在使用此类应用。

本书前面的一大部分内容,采用“纯文字界面”作为范例程序主要采用界面方式。此安排自然是为了避免读者一开始就陷入复杂的GUI界面设计的“泥潭”之中。然而,与大多数纯粹的C++语言教程止步于此不同,如果编写(跨平台)的“图形用户界面”,也是本书的主要教学内容。

C++ 的GUI程序库,同样充满很多选择。一些非开源产品多数仅限于某一平台。wxWidgets是同时符合“开源”、“成熟”、“跨平台”,并且支持各平台“ 原生界面”的GUI库中的佼佼者。这里的“原生界面”是指,在A系统下,其生成的界面元素,就和A系统的本地界面风格一致,这里的A,可以指代 Windows XP、也可以指代Linux下某一图形界面。

wxWidgets的主要特点如下:

  • 开源
  • 跨平台:Windows、Mac OS X、GTK+、X11、Motif、WinCE。
  • 既支持原生界面,也支持统一风格界面。
  • 不仅仅是图形界面库:集成提供了包括文件操作、目录操作、多线程、网络通讯、OpenGL(3D)等等功能库。
  • 支持不同的库链接形式:静态链接,动态链接库,其中后者还可以支持编译成单一动态库,或多个小动态库的形式。
  • 支持UNICODE:编译时,可以编译出UNICODE或非UNICODE两种版本,本书采用UNICODE,以方便编程上对汉字的操作。
  • 发展成熟:有较长的发展历史(起源于1992年),业界有许多基于wxWidgets的成熟应用
  • 学习资源丰富:网络上的可以找到较多的资源。
  • 与Code::Blocks结合紧密:C::B本身正是采用wxWidgets实现跨平台的图形用户界面。另外C::B内置了使用wxWidgets的可视界面设计工具。
  • 支持多种编译器:包括本书采用的开源gcc/g++编译器,也包括Visual C++等商业编译器。

本书配套光盘提供了wxWidgets 2.8.8 版本的安装程序,以及四种编译库。您可以通过查看光盘中对应的说明文件,实现快速安装。

如果您需要更新版本的wxWidgets,或者您只是想学习其编译过程,那么请继续阅读下述内容。

2.4.1. 准备

请通过本小节末尾的“相关网站”链接,上网下载wxWidgets的最新稳定版本。请注意选择为for windows版本。

下载得到的,应是一个可执行的安装程序。与Code::Blocks的安装类似,建议将其安装某个驱动器的根目录下,比如:E:\ wxWidgets-2.8.8 目录下(尾部数字以您所使用的wxWidgets版本为准)。

当然,由于本书将指导您安装多个C++扩展库,因此,如果您愿意建立一个新文件夹,用于作为各个C++扩展库的统一的父文件夹,的确是个好主意。

2.4.2. 编译

上一步安装wxWidgets,仅得到源代码文件及一些说明。我们还需要将其中的源代码编译成“库”文件。此类“库”有很多种形式,但其实都可以称为“预编译库”。意思是:它们已经编译好了,你直接拿去用吧。

我们先简单地了解一下“动态、静态”及“调试、发行”的区别。

“库”通常包含很多功能代码,但它们本身并不能直接运行,而是准备提供给应用程序使用, “动态链接”和“静态链接”代表程序如何找到及使用库功能的两种不同方法。

“静态链接”是指,直接将库文件和程序文件合成一个大文件,在合并过程中,完成对库文件各个功能的定位。这个事情发生程序编译的过程。

“动态链接”与此相反,库文件和程序文件各自独立存在,程序在运行时,才去库文件找相关的功能。

完全使用“静态链接”,我们可能会得到一个“胖胖”的程序,直接发给用户使用就可以了。使用“动态链接”,我们可能需要分发给你的用户多个文件(程序和各个库文件)。不小心弄丢了哪个文件,用户们就要抱怨你的软件“跑”不起来了。

“调试版”和“发行版”就和链接技术无关了。前者表示,二者的区别在于:前者含有调试信息,而后者没有。有调试信息有利于我们纠出程序中的错误,不过既然它的含有额外的信息,所以这样的库肯定“胖”了不少。如果觉得错误纠得差不多了,我们就可以改用相对“苗条”的发行版。

有关“库”的详细知识,我们留在第四章讲。今天我们将把wxWidgets编译译成四种形式:

  • 动态链接库 + 调试版
  • 动态链接库 + 发行版
  • 静态链接库 + 调试版
  • 静态链接库 + 发行版

另 外,wxWidgets的编译,还可以区分“UNICODE”及“非 UNICODE”两种式。在多数场合下,“UNICODE”可以对汉字及国际化有较方便的支持。因此以上四种组合,我们全部都加上相同“UNICODE” 选项。当然, “非UNICODE”选项倒也不是一无是处,以后我们也会用到。

正式开始编译之前,必须确保已经在电脑上正确安装好mingw32环境,如果您不在确信,请使用2.1.6.1小节中提及的方法进行检测。

(以下四个版本都需要编译,而不是仅编译其一。)

  • 静态链接(调试版)

步骤1:打开“开始”菜单:“所有程序→附件→记事本”程序。用它打开 wxWidgets安装目录下,子目录“build\msw\”下面的“config.gcc”文件。

步骤2:找到以下内容,并确保将等号(:=)后面的值如下面加粗内容所示。另外其它未指部分的内容,请保持不变。

步骤3:请确认保存了本步的修改成果,再继续下一步。

# What type of library to build? [0,1]
SHARED := 0

# Compile Unicode build of wxWidgets? [0,1]
UNICODE := 1

# Type of compiled binaries [debug,release]
BUILD := debug

步骤4:SHARED 为 0表示要编译的是静态库,而不是动态库(共享库);

UNICODE为1,表示要编译成UNICODE版本,这一项在本次编译过程中始终为1。

BUILD为debug,表示要编译成含有调试信息的版本。

步骤5:打开“开始”菜单,选择“运行”,输入cmd,进入控制台窗口,然后在其内通过cd命令,切换到wxWidgets安装目录下的“build\msw”路径。比如您把wxWidgets 安装在“E:\wxWidgets-2.8.8”,则过程如下:

E: (回车)

cd wxWidgets-2.8.8 (回车)

cd build\msw (回车)

步骤6:在第3步的控制台中,继续输入以下命令:

mingw32-make.exe -f makefile.gcc (回车)

步骤7:这将开始一段长约30分钟的编译过程(视你的机器配置而定)。

  • 静态链接(发行版)

编译过程和前一版本主要差别在于“config.gcc”文件的修改内容:

# What type of library to build? [0,1]
SHARED := 0

# Compile Unicode build of wxWidgets? [0,1]
UNICODE := 1

# Type of compiled binaries [debug,release]
BUILD := release

其它操作方法不变。

  • 动态链接(调试版)

编译过程和前一版本主要差别在于“config.gcc”文件的修改内容:

# What type of library to build? [0,1]
SHARED := 1

# Compile Unicode build of wxWidgets? [0,1]
UNICODE := 1

# Type of compiled binaries [debug,release]
BUILD := debug

其它操作方法不变。

  • 动态链接(发行版)

编译过程和前一版本主要差别在于“config.gcc”文件的修改内容:


# What type of library to build? [0,1]
SHARED := 1

# Compile Unicode build of wxWidgets? [0,1]
UNICODE := 1

# Type of compiled binaries [debug,release]
BUILD := release

其它操作方法不变。

2.4.3. 结束

这是一次漫长的编译过程,至此,我们完成了四个版本的wxWidgets库的编译。它是我们编写图形用户界面基础。

为 了确保大家尽量顺利地完成编译,我们在“config.gcc”仅选择了最基本的编译选项。因此一些强大wxWidgets功能模块没有被编译成库,比如 前面提到的非Unicode版,另外没有支持OpenGL、OBDC等功能。后面需要时,我们将会加上,并且令大家放心的是,这并不会带来又一次 wxWidgets漫长完全编译。

〖小提示〗:程序员,起来走走……

每次30分钟的编译过程,漫长的等待中,我猜到您不耐烦得站起来四处走动。走动其实健康有益。然而,在程序员的生涯中,我们更多面临的,可能是无数个3分钟,甚至是30秒的等待,这时,你是否记得提醒自己也要常常起来走动走动?

Code::Blocks专门提供一个插件,用于提醒程序员注意休息,很有兴趣?请在休息之后继续我们的白话之旅。

编 译过程中会产生大量的中间临时文件。请进入wxWidgets文件夹内的“build”子文件夹,搜索 “*.o”文件(确保选中“搜索子文件夹”选项):约有3千多个中间文件,全部删除,可以腾出1G多的空间。(Windows下多数编译器会生产扩展名为 “.obj”的文件,但mingw32-g++产生的为“.o”文件。)

编译完成后,所有静态链接库位于wxWidgets安装目录下的“lib\gcc_lib”子目录。而动态链接库位于 “lib\gcc_dll”子目录。

仔细观察,会发现库文件名称中,有不少带有“ud”字母的文件,其中‘d’表示“debug”,即调试版本 ,不带‘d’的通常是“release”版本。

‘u’表示“Unicode”,即支持Unicode编码。虽然我们这一次编译的全部采用“Unicode”选项,但还是存在一些文件并不带‘u’字母,那是因为这些库并不涉及到字符串的国际化处理,所以不需要考虑编码,比如与Jpeg图形功能有关的库。

  • 静态库文件

libwxbase28u.a
libwxbase28ud.a libwxbase28ud_net.a libwxbase28ud_xml.a
libwxbase28u_net.a libwxbase28u_xml.a libwxexpat.a
libwxexpatd.a libwxjpeg.a libwxjpegd.a
libwxmsw28ud_adv.a libwxmsw28ud_aui.a libwxmsw28ud_core.a
libwxmsw28ud_html.a libwxmsw28ud_media.a libwxmsw28ud_richtext.a
libwxmsw28ud_xrc.a libwxmsw28u_adv.a libwxmsw28u_aui.a
libwxmsw28u_core.a libwxmsw28u_html.a libwxmsw28u_media.a
libwxmsw28u_richtext.a libwxmsw28u_xrc.a libwxpng.a
libwxpngd.a libwxregexu.a libwxregexud.a
libwxtiff.a libwxtiffd.a libwxzlib.a
libwxzlibd.a

  • 动态库文件

wxbase28ud_gcc_custom.dll wxbase28ud_net_gcc_custom.dll
wxbase28ud_xml_gcc_custom.dll wxbase28u_gcc_custom.dll
wxbase28u_net_gcc_custom.dll wxbase28u_xml_gcc_custom.dll
wxmsw28ud_adv_gcc_custom.dll wxmsw28ud_aui_gcc_custom.dll
wxmsw28ud_core_gcc_custom.dll wxmsw28ud_html_gcc_custom.dll
wxmsw28ud_media_gcc_custom.dll wxmsw28ud_richtext_gcc_custom.dll
wxmsw28ud_xrc_gcc_custom.dll wxmsw28u_adv_gcc_custom.dll
wxmsw28u_aui_gcc_custom.dll wxmsw28u_core_gcc_custom.dll
wxmsw28u_html_gcc_custom.dll wxmsw28u_media_gcc_custom.dll
wxmsw28u_richtext_gcc_custom.dll wxmsw28u_xrc_gcc_custom.dll

在gcc_dll目录下,除了扩展名为“.dll”的动态链接库以外,还存在大量的扩展为“.a”的文件,称为“导入库/Import library”,为了节省篇幅,上表未加列出。

“导入库”在编译时向应用程序提供信息,以辅助应用程序在运行时,定位具体某一功能在DLL文件中的入口。也就是说,今后编译程序时,将用到它们,但它们不必随同程序一起发行。

2.4.4. 相关网址

wxWidgets 官方网站:www.wxWidgets.org

wxWidgets扩展代码:www.wxCode.com