开始学网络编程...我各种看文章以后,最终选择用Boost.Asio库。
这里就写点自己遇到过的坑以及收集的各种文章:
教程
Asio入门教程
同步异步阻塞非阻塞
“阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解,提供一个从分布式系统角度的回答。
1.同步与异步
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。
换句话说,就是由调用者主动等待这个调用的结果。而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。
典型的异步编程模型比如Node.js
举个通俗的例子:
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。2. 阻塞与非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。还是上面的例子,
你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
如果是关心blocking IO/ asynchronous IO, 参考 Unix Network Programming View Book
作者:严肃
原文链接
epoll讲解
其他好文
笔记
零散
最好的例子在Boost
的源码样例里,也就是${boost}\doc\html\boost_asio\example
里
socket类是不支持拷贝构造的,所以得用boost::shared_ptr<socket>
// boost::bind用法
boost::bind(&函数对象, ...)
boost::bind(&成员函数对象, 实例, ...)
boost::bind(实例, ...) // 重载了()的实例
Echo Client/Server
// async_tcp_echo_server.cpp
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
class Server {
typedef boost::shared_ptr<tcp::socket> socket_ptr;
typedef boost::system::system_error system_error;
typedef boost::system::error_code error_code;
public:
Server(boost::asio::io_service &service, int port) :
_service(service),
_acceptor(service, tcp::endpoint(tcp::v4(), port)) { }
void start() {
socket_ptr pSocket(new tcp::socket(_service));
_acceptor.async_accept(*pSocket, boost::bind(&Server::acceptHandler, this, pSocket, _1));
}
private:
enum { MAX_BUFF_SIZE = 1024 };
void acceptHandler(socket_ptr pSocket, const error_code &ec) {
if (ec) {
std::cerr << "From " << pSocket->remote_endpoint().address() << " error: " << system_error(ec).what() << std::endl;
return;
}
std::cerr << "Connected ip: " << pSocket->remote_endpoint().address() << std::endl;
start();
pSocket->async_read_some(boost::asio::buffer(_buff, MAX_BUFF_SIZE), boost::bind(&Server::readHandler, this, pSocket, _1, _2));
}
void readHandler(socket_ptr pSocket, const error_code &ec, size_t bytes) {
if (ec) {
std::cerr << "From " << pSocket->remote_endpoint().address() << " error: " << system_error(ec).what() << std::endl;
return;
}
std::cerr << "From " << pSocket->remote_endpoint().address() << " Received: " << std::string(_buff, bytes) << std::endl;
pSocket->async_read_some(boost::asio::buffer(_buff, MAX_BUFF_SIZE), boost::bind(&Server::readHandler, this, pSocket, _1, _2));
}
boost::asio::io_service &_service;
tcp::acceptor _acceptor;
char _buff[MAX_BUFF_SIZE];
};
int main(int argc, char* argv[]) {
boost::asio::io_service io_service;
try {
Server server(io_service, 2333);
server.start();
io_service.run();
}
catch (boost::system::system_error & e) {
std::cerr << "error : " << e.what() << std::endl;
}
return 0;
}
// async_tcp_echo_server_2.cpp
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
typedef boost::system::system_error system_error;
typedef boost::system::error_code error_code;
class Session :
public std::enable_shared_from_this<Session> {
public:
Session(tcp::socket socket)
: _socket(std::move(socket)) { }
void start() {
doRead();
}
private:
void doRead() {
auto self(shared_from_this());
_socket.async_read_some(boost::asio::buffer(_buff, BUFF_MAX_SIZE), [this, self] (const error_code &ec, size_t bytes) {
if (ec) {
std::cerr << "Error from " << _socket.remote_endpoint().address() << " : " << system_error(ec).what() << std::endl;
return;
}
std::cerr << "From " << _socket.remote_endpoint().address() << " received: " << std::string(_buff, bytes) << std::endl;
doWrite();
doRead();
}
);
}
void doWrite() {
auto self(shared_from_this());
_socket.async_write_some(boost::asio::buffer("Welcome"), [this, self] (const error_code &ec, size_t bytes) {
if (ec) {
std::cerr << "Error to " << _socket.remote_endpoint().address() << " : " << system_error(ec).what() << std::endl;
return;
}
}
);
}
private:
enum { BUFF_MAX_SIZE = 1024 };
tcp::socket _socket;
char _buff[BUFF_MAX_SIZE];
};
class Server {
public:
Server(int port) :
_service(),
_acceptor(_service, tcp::endpoint(tcp::v4(), port)),
_socket(_service) { }
void start() {
doAccept();
_service.run();
}
private:
void doAccept() {
_acceptor.async_accept(_socket, [this](const error_code &ec) {
if (!ec) {
std::make_shared<Session>(std::move(_socket))->start();
}
doAccept();
}
);
}
boost::asio::io_service _service;
tcp::acceptor _acceptor;
tcp::socket _socket;
};
int main(int argc, char* argv[]) {
try {
int port = 2333;
std::cerr << "Listening port : " << port << std::endl;
Server server(port);
server.start();
}
catch (boost::system::system_error & e) {
std::cerr << "error : " << e.what() << std::endl;
}
return 0;
}
// blocking_tcp_echo_client.cpp
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
enum {
max_length = 1024
};
boost::asio::io_service io_service;
void heartBeat(tcp::endpoint ep) {
tcp::socket s(io_service);
s.connect(ep);
for (;;) {
s.write_some(boost::asio::buffer(std::string("Heart Beat")));
boost::this_thread::sleep(boost::posix_time::millisec(2000));
}
}
void sendMsg(tcp::endpoint ep) {
tcp::socket s(io_service);
s.connect(ep);
for (;;) {
std::cout << "Enter message: ";
char request[max_length];
std::cin.getline(request, max_length);
size_t request_length = std::strlen(request);
s.write_some(boost::asio::buffer(request, request_length));
/*
char reply[max_length];
size_t reply_length = boost::asio::read(s,
boost::asio::buffer(reply, request_length));
std::cout << "Reply is: ";
std::cout.write(reply, reply_length);
std::cout << "\n";
*/
}
}
int main(int argc, char* argv[]) {
try {
tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 2333);
boost::thread_group threads;
threads.create_thread(boost::bind(heartBeat, ep));
threads.create_thread(boost::bind(sendMsg, ep));
threads.join_all();
}
catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
system("pause");
}
return 0;
}
坑
*_read_until
:这种东西得用streambuff
而且最好手写completion
,要不然它读到delim
后,还会多读一些后面的数据到buffer里,十分坑。*_read
这种东西一定要在completion
里加上transfer_exactly
,理由同上