关于echo的简单server,几乎多得发指,但大部分都没有提供类似粘包,定时器,安全退出等开发中的常用机制,换句话说,为了echo而echo,借鉴价值大打折扣,毕竟我们平时的工作不可能这么简单。这几天研究了下asio,感觉不错,boost接纳asio后,在服务器开发领域是不是该得到重视呢:),还是贴代码吧,有注释
// asio.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
//禁止boost自动链接静态库
#define BOOST_ALL_DYN_LINK
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
using boost::asio::ip::tcp;
class session
{
public:
session(boost::asio::io_service& io_service)
: socket_(io_service),
timer_(io_service),
exit_flag_(false)
{
std::cout << "session born\n";
}
~session()
{
std::cout << "session destroy\n";
}
tcp::socket& socket()
{
return socket_;
}
void start()
{//async_receive,async_read_some
/**//*
socket_.async_receive(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
*/
//读到4个字节才返回,可以和上面注释的sync_receive比较
boost::asio::async_read(socket_,
boost::asio::buffer(data_, 4),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
));
size_t cancelcnt = timer_.expires_from_now(boost::posix_time::seconds(10));
//@return The number of asynchronous operations that were cancelled;第一次启动定时器
std::cout << "timer canceled count is = " << cancelcnt << std::endl;
timer_.async_wait(boost::bind(&session::handle_timeout,this,
boost::asio::placeholders::error));
}
protected:
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{//可以考虑复杂应用,如:如何集成数据库操作等
if (!error)
{
if (bytes_transferred < header_lenght)
{//处理正常退出
std::cout << "need close\n";
this->handle_exit();
delete this;
return;
}
else if (bytes_transferred == header_lenght)//header
{
data_[bytes_transferred] = '\0';
std::cout << "packet len = " << data_ << std::endl;
int bodylen = boost::lexical_cast<int>(data_);
boost::asio::async_read(socket_,
boost::asio::buffer(data_,bodylen),
boost::bind(&session::handle_read,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
return;
}
else
{//读到了bodylen长度才返回,如果由于网络原因等导致长时间没有读全,则上面的定时器会做超时处理
if (bytes_transferred < max_length)
{
data_[bytes_transferred] = '\0';
}
std::cout << data_ << std::endl;
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, this,
boost::asio::placeholders::error));
// size_t cancelcnt = timer_.expires_from_now(boost::posix_time::seconds(10));
// std::cout << "timer canceled count is = " << cancelcnt << std::endl;
// timer_.async_wait(boost::bind(&session::handle_timeout,this,
// boost::asio::placeholders::error));
socket_.async_receive(boost::asio::buffer(data_, 1),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));//发起异步读操作,以便后面正常退出
}
}
else
{
std::cout << "error = " << error << " bytes_transferred = " << bytes_transferred << std::endl;
this->handle_exit();
delete this;//这句不能放在handle_exit里面,否则问题多多,没找到好的解决方案
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
std::cout << "handle_write ok\n";
this->handle_exit();
}
else
{
std::cout << "handle_write error\n";
}
return;
}
void handle_timeout(const boost::system::error_code& e)
{
if (e != boost::asio::error::operation_aborted)
{
std::cout << "timeout now.\n";
this->handle_exit();
}
else
{//被取消
std::cout << "cancelled now.\n";
}
return;
}
void handle_exit()
{
std::cout << "enter handle_exit1.\n";
if (!exit_flag_)
{
std::cout << "enter handle_exit2.\n";
exit_flag_ = true;
timer_.cancel();
socket_.close();
std::cout << "after socket close.\n";
/**//*
Any asynchronous send, receive
or connect operations will be cancelled immediately, and will complete
with the boost::asio::error::operation_aborted error
*/
//delete this;
return;
}
else
{
return;
}
}
private:
tcp::socket socket_;
boost::asio::deadline_timer timer_;
bool exit_flag_;
enum {header_lenght = 4, max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
void handle_accept(session* new_session,
const boost::system::error_code& error)
{
if (!error)
{
new_session->start();
new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
else
{
delete new_session;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[])
{
try
{
boost::asio::io_service io_service;
server s(io_service, 9000);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
下面是python测试脚本:
import socket,sys,time
host = sys.argv[1]
strPort = sys.argv[2]
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
port = int(strPort)
try:
sock.connect((host,port))
except socket.gaierror,e:
print "addr error:%s" % e
sys.exit(1)
except socket.error,e:
print "connection error :%s" % e
sys.exit(1)
try:
pack = 'hello world!'
packheader = '%04d' % len(pack)
print "header : %s" % packheader
sock.sendall(packheader)
sock.sendall(pack)
time.sleep(10)
sock.sendall("hello world from python2!")
recvbuf = sock.recv(1024)
print "recv data : %s " % recvbuf
except socket.error,e:
print "send data error : %s" % e
sys.exit(1)
print "exit now"
测试脚本中的内容可以灵活修改,用python写这个实在是方便,比起用C++来写,强多了。
最后,欢迎大家到www.opensourceinsight.com交流哦,呵呵