网络编程分为同步模式和异步模式:
同步模式是有一个数据块客户端发送过来,服务端就必须处理完才能处理下一个数据块;
异步模式是客户端发送的数据块放入缓存队列;
异步处理不阻塞,同步模式是阻塞式的。
同步模式介绍
大家好!我是同步方式!
我的主要特点就是执着!所有的操作都要完成或出错才会返回,不过偶的执着被大家称之为阻塞,实在是郁闷~~(场下一片嘘声),其实这样也是有好处的,比如逻辑清晰,编程比较容易。
在服务器端,我会做个socket交给acceptor对象,让它一直等客户端连进来,连上以后再通过这个socket与客户端通信, 而所有的通信都是以阻塞方式进行的,读完或写完才会返回。
在客户端也一样,这时我会拿着socket去连接服务器,当然也是连上或出错了才返回,最后也是以阻塞的方式和服务器通信。
有人认为同步方式没有异步方式高效,其实这是片面的理解。在单线程的情况下可能确实如此,我不能利用耗时的网络操作这段时间做别的事情,不是好的统筹方法。不过这个问题可以通过多线程来避免,比如在服务器端让其中一个线程负责等待客户端连接,连接进来后把socket交给另外的线程去和客户端通信,这样与一个客户端通信的同时也能接受其它客户端的连接,主线程也完全被解放了出来。
同步模式编程
服务器端
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
//流程:
//① 创建io_service对象
//② 打包IP和端口
//③ 利用ios和ep创建连接器
//④ 利用io_service对象创建socket
//⑤ 利用连接器连接
//⑥ 读写信息
int _tmain(int argc, _TCHAR* argv[])
{
boost::asio::io_service ios;//asio网络编程必须io_service对象
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(),12248);//打包IP和端口
boost::asio::ip::tcp::acceptor ac(ios,ep);//利用ios和ep创建连接器
while (true)//监听客户端
{
boost::asio::ip::tcp::socket sock(ios);//利用io_service对象创建socket
ac.accept(sock);//连接客户端的socket
//接收客户端消息,如果没有接收到,就一直卡在这里
cout << sock.remote_endpoint().address() << endl;//显示连接进来的客户端信息
boost::system::error_code ec;
if (ec)
{
cout << boost::system::system_error(ec).what() << endl;
break;
}
else
{
sock.write_some(boost::asio::buffer("hello world!"), ec);
}
}
return 0;
}
补充说明:
服务器端,做个socket交给acceptor对象,让它一直等客户端连进来,连上以后再通过这个socket与客户端通信。如果一直连不上,就卡在这里。
客户端
#include "stdafx.h"
#include
#include
#include
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
try
{
boost::asio::io_service ios;//asio网络编程必须io_service对象
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"),12248);//打包IP和端口
boost::asio::ip::tcp::socket sock(ios);//创建socket
sock.connect(ep);//利用IP和端口连接服务器
while (true)
{
boost::array buf;
boost::system::error_code ec;
size_t len = sock.read_some(boost::asio::buffer(buf),ec);
if (ec == boost::asio::error::eof)
break;
else if (ec)
throw boost::system::system_error(ec);
cout.write(buf.data(),len);
}
}
catch (exception &e)
{
cout << e.what() << endl;
}
return 0;
}
此外,在同步模式下,可以通过多线程来避免阻塞。在服务器端让其中一个线程负责等待客户端连接,连接进来后把socket交给另外的线程去和客户端通信,这样与一个客户端通信的同时也能接受其它客户端的连接,主线程也完全被解放了出来。示例如下:
//服务器端
boost::asio::io_service ios;
//asio编程必须io_service对象,服务端和客户端创建socket和服务端创建acceptor对象要用
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(),9800);
//TCP协议的服务器所在的IP和网络编程开放的端口,客户端连接的IP和端口
boost::asio::ip::tcp::acceptor acceptor(ios,ep);
//监听连接
while(1)
{
boost::asio::ip::tcp::socket sock(ios);//创建socket连接对象
acceptor.accept(sock);
cont<<sock.remote_endpoint().address()<<endl;
boost::thread(boost::bind(svr_handle,sock)).detach();
}
//交互处理
void svr_handle(boost::asio::ip::tcp::socket sock)
{
string msg;
sock.write_some(boost::asio::buffer("hello world"));
char msg[1024];
sock.read_some(boost::asio::buffer(msg));
cout<<"client send msg:"<<msg<<endl;
}
2.2 异步模式
和同步方式不同,异步方式从来不花时间去等那些龟速的IO操作,我只是向系统说一声要做什么,然后就可以做其它事去了。如果系统完成了操作, 系统就会通过我之前给它的回调对象来通知我。
在ASIO库中,异步方式的函数或方法名称前面都有“async_ ” 前缀,函数参数里会要求放一个回调函数。异步操作执行后不管有没有完成都会立即返回,这时可以做一些其它事,直到回调函数被调用,说明异步操作已经完成。
在ASIO中很多回调函数都只接受一个boost::system::error_code
参数,在实际使用时肯定是不够的,所以一般使用仿函数携带一堆相关数据作为回调,或者使用boost::bind
来绑定一堆数据。
另外要注意的是,只有io_service类的run()方法运行之后回调对象才会被调用,否则即使系统已经完成了异步操作也不会有任 务动作。
服务端
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
class AsyncServer
{
public:
//构造函数
AsyncServer(boost::asio::io_service &io, boost::asio::ip::tcp::endpoint &ep) :ios(io), ac(io,ep)
{
this->start();
}
private:
boost::asio::io_service &ios;
boost::asio::ip::tcp::acceptor ac;
typedef boost::shared_ptr sock_ptr;
//启动异步连接,接受客户端的连接请求
void start()
{
sock_ptr sock(new boost::asio::ip::tcp::socket(ios));
ac.async_accept(*sock, boost::bind(&AsyncServer::accept_handler, this, boost::asio::placeholders::error, sock));
}
void accept_handler(const boost::system::error_code &ec, sock_ptr &sock)
{
if (ec)
return;
cout << "客户端地址" <remote_endpoint().address() << endl;
cout << "客户端端口" <remote_endpoint().port() <async_write_some(boost::asio::buffer("Hello World"),
boost::bind(&AsyncServer::write_handler,this,boost::asio::placeholders::error));
//再次启动异步接受连接
start();
}
void write_handler(const boost::system::error_code &)
{
cout << "服务端发送消息完成" << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
try
{
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(),12248);
AsyncServer server(ios,ep);//启动异步服务
ios.run();
}
catch (exception &e)
{
cout << e.what() << endl;
}
return 0;
}
客户端
#include "stdafx.h"
#include
#include
#include
#include
#include
using namespace std;
class AsyncClient
{
public:
AsyncClient(boost::asio::io_service &io, boost::asio::ip::tcp::endpoint ep):ios(io),ep(ep)
{
this->start();
}
private:
boost::asio::io_service &ios;
typedef boost::shared_ptr sock_ptr;
boost::asio::ip::tcp::endpoint ep;
char m_RcvStr[1024];
void start()
{
sock_ptr sock(new boost::asio::ip::tcp::socket(ios));
sock->async_connect(ep, boost::bind(&AsyncClient::connect_handler, this, boost::asio::placeholders::error, sock));
}
void connect_handler(const boost::system::error_code &ec, sock_ptr &sock)
{
if (ec)
return;
cout << "服务端地址" <remote_endpoint().address() << endl;
cout << "服务端端口" <remote_endpoint().port() <async_read_some(boost::asio::buffer(m_RcvStr), boost::bind(&AsyncClient::read_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void read_handler(const boost::system::error_code &ec,size_t i)
{
if (ec)
return;
cout << "读数据成功" << endl;
cout << m_RcvStr << endl;
cout << i<<endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
try
{
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 12248);
AsyncClient client(ios,ep);
ios.run();
}
catch (exception &e)
{
cout << e.what() << endl;
}
return 0;
}
3. 补充说明
3.1 io_serveice类不支持拷贝构造
如下程序:
class A
{
private:
io_service io;
public:
A(io_service io_): io(io_)
{
}
};
程序执行时,会报错;原因在于没有使用引用,而是使用了拷贝。
应该修改成:
io_service &io;