回显服务端/客户端

回显服务端/客户端

在这一章,我们将会实现一个小的客户端/服务端应用,这可能会是你写过的最简单的客户端/服务端应用。回显应用就是一个把客户端发过来的任何内容回显给其本身,然后关闭连接的的服务端。这个服务端可以处理任何数量的客户端。每个客户端连接之后发送一个消息,服务端接收到完成消息后把它发送回去。在那之后,服务端关闭连接。

因此,每个回显客户端连接到服务端,发送一个消息,然后读取服务端返回的结果,确保这是它发送给服务端的消息就结束和服务端的会话。

我们首先实现一个同步应用,然后实现一个异步应用,以便你可以很容易对比他们:

为了节省空间,下面的代码有一些被裁剪掉了。你可以在附加在这本书的代码中看到全部的代码。

TCP回显服务端/客户端

对于TCP而言,我们需要一个额外的保证;每一个消息以换行符结束(‘\n’)。编写一个同步回显服务端/客户端非常简单。

我们会展示编码内容,比如同步客户端,同步服务端,异步客户端和异步服务端。

TCP同步客户端

在大多数有价值的例子中,客户端通常比服务端编码要简单(因为服务端需要处理多个客户端请求)。 下面的代码展示了不符合这条规则的一个例外:

size_t read_complete(char * buf, const error_code & err, size_t bytes){if ( err) return 0;bool found ;: 1;}void sync_echo(std::string msg) {msg += “\n”;ip::tcp::socket sock(service);sock.connect(ep);sock.write_some(buffer(msg));char buf[1024];int bytes = read(sock, buffer(buf), boost::bind(read_complete,buf,_1,_2));std::string copy(buf, bytes – 1);msg = msg.substr(0, msg.size() – 1);std::cout << “server echoed our ” << msg << “: “<< (copy == msg ? “OK” : “FAIL”) << std::endl;sock.close();}int main(int argc, char* argv[]) {char* messages[] = { “John says hi”, “so does James”, “Lucy just got home”, “Boost.Asio is Fun!”, 0 };boost::thread_group threads;for ( char ** message = messages; *message; ++message) {threads.create_thread( boost::bind(sync_echo, *message));boost::this_thread::sleep( boost::posix_time::millisec(100));}threads.join_all();}

核心功能sync_echo。它包含了连接到服务端,发送信息然后等待回显的所有逻辑。

你会发现,在读取时,我使用了自由函数read(),因为我想要读’\n’之前的所有内容。sock.read_some()方法满足不了这个要求,因为它只会读可用的,而不是全部的消息。

read()方法的第三个参数是完成处理句柄。当读取到完整消息时,它返回0。否则,它会返回我下一步(直到读取结束)能都到的最大的缓冲区大小。在我们的例子中,返回结果始终是1,因为我永远不想读的消息比我们需要的更多。

在main()中,我们创建了几个线程;每个线程负责把消息发送到客户端,然后等待操作结束。如果你运行这个程序,你会看到下面的输出:

server echoed our John says hi: OKserver echoed our so does James: OKserver echoed our Lucy just got home: OKserver echoed our Boost.Asio is Fun!: OK

注意:因为我们是同步的,所以不需要调用service.run()。

TCP同步服务端

回显同步服务端的编写非常容易,参考如下的代码片段:

io_service service;size_t read_complete(char * buff, const error_code & err, size_t bytes) {if ( err) return 0;bool found = std::find(buff, buff + bytes, ‘\n’) < buff + bytes;// 我们一个一个读取直到读到回车,不缓存return found ? 0 : 1;}void handle_connections() {ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(),8001));char buff[1024];while ( true) {ip::tcp::socket sock(service);acceptor.accept(sock);int bytes = read(sock, buffer(buff), boost::bind(read_complete,buff,_1,_2));std::string msg(buff, bytes);sock.write_some(buffer(msg));sock.close();}}int main(int argc, char* argv[]) {handle_connections();}

服务端的逻辑主要在handle_connections()。因为是单线程,我们接受一个客户端请求,,读取它发送给我们的消息,然后回显,然后等待下一个连接。可以确定,当两个客户端同时连接时,第二个客户端需要等待服务端处理完第一个客户端的请求。

还是要注意因为我们是同步的,所以不需要调用service.run()。

TCP异步客户端

当我们开始异步时,编码会变得稍微有点复杂。我们会构建在第二章 保持活动中展示的connection类。

观察这个章节中接下来的代码,你会发现每个异步操作启动了新的异步操作,以保持service.run()一直工作。 首先,核心功能如下:

#define MEM_FN(x)boost::bind(&self_type::x, shared_from_this())#define MEM_FN1(x,y) boost::bind(&self_type::x, shared_from_this(),y)#define MEM_FN2(x,y,z) boost::bind(&self_type::x, shared_from_this(),y,z)class talk_to_svr : public boost::enable_shared_from_this<talk_to_svr> , boost::noncopyable {typedef talk_to_svr self_type;talk_to_svr(const std::string & message) : sock_(service), started_(true), message_(message) {}void start(ip::tcp::endpoint ep) {sock_.async_connect(ep, MEM_FN1(on_connect,_1));}public:typedef boost;typedef boost::shared_ptr<talk_to_svr> ptr;static ptr start(ipmessage) {ptr new_(new talk_to_svr(message));new_->start(ep);return new_;}void stop() {if ( !started_) return;started_ = false;sock_.close();}bool started() { return started_; }…private:ip::tcp::socket sock_;enum { max_msg = 1024 };char read_buffer_[max_msg];char write_buffer_[max_msg];bool started_;std::string message_; }; 也许这就是一个人无法抗拒的命运,有你、有我、也有他。

回显服务端/客户端

相关文章:

你感兴趣的文章:

标签云: