网络服务器软件开发/中间件开发,关注ACE/ICE/boost

C++博客 首页 新随笔 联系 聚合 管理
  152 Posts :: 3 Stories :: 172 Comments :: 0 Trackbacks

        关于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交流哦,呵呵

posted on 2008-07-20 13:45 true 阅读(3672) 评论(3)  编辑 收藏 引用 所属分类: boost通信技术

Feedback

# re: Asio实现的实用echo server 2008-07-20 19:28 haskell
python做网络测试确实不错
c++的网络库一抓一大把,而且质量都不错  回复  更多评论
  

# re: Asio实现的实用echo server 2008-08-21 15:24 roger007
this->handle_exit();
delete this;//这句不能放在handle_exit里面,否则问题多多,没找到好的解决方案

=============
可以考虑使用boost的智能指针来解决这个问题,就不需要再考虑对象的delete了,具体可以参考ASIO的chat例子。
  回复  更多评论
  

# re: Asio实现的实用echo server 2009-05-11 15:19 zxh
我看了一下asio下的example\http2 其中创建了多个io_service放入多个线程来执行run方法。 与http3这个例子中的多线程有什么区别,其中他使用了strand来同步线程,http2中没有,我有些不太明白,还有不少疑问想请教一下你, 你加的QQ吗?我的QQ:6346289  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理