回显服务端/client

回显服务端/client

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

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

我们首先实现一个同步应用。然后实现一个异步应用,以便你能够很easy对照他们:

回显服务端/client

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

TCP回显服务端/client

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

我们会展示编码内容,比方同步client,同步服务端。异步client和异步服务端。

TCP同步client

在大多数有价值的样例中。client通常比服务端编码要简单(由于服务端须要处理多个client请求)。

以下的代码展示了不符合这条规则的一个例外:

size_t read_complete(char <span class="hljs-subst">*</span> buf, const <span class="hljs-keyword">error_code</span> <span class="hljs-subst">&</span> err, size_t <span class="hljs-built_in">bytes</span>)
{
    <span class="hljs-keyword">if</span> ( err) <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    bool found <span class="hljs-subst">=</span> std<span class="hljs-tag">::find</span>(buf, buf <span class="hljs-subst">+</span> <span class="hljs-built_in">bytes</span>, <span class="hljs-string">'\n'</span>) <span class="hljs-subst"><</span> buf <span class="hljs-subst">+</span> <span class="hljs-built_in">bytes</span>;

    <span class="hljs-keyword">return</span> found <span class="hljs-subst">?<p></p></span>

<span class="hljs-number">0</span> : <span class="hljs-number">1</span>;
}
<span class="hljs-literal">void</span> sync_echo(std<span class="hljs-tag">::string</span> msg) {
msg <span class="hljs-subst">+=</span> <span class="hljs-string">"\n&#x201D;;
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 << "</span>server echoed our <span class="hljs-string">" << msg << "</span>: <span class="hljs-string">"<< (copy == msg ? "</span>OK<span class="hljs-string">" : "</span>FAIL<span class="hljs-string">") << std::endl;
sock.close();
}
int main(int argc, char* argv[]) {
char* messages[] = { "</span>John says hi<span class="hljs-string">", "</span>so does James<span class="hljs-string">", "</span>Lucy just got home<span class="hljs-string">", "</span>Boost<span class="hljs-built_in">.</span>Asio is Fun<span class="hljs-subst">!</span><span class="hljs-string">", 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();
}</span>

核心功能 sync_echo

它包括了连接到服务端。发送信息然后等待回显的全部逻辑。

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

_read()_方法的第三个參数是完毕处理句柄。当读取到完整消息时,它返回0。否则,它会返回我下一步(直到读取结束)能都到的最大的缓冲区大小。

在我们的样例中。返回结果始终是1,由于我永远不想读的消息比我们须要的很多其它。

在 _main()_中,我们创建了几个线程;每一个线程负责把消息发送到client,然后等待操作结束。

假设你执行这个程序,你会看到以下的输出:

<span class="hljs-built_in">server</span> echoed our John says hi: OK
<span class="hljs-built_in">server</span> echoed our so does James: OK
<span class="hljs-built_in">server</span> echoed our Lucy just got home: OK
<span class="hljs-built_in">server</span> echoed our Boost.Asio <span class="hljs-keyword">is</span> Fun!: OK

注意:由于我们是同步的,所以不须要调用 service.run()

TCP同步服务端

回显同步服务端的编写很easy。參考例如以下的代码片段:

io_service service;
size_t read_complete(<span class="hljs-keyword">char</span> * buff, <span class="hljs-keyword">const</span> error_code & err, size_t bytes) {
    <span class="hljs-keyword">if</span> ( err) <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    <span class="hljs-keyword">bool</span> found = <span class="hljs-built_in">std</span>::find(buff, buff + bytes, <span class="hljs-string">'\n'</span>) < buff + bytes;

    <span class="hljs-keyword">return</span> found ? <span class="hljs-number">0</span> : <span class="hljs-number">1</span>;
}
<span class="hljs-keyword">void</span> handle_connections() {
    ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(),<span class="hljs-number">8001</span>));
    <span class="hljs-keyword">char</span> buff[<span class="hljs-number">1024</span>];
    <span class="hljs-keyword">while</span> ( <span class="hljs-keyword">true</span>) {
        ip::tcp::socket sock(service);
        acceptor.accept(sock);
        <span class="hljs-keyword">int</span> bytes = read(sock, buffer(buff), boost::bind(read_complete,buff,_1,_2));
        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> msg(buff, bytes);
        sock.write_some(buffer(msg));
        sock.close();
    }
}
<span class="hljs-keyword">int</span> main(<span class="hljs-keyword">int</span> argc, <span class="hljs-keyword">char</span>* argv[]) {
    handle_connections();
}

服务端的逻辑主要在 handle_connections()。由于是单线程。我们接受一个client请求。读取它发送给我们的消息,然后回显。然后等待下一个连接。能够确定。当两个client同一时候连接时,第二个client须要等待服务端处理完第一个client的请求。

还是要注意由于我们是同步的,所以不须要调用 service.run()

TCP异步client

当我们開始异步时。编码会变得略微有点复杂。

我们会构建在 第二章 保持活动中展示的 _connection_类。

观察这个章节中接下来的代码,你会发现每一个异步操作启动了新的异步操作。以保持 _service.run()_一直工作。

首先,核心功能例如以下:

<span class="hljs-variable">#define</span> MEM_FN(x)       boost<span class="hljs-tag">::bind</span>(<span class="hljs-subst">&</span>self_type<span class="hljs-tag">::x</span>, shared_from_this())
<span class="hljs-variable">#define</span> MEM_FN1(x,y)    boost<span class="hljs-tag">::bind</span>(<span class="hljs-subst">&</span>self_type<span class="hljs-tag">::x</span>, shared_from_this(),y)
<span class="hljs-variable">#define</span> MEM_FN2(x,y,z)  boost<span class="hljs-tag">::bind</span>(<span class="hljs-subst">&</span>self_type<span class="hljs-tag">::x</span>, shared_from_this(),y,z)
class talk_to_svr : <span class="hljs-keyword">public</span> boost<span class="hljs-tag">::enable_shared_from_this</span><span class="hljs-subst"><</span>talk_to_svr<span class="hljs-subst">></span> , boost<span class="hljs-tag">::noncopyable</span> {
    typedef talk_to_svr self_type;
    talk_to_svr(const std<span class="hljs-tag">::string</span> <span class="hljs-subst">&</span> message) : sock_(service), started_(<span class="hljs-literal">true</span>), message_(message) {}
    <span class="hljs-literal">void</span> start(ip<span class="hljs-tag">::tcp</span><span class="hljs-tag">::endpoint</span> ep) {
        sock_<span class="hljs-built_in">.</span>async_connect(ep, MEM_FN1(on_connect,_1));
    }
<span class="hljs-keyword">public</span>:
    typedef boost<span class="hljs-tag">::system</span><span class="hljs-tag">::error_code</span> <span class="hljs-keyword">error_code</span>;
    typedef boost<span class="hljs-tag">::shared_ptr</span><span class="hljs-subst"><</span>talk_to_svr<span class="hljs-subst">></span> ptr;
    static ptr start(ip<span class="hljs-tag">::tcp</span><span class="hljs-tag">::endpoint</span> ep, const std<span class="hljs-tag">::string</span> <span class="hljs-subst">&</span>message) {
        ptr new_(<span class="hljs-literal">new</span> talk_to_svr(message));
        new_<span class="hljs-subst">-></span>start(ep);
        <span class="hljs-keyword">return</span> new_;
    }
    <span class="hljs-literal">void</span> stop() {
        <span class="hljs-keyword">if</span> ( <span class="hljs-subst">!</span>started_) <span class="hljs-keyword">return</span>;
        started_ <span class="hljs-subst">=</span> <span class="hljs-literal">false</span>;
        sock_<span class="hljs-built_in">.</span>close();
    }
    bool started() { <span class="hljs-keyword">return</span> started_; }
    <span class="hljs-attribute">...</span>
<span class="hljs-keyword">private</span>:
    ip<span class="hljs-tag">::tcp</span><span class="hljs-tag">::socket</span> sock_;
    enum { max_msg <span class="hljs-subst">=</span> <span class="hljs-number">1024</span> };
    char read_buffer_<span class="hljs-preprocessor">[</span>max_msg<span class="hljs-preprocessor">]</span><span class="hljs-markup">;
    char write_buffer_</span><span class="hljs-preprocessor">[</span>max_msg<span class="hljs-preprocessor">]</span><span class="hljs-markup">;
    bool started_;
    std::string message_;
}; </span>

我们须要一直使用指向 _talk_to_svr_的智能指针。这种话当在 _tack_to_svr_的实例上有异步操作时,那个实例是一直活动的。

为了避免错误。比方在栈上构建一个 talk_to_svr_对象的实例时,我把构造方法设置成了私有并且不同意拷贝构造(继承自 _boost::noncopyable)。

我们有了核心方法,比方 start(),stop()_和 _started(),它们所做的事情也正如它们名字表达的一样。假设须要建立连接,调用 _talk_to_svr::start(endpoint, message)_就可以。我们同一时候另一个read缓冲区和一个write缓冲区。( _read_buufer__和 _write_buffer__)。

MEM_FN _是一个方便使用的宏,它们通过shared_ptr_from_this()_方法强制使用一个指向 _this_的智能指针。

以下的几行代码和之前的解释很不同:


sock_<span class="hljs-built_in">.</span>async_connect(ep,boost<span class="hljs-tag">::bind</span>(<span class="hljs-subst">&</span>talk_to_svr<span class="hljs-tag">::on_connect</span>,shared_ptr_from_this(),_1));
sock_<span class="hljs-built_in">.</span>async_connect(ep, boost<span class="hljs-tag">::bind</span>(<span class="hljs-subst">&</span>talk_to_svr<span class="hljs-tag">::on_connect</span>,this,_1));

在上述样例中。我们正确的创建了 _async_connect_的完毕处理句柄;在调用完毕处理句柄之前它会保留一个指向 _talk_to_server_实例的智能指针,从而保证当其发生时 _talk_to_server_实例还是保持活动的。

在接下来的样例中,我们错误地创建了完毕处理句柄。当它被调用时。 _talk_to_server_实例很可能已经被释放了。

从socket读取或写入时,你使用例如以下的代码片段:

void <span class="hljs-function">do_read()</span> {
    <span class="hljs-function">async_read(sock_, <span class="hljs-function">buffer(read_buffer_)</span>, <span class="hljs-function">MEM_FN2(read_complete,_1,_2)</span>, <span class="hljs-function">MEM_FN2(on_read,_1,_2)</span>)</span>;
}
void <span class="hljs-function">do_write(const std::string & msg)</span> {
    if ( !<span class="hljs-function">started()</span> ) return;
    std<span class="hljs-value">::copy(msg.begin(), msg.end(), write_buffer_);</span>
    sock_<span class="hljs-class">.async_write_some</span>( <span class="hljs-function">buffer(write_buffer_, msg.<span class="hljs-function">size()</span>)</span>, <span class="hljs-function">MEM_FN2(on_write,_1,_2)</span>);
}
size_t <span class="hljs-function">read_complete(const boost::system::error_code & err, size_t bytes)</span> {

}

_do_read()_方法会保证当 _on_read()_被调用的时候,我们从服务端读取一行。 _do_write()_方法会先把信息复制到缓冲区(考虑到当 _async_write_发生时msg可能已经超出范围被释放),然后保证实际的写入操作发生时 _on_write()_被调用。

然后是最重要的方法,这种方法包括了类的主要逻辑:

<span class="hljs-literal">void</span> on_connect(const <span class="hljs-keyword">error_code</span> <span class="hljs-subst">&</span> err) {
    <span class="hljs-keyword">if</span> ( <span class="hljs-subst">!</span>err)      do_write(message_ <span class="hljs-subst">+</span> <span class="hljs-string">"\n"</span>);
    <span class="hljs-keyword">else</span>            stop();
}
<span class="hljs-literal">void</span> on_read(const <span class="hljs-keyword">error_code</span> <span class="hljs-subst">&</span> err, size_t <span class="hljs-built_in">bytes</span>) {
    <span class="hljs-keyword">if</span> ( <span class="hljs-subst">!</span>err) {
        std<span class="hljs-tag">::string</span> copy(read_buffer_, <span class="hljs-built_in">bytes</span> <span class="hljs-subst">-</span> <span class="hljs-number">1</span>);
        std<span class="hljs-tag">::cout</span> <span class="hljs-subst"><<</span> <span class="hljs-string">"server echoed our "</span> <span class="hljs-subst"><<</span> message_ <span class="hljs-subst"><<</span> <span class="hljs-string">": "</span> <span class="hljs-subst"><<</span> (copy <span class="hljs-subst">==</span> message_ <span class="hljs-subst">?</span> <span class="hljs-string">"OK"</span> : <span class="hljs-string">"FAIL"</span>) <span class="hljs-subst"><<</span> std<span class="hljs-tag">::endl</span>;
    }
    stop();
}
<span class="hljs-literal">void</span> on_write(const <span class="hljs-keyword">error_code</span> <span class="hljs-subst">&</span> err, size_t <span class="hljs-built_in">bytes</span>) {
    do_read();
}

当连接成功之后。我们发送消息到服务端, do_write()。当write操作结束时, _on_write()_被调用,它初始化了一个 _do_read()_方法。当 _do_read()_完毕时。 _on_read()_被调用;这里,我们简单的检查一下返回的信息是否是服务端的回显。然后退出服务。

我们会发送三个消息到服务端让它变得更有趣一点:

int main(int argc, char* argv[]) {
    <span class="hljs-symbol">ip:</span><span class="hljs-symbol">:tcp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:endpoint</span> ep( <span class="hljs-symbol">ip:</span><span class="hljs-symbol">:address</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:from_string</span>(<span class="hljs-string">"127.0.0.1"</span>), <span class="hljs-number">8001</span>);
    char* messages[] = { <span class="hljs-string">"John says hi"</span>, <span class="hljs-string">"so does James"</span>, <span class="hljs-string">"Lucy got home"</span>, <span class="hljs-number">0</span> };
    <span class="hljs-keyword">for</span> ( char ** message = messages; *message; ++message) {
        <span class="hljs-symbol">talk_to_svr:</span><span class="hljs-symbol">:start</span>( ep, *message);
        <span class="hljs-symbol">boost:</span><span class="hljs-symbol">:this_thread</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:sleep</span>( <span class="hljs-symbol">boost:</span><span class="hljs-symbol">:posix_time</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:millisec</span>(<span class="hljs-number">100</span>));
    }
    service.run();
}

上述的代码会生成例如以下的输出:

<span class="hljs-keyword">server</span> echoed our John says hi: OK
<span class="hljs-keyword">server</span> echoed our so does James: OK
<span class="hljs-keyword">server</span> echoed our Lucy just got home: OK

TCP异步服务端

核心功能和同步服务端的功能相似,例如以下:

class talk_to_client : <span class="hljs-keyword">public</span> boost<span class="hljs-tag">::enable_shared_from_this</span><span class="hljs-subst"><</span>talk_to_
   client<span class="hljs-subst">></span>, boost<span class="hljs-tag">::noncopyable</span> {
    typedef talk_to_client self_type;
    talk_to_client() : sock_(service), started_(<span class="hljs-literal">false</span>) {}
<span class="hljs-keyword">public</span>:
    typedef boost<span class="hljs-tag">::system</span><span class="hljs-tag">::error_code</span> <span class="hljs-keyword">error_code</span>;
    typedef boost<span class="hljs-tag">::shared_ptr</span><span class="hljs-subst"><</span>talk_to_client<span class="hljs-subst">></span> ptr;
    <span class="hljs-literal">void</span> start() {
        started_ <span class="hljs-subst">=</span> <span class="hljs-literal">true</span>;
        do_read();
    }

    static ptr new_() {
        ptr new_(<span class="hljs-literal">new</span> talk_to_client);
        <span class="hljs-keyword">return</span> new_;
    }
    <span class="hljs-literal">void</span> stop() {
        <span class="hljs-keyword">if</span> ( <span class="hljs-subst">!</span>started_) <span class="hljs-keyword">return</span>;
        started_ <span class="hljs-subst">=</span> <span class="hljs-literal">false</span>;
        sock_<span class="hljs-built_in">.</span>close();
    }
    ip<span class="hljs-tag">::tcp</span><span class="hljs-tag">::socket</span> <span class="hljs-subst">&</span> sock() { <span class="hljs-keyword">return</span> sock_;}
    <span class="hljs-attribute">...</span>
<span class="hljs-keyword">private</span>:
    ip<span class="hljs-tag">::tcp</span><span class="hljs-tag">::socket</span> sock_;
    enum { max_msg <span class="hljs-subst">=</span> <span class="hljs-number">1024</span> };
    char read_buffer_<span class="hljs-preprocessor">[</span>max_msg<span class="hljs-preprocessor">]</span><span class="hljs-markup">;
    char write_buffer_</span><span class="hljs-preprocessor">[</span>max_msg<span class="hljs-preprocessor">]</span><span class="hljs-markup">;
    bool started_;
};</span>

由于我们是很简单的回显服务,这里不须要 _is_started()_方法。对每一个client。仅仅读取它的消息,回显,然后关闭它。

_do_read(),do_write()_和 _read_complete()_方法和TCP同步服务端的全然一致。

基本的逻辑相同是在 _on_read()_和 _on_write()_方法中:

<span class="hljs-literal">void</span> on_read(const <span class="hljs-keyword">error_code</span> <span class="hljs-subst">&</span> err, size_t <span class="hljs-built_in">bytes</span>) {
    <span class="hljs-keyword">if</span> ( <span class="hljs-subst">!</span>err) {
        std<span class="hljs-tag">::string</span> msg(read_buffer_, <span class="hljs-built_in">bytes</span>);
        do_write(msg <span class="hljs-subst">+</span> <span class="hljs-string">"\n"</span>);
    }
    stop();
}
<span class="hljs-literal">void</span> on_write(const <span class="hljs-keyword">error_code</span> <span class="hljs-subst">&</span> err, size_t <span class="hljs-built_in">bytes</span>) {
    do_read();
}

对client的处理例如以下:

ip<span class="hljs-tag">::tcp</span><span class="hljs-tag">::acceptor</span> acceptor(service, ip<span class="hljs-tag">::tcp</span><span class="hljs-tag">::endpoint</span>(ip<span class="hljs-tag">::tcp</span><span class="hljs-tag">::v4</span>(),<span class="hljs-number">8001</span>));
<span class="hljs-literal">void</span> handle_accept(talk_to_client<span class="hljs-tag">::ptr</span> client, const <span class="hljs-keyword">error_code</span> <span class="hljs-subst">&</span> err)
{
    client<span class="hljs-subst">-></span>start();
    talk_to_client<span class="hljs-tag">::ptr</span> new_client <span class="hljs-subst">=</span> talk_to_client<span class="hljs-tag">::new_</span>();
    acceptor<span class="hljs-built_in">.</span>async_accept(new_client<span class="hljs-subst">-></span>sock(), boost<span class="hljs-tag">::bind</span>(handle_accept,new_client,_1));
}
int main(int argc, char<span class="hljs-subst">*</span> argv<span class="hljs-preprocessor">[</span><span class="hljs-preprocessor">]</span><span class="hljs-markup">) {
    talk_to_client::ptr client = talk_to_client::new_();
    acceptor.async_accept(client->sock(), boost::bind(handle_accept,client,_1));
    service.run();
} </span>

每一次client连接到服务时, _handle_accept_被调用,它会异步地从client读取。然后相同异步地等待一个新的client。

代码

你会在这本书对应的代码中得到全部4个应用(TCP回显同步client,TCP回显同步服务端,TCP回显异步client,TCP回显异步服务端)。

当測试时。你能够使用随意client/服务端组合(比方,一个异步client和一个同步服务端)。

UDP回显服务端/client

由于UDP不能保证全部信息都抵达接收者。我们不能保证”信息以回车结尾”。

没收到消息,我们仅仅是回显,可是没有socket去关闭(在服务端)。由于我们是UDP。

UDP同步回显client

UDP回显client比TCP回显client要简单:

<span class="hljs-symbol">ip:</span><span class="hljs-symbol">:udp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:endpoint</span> ep( <span class="hljs-symbol">ip:</span><span class="hljs-symbol">:address</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:from_string</span>(<span class="hljs-string">"127.0.0.1"</span>), <span class="hljs-number">8001</span>);
void sync_echo(<span class="hljs-symbol">std:</span><span class="hljs-symbol">:string</span> msg) {
    <span class="hljs-symbol">ip:</span><span class="hljs-symbol">:udp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:socket</span> sock(service, <span class="hljs-symbol">ip:</span><span class="hljs-symbol">:udp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:endpoint</span>(<span class="hljs-symbol">ip:</span><span class="hljs-symbol">:udp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:v4</span>(), <span class="hljs-number">0</span>));
    sock.send_to(buffer(msg), ep);
    char buff[<span class="hljs-number">1024</span>];
    <span class="hljs-symbol">ip:</span><span class="hljs-symbol">:udp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:endpoint</span> sender_ep;
    int bytes = sock.receive_from(buffer(buff), sender_ep);
    <span class="hljs-symbol">std:</span><span class="hljs-symbol">:string</span> copy(buff, bytes);
    <span class="hljs-symbol">std:</span><span class="hljs-symbol">:cout</span> << <span class="hljs-string">"server echoed our "</span> << msg << <span class="hljs-string">": "</span> << (copy == msg ?<p></p>

<span class="hljs-string">"OK"</span> <span class="hljs-symbol">:</span> <span class="hljs-string">"FAIL"</span>) << <span class="hljs-symbol">std:</span><span class="hljs-symbol">:endl</span>;
sock.close();
}
int main(int argc, char* argv[]) {
char* messages[] = { <span class="hljs-string">"John says hi"</span>, <span class="hljs-string">"so does James"</span>, <span class="hljs-string">"Lucy got home"</span>, <span class="hljs-number">0</span> };
<span class="hljs-symbol">boost:</span><span class="hljs-symbol">:thread_group</span> threads;
<span class="hljs-keyword">for</span> ( char ** message = messages; *message; ++message) {
threads.create_thread( <span class="hljs-symbol">boost:</span><span class="hljs-symbol">:bind</span>(sync_echo, *message));
<span class="hljs-symbol">boost:</span><span class="hljs-symbol">:this_thread</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:sleep</span>( <span class="hljs-symbol">boost:</span><span class="hljs-symbol">:posix_time</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:millisec</span>(<span class="hljs-number">100</span>));
}
threads.join_all();
}

全部的逻辑都在 _synch_echo()_中。连接到服务端。发送消息,接收服务端的回显,然后关闭连接。

UDP同步回显服务端

UDP回显服务端会是你写过的最简单的服务端:

io_service service;
void handle_connections() {
    char buff[<span class="hljs-number">1024</span>];
    <span class="hljs-symbol">ip:</span><span class="hljs-symbol">:udp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:socket</span> sock(service, <span class="hljs-symbol">ip:</span><span class="hljs-symbol">:udp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:endpoint</span>(<span class="hljs-symbol">ip:</span><span class="hljs-symbol">:udp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:v4</span>(), <span class="hljs-number">8001</span>));
    <span class="hljs-keyword">while</span> ( <span class="hljs-keyword">true</span>) {
        <span class="hljs-symbol">ip:</span><span class="hljs-symbol">:udp</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:endpoint</span> sender_ep;
        int bytes = sock.receive_from(buffer(buff), sender_ep);
        <span class="hljs-symbol">std:</span><span class="hljs-symbol">:string</span> msg(buff, bytes);
        sock.send_to(buffer(msg), sender_ep);
    }
}
int main(int argc, char* argv[]) {
    handle_connections();
}

它很简单。并且能很好的自释。

我把异步UDPclient和服务端留给读者当作一个练习。

总结

我们已经写了完整的应用。终于让Boost.Asio得以工作。

回显应用是開始学习一个库时很好的工具。你能够常常学习和执行这个章节所展示的代码,这样你就能够很easy地记住这个库的基础。

在下一章。我们会建立更复杂的client/服务端应用,我们要确保避免低级错误,比方内存泄漏,死锁等等。

Original: https://www.cnblogs.com/wgwyanfs/p/7403661.html
Author: wgwyanfs
Title: 回显服务端/client

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/544087/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 各种锁、volatile、synchronized、单例模式

    1、 锁 (1)各种锁 == 可重入锁(递归锁):广义上的可重入锁,并非单指ReentrantLock。指的是同一线程外层函数获得锁后,内层递归函数仍然有获得该锁的代码,但不锁影响…

    Java 2023年6月5日
    059
  • Qt QThread线程的简单使用

    一、概述 案例:在GUI编程中一般把耗时任务放入单独的线程中执行,用以防止主线程卡死,导致页面播放不流畅等问题。下面就简单说下在Qt中使用其自带的QThread来实现一个线程 实现…

    Java 2023年5月30日
    096
  • Java实现动态数组【数据结构与算法】

    1、数组 类型固定、长度固定 连续的内存空间 顺序存储、随机读取 查询快、新增删除慢。 最好初始化的时候就指定数组大小。这样就可以避免一定的数组扩容出现的内存消耗。 import …

    Java 2023年6月15日
    083
  • github学生认证——申请学生开发包

    写在前面 申请学生认证的好处: GitHub学生的免费AWS Educate入门帐户,价值100美元。 专业的桌面IDE:IntelliJ IDEA,PyCharm等。学生的免费订…

    Java 2023年6月9日
    0107
  • RabbitMQ实现订单超时案例

    人间清醒 用戶在购买商品的时候通常会预购然后没付款,没付款的订单通常会被设置一个自动超时时间如30分钟后超时,所以我们要在订单到30分钟后自动将超时的订单取消。 DelayQueu…

    Java 2023年6月5日
    092
  • DNS分离解析IPV6与IPV4用户

    IPV6改造中经常会遇到,网站使用了CDN,但是CDN厂商还不支持IPV6的情况,而AAAA、A、CNAME记录互相冲突,想实现IPV6用户得到AAAA记录,IPV4用户得到CNA…

    Java 2023年6月5日
    083
  • Java 获取Excel中的表单控件

    Excel中可通过【开发工具】菜单栏下插入表单控件,如文本框、单选按钮、复选框、组合框等等,插入后的控件可执行设置控件格式,如大小、是否锁定、位置、可选文字、数据源区域、单元格链接…

    Java 2023年5月29日
    092
  • AQS源码三视-JUC系列

    AQS源码三视-JUC系列 前两篇文章介绍了AQS的核心同步机制,使用CHL同步队列实现线程等待和唤醒,一个int值记录资源量。为上层各式各样的同步器实现画好了模版,像已经介绍到的…

    Java 2023年6月13日
    095
  • 再见收费的Navicat!操作所有数据库就靠它了!

    作为一名开发者,免不了要和数据库打交道,于是我们就需要一款顺手的数据库管理工具。很长一段时间里,Navicat 都是我的首选,但最近更换了一台新电脑,之前的绿色安装包找不到了。 于…

    Java 2023年6月9日
    0102
  • 6、spring注解+springMVC注解+Mybatis注解+log4j+idea+maven

    1、项目结构如下 2、编辑pom.xml文件配置依赖 1 <?xml version="1.0" encoding="UTF-8"?&…

    Java 2023年6月13日
    096
  • 程序员转行做运营,曾被逼得每天想离职,最后…

    做程序员的时候的时候觉得做运营肯定很轻松,转运营后我曾经每天都想着离职。 用了一年才终于适应了运营这个角色,这一年的经历颠覆我对运营这个工作本身的认知,也改变了我对个人成长、职场发…

    Java 2023年6月9日
    0202
  • 【校招VIP】[Java][一本][6分]按照真实的技能点进行业务描述

    关注【校招VIP】公众号 ,回复【简历 】,添加校招顾问微信,即可获取简历指导! 本份简历是一位21届一本java同学的简历,简历评分6分。 一、学员简历 二、指导意见 简历版式问…

    Java 2023年6月5日
    093
  • 【Java】【51】Quartz定时器

    前言: 但是,有的时候我们的任务是动态的。比如,可以在后台添加任意个数任意时间的推送短信任务,任务没有开始之前,可以更改推送时间。这就需要用到Quartz动态添加、修改和删除定时任…

    Java 2023年5月29日
    076
  • SpringResourceBundleMessageSource示例(转)

    对于支持国际化的应用程序,它需要能够为不同的语言环境解析文本消息。Spring的应用程序上下文能够通过键解析目标语言环境的文本消息。通常,一个语言环境的消息应存储在一个单独的属性文…

    Java 2023年5月30日
    067
  • Docker安装和卸载(centos)

    Docker安装和卸载 一,已安装Docker,卸载Docker 1.方法一 sudo yum remove docker \ docker-client \ docker-cli…

    Java 2023年6月15日
    088
  • Java基础— 小应用:比较两个数值大小

    在日常生活中,经常会要求比较两个数的大小。于是就想写个博客稍微总结一下。 package com.basic.day02; public class CompareTwoValue…

    Java 2023年6月7日
    078
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球