ThistutorialprogramshowshowtouseasiotoimplementaclientapplicationwithTCP.
Westartbyincludingthenecessaryheaderfiles.
本示例程序显示如何使用Asio来实现一个TCP客户端程序。
让我们从添加必需的头文件开始。
这个应用程序的目的是访问一个daytime服务器,因此我们需要用户去指定服务器。(如time-nw.nist.gov,用IP亦可)
usingboost::asio::ip::tcp;intmain(intargc,char*argv[]){try{if(argc!=2){std::cerr<<"Usage:client 所有使用asio的程序都至少需要一个boost::asio::io_service对象。 我们需要把服务器的名称转化为TCP的节点,而该名称是通过应用程序的参数指定的。我们使用boost::asio::ip::tcp::resolver对象来完成。 一个resolver对象获得一个query对象,并将其转换为节点列表.我们通过argv[1]中的服务器名称和服务名,在这里是daytime,构造一个query。 节点列表用boost::asio::ip::tcp::resolver::iterator类型的迭代器返回。返回的iterator将采用boost::asio::ip::tcp::resolver::iterator的默认构造函数来构造。 tcp::resolver::iteratorendpoint_iterator=resolver.resolve(query);tcp::resolver::iteratorend;Nowwecreateandconnectthesocket.ThelistofendpointsobtainedabovemaycontainbothIPv4andIPv6endpoints,soweneedtotryeachofthemuntilwefindonethatworks.ThiskeepstheclientprogramindependentofaspecificIPversion. 现在我们建立一个socket并连接之,由于获得的节点既有IPv4也有IPv6的。所以,我们需要依次尝试访问它们直到找到一个可以正常工作的。这样做可使得我们的程序独立于特定的IP版本。 tcp::socketsocket(io_service);boost::system::error_codeerror=boost::asio::error::host_not_found;while(error&&endpoint_iterator!=end){socket.close();socket.connect(*endpoint_iterator++,error);}if(error)throwboost::system::system_error(error);Theconnectionisopen.Allweneedtodonowisreadtheresponsefromthedaytimeservice. Weuseaboost::arraytoholdthereceiveddata.Theboost::asio::buffer()functionautomaticallydeterminesthesizeofthearraytohelppreventbufferoverruns.Insteadofaboost::array,wecouldhaveusedachar[]orstd::vector. 连接打开后,现在我们需要做的就是读取daytime服务器的响应。 我们使用boost::array来存放接收到的数据。boost::asio::buffer()函数会自动确定array的长度来防止缓冲区溢出。我们也可以使用char[]或std::vector来代替boost::array。 for(;;){boost::array 当服务器关闭连接时,boost::asio::ip::tcp::socket::read_some()函数会以boost::asio::error::eof错误标志返回,通过该错误标志,我们知道应该退出循环了。 if(error==boost::asio::error::eof)break;//Connectionclosedcleanlybypeer.elseif(error)throwboost::system::system_error(error);//Someothererror.std::cout.write(buf.data(),len);}Finally,handleanyexceptionsthatmayhavebeenthrown. 最后,处理所有可能抛出的异常。 完整代码: 一个同步的TCPdaytime服务器 ThistutorialprogramshowshowtouseasiotoimplementaserverapplicationwithTCP. 本示例示范如何使用Asio来实现一个TCP服务器程序。 我们先定义一个make_daytime_string()来产生需要发送给客户端的字符串.这个函数会在我们所有的daytime服务器上被使用。 std::stringmake_daytime_string(){usingnamespacestd;//Fortime_t,timeandctime;time_tnow=time(0);returnctime(&now);}intmain(){try{boost::asio::io_serviceio_service;Aboost::asio::ip::tcp::acceptorobjectneedstobecreatedtolistenfornewconnections.ItisinitialisedtolistenonTCPport13,forIPversion4. 新建一个asio::ip::tcp::acceptor对象来监听新的连接。该对象应遵守IPv4协议,监听TCP端口13。 Thisisaniterativeserver,whichmeansthatitwillhandleoneconnectionatatime.Createasocketthatwillrepresenttheconnectiontotheclient,andthenwaitforaconnection. for(;;){tcp::socketsocket(io_service);acceptor.accept(socket);Aclientisaccessingourservice.Determinethecurrenttimeandtransferthisinformationtotheclient. std::stringmessage=make_daytime_string();boost::system::error_codeignored_error;boost::asio::write(socket,boost::asio::buffer(message),boost::asio::transfer_all(),ignored_error);}}Finally,handleanyexceptions. 最后, 处理异常。 全部源码: 主函数 intmain(){try{Weneedtocreateaserverobjecttoacceptincomingclientconnections.Theboost::asio::io_serviceobjectprovidesI/Oservices,suchassockets,thattheserverobjectwilluse. 我们需要创建一个服务器对象,用来接受客户端的连接。boost::asio::io_service对象提供了像sockets这样的I/O服务,这些服务都是服务器对象将要使用的。 运行boost::asio::io_service对象,它将执行你想要的异步操作。 classtcp_server{public:TheconstructorinitialisesanacceptortolistenonTCPport13. 构造函数初始化一个用于监听TCP端口13的接收器。 tcp_server(boost::asio::io_service&io_service):acceptor_(io_service,tcp::endpoint(tcp::v4(),13)){start_accept();}private:Thefunctionstart_accept()createsasocketandinitiatesanasynchronousacceptoperationtowaitforanewconnection. 函数start_accept()创建一个socket,同时启动一个异步接收操作去等待一个新的连接。 voidstart_accept(){tcp_connection::pointernew_connection=tcp_connection::create(acceptor_.io_service());acceptor_.async_accept(new_connection->socket(),boost::bind(&tcp_server::handle_accept,this,new_connection,boost::asio::placeholders::error));}Thefunctionhandle_accept()iscalledwhentheasynchronousacceptoperationinitiatedbystart_accept()finishes.Itservicestheclientrequest,andthencallsstart_accept()toinitiatethenextacceptoperation. 当start_accept()启动的异步接收操作完成后,handle_accept()函数将被调用。它响应客户端的请求,然后调用start_accept()函数去启动另一个接收操作。 Wewilluseshared_ptrandenable_shared_from_thisbecausewewanttokeepthetcp_connectionobjectaliveaslongasthereisanoperationthatreferstoit. 我们希望只要还有一个操作涉及tcp_connection对象,该对象就是有效的。因此我们使用shared_ptr和enable_shared_from_this。 classtcp_connection:publicboost::enable_shared_from_this 在start()函数中,我们调用boost::asio::async_write()为客户端处理数据。注意:为了确保数据被整块发送,我们使用的是boost::asio::async_write(),而不是boost::asio::ip::tcp::socket::async_write_some()。 voidstart(){Thedatatobesentisstoredintheclassmembermessage_asweneedtokeepthedatavaliduntiltheasynchronousoperationiscomplete. 要发送的数据保存在类成员变量message_中,在异步操作完成前我们需要保证数据的有效性。 Wheninitiatingtheasynchronousoperation,andifusingboost::bind(),youmustspecifyonlytheargumentsthatmatchthehandler'sparameterlist.Inthisprogram,bothoftheargumentplaceholders(boost::asio::placeholders::errorandboost::asio::placeholders::bytes_transferred)couldpotentiallyhavebeenremoved,sincetheyarenotbeingusedinhandle_write(). 当启动一个异步操作时,如果使用boost::bind(),你只需要指定一个符合句柄参数列表签名的参数。在本例中,任何一个参数占位符(boost::asio::placeholders::error和boost::asio::placeholders::bytes_transferred)皆可被隐式地移除,因为操作write()并没有使用它们。 boost::asio::async_write(socket_,boost::asio::buffer(message_),boost::bind(&tcp_connection::handle_write,shared_from_this(),boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));Anyfurtheractionsforthisclientconnectionarenowtheresponsibilityofhandle_write(). 任何对客户端连接的下一步操作都由handle_write()函数负责处理。 Youmayhavenoticedthattheerror,andbytes_transferredparametersarenotusedinthebodyofthehandle_write()function.Ifparametersarenotneeded,itispossibletoremovethemfromthefunctionsothatitlookslike: 你可能已经注意到了:error和bytes_transferred参数并没有在handle_write()函数体内被应用。因此,如果参数并不是必须的,我们可以移除它们,如下所示: voidhandle_write(){}Theboost::asio::async_write()callusedtoinitiatethecallcanthenbechangedtojust: 用来发起呼叫的boost::asio::async_write()函数通常可以被改写成下面这样: