Added chat example.
This commit is contained in:
parent
35c6e23638
commit
d0c3eb74dd
@ -15,6 +15,8 @@ noinst_PROGRAMS = \
|
||||
tests/timed_dgram_recv_test \
|
||||
tests/timer_test \
|
||||
tests/tpc_echo_server_test \
|
||||
examples/chat/chat_client \
|
||||
examples/chat/chat_server \
|
||||
examples/echo/async_tcp_echo_server \
|
||||
examples/echo/async_udp_echo_server \
|
||||
examples/echo/blocking_tcp_echo_client \
|
||||
@ -40,6 +42,8 @@ tests_timed_connect_test_SOURCES = tests/timed_connect_test.cpp
|
||||
tests_timed_dgram_recv_test_SOURCES = tests/timed_dgram_recv_test.cpp
|
||||
tests_timer_test_SOURCES = tests/timer_test.cpp
|
||||
tests_tpc_echo_server_test_SOURCES = tests/tpc_echo_server_test.cpp
|
||||
examples_chat_chat_client_SOURCES = examples/chat/chat_client.cpp
|
||||
examples_chat_chat_server_SOURCES = examples/chat/chat_server.cpp
|
||||
examples_echo_async_tcp_echo_server_SOURCES = examples/echo/async_tcp_echo_server.cpp
|
||||
examples_echo_async_udp_echo_server_SOURCES = examples/echo/async_udp_echo_server.cpp
|
||||
examples_echo_blocking_tcp_echo_client_SOURCES = examples/echo/blocking_tcp_echo_client.cpp
|
||||
|
@ -19,6 +19,8 @@ all: \
|
||||
tests\timed_dgram_recv_test.exe \
|
||||
tests\timer_test.exe \
|
||||
tests\tpc_echo_server_test.exe \
|
||||
examples\chat\chat_client.exe \
|
||||
examples\chat\chat_server.exe \
|
||||
examples\echo\async_tcp_echo_server.exe \
|
||||
examples\echo\async_udp_echo_server.exe \
|
||||
examples\echo\blocking_tcp_echo_client.exe \
|
||||
|
@ -21,6 +21,8 @@ TEST_EXES = \
|
||||
tests/tpc_echo_server_test.exe
|
||||
|
||||
EXAMPLE_EXES = \
|
||||
examples\chat\chat_client.exe \
|
||||
examples\chat\chat_server.exe \
|
||||
examples\echo\async_tcp_echo_server.exe \
|
||||
examples\echo\async_udp_echo_server.exe \
|
||||
examples\echo\blocking_tcp_echo_client.exe \
|
||||
|
@ -19,6 +19,8 @@ all: \
|
||||
tests\timed_dgram_recv_test.exe \
|
||||
tests\timer_test.exe \
|
||||
tests\tpc_echo_server_test.exe \
|
||||
examples\chat\chat_client.exe \
|
||||
examples\chat\chat_server.exe \
|
||||
examples\echo\async_tcp_echo_server.exe \
|
||||
examples\echo\async_udp_echo_server.exe \
|
||||
examples\echo\blocking_tcp_echo_client.exe \
|
||||
@ -42,6 +44,8 @@ tests\timed_connect_test.exe: tests\timed_connect_test.obj
|
||||
tests\timed_dgram_recv_test.exe: tests\timed_dgram_recv_test.obj
|
||||
tests\timer_test.exe: tests\timer_test.obj
|
||||
tests\tpc_echo_server_test.exe: tests\tpc_echo_server_test.obj
|
||||
examples\chat\chat_client.exe: examples\chat\chat_client.obj
|
||||
examples\chat\chat_server.exe: examples\chat\chat_server.obj
|
||||
examples\echo\async_tcp_echo_server.exe: examples\echo\async_tcp_echo_server.obj
|
||||
examples\echo\async_udp_echo_server.exe: examples\echo\async_udp_echo_server.obj
|
||||
examples\echo\blocking_tcp_echo_client.exe: examples\echo\blocking_tcp_echo_client.obj
|
||||
|
8
asio/src/examples/chat/.cvsignore
Normal file
8
asio/src/examples/chat/.cvsignore
Normal file
@ -0,0 +1,8 @@
|
||||
.deps
|
||||
.dirstamp
|
||||
*.exe
|
||||
*_server
|
||||
*_client
|
||||
*.ilk
|
||||
*.pdb
|
||||
*.tds
|
138
asio/src/examples/chat/chat_client.cpp
Normal file
138
asio/src/examples/chat/chat_client.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <boost/bind.hpp>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
typedef std::list<chat_message> chat_message_list;
|
||||
|
||||
class chat_client
|
||||
{
|
||||
public:
|
||||
chat_client(asio::demuxer& d, short port, const std::string& host)
|
||||
: demuxer_(d),
|
||||
connector_(d),
|
||||
socket_(d)
|
||||
{
|
||||
connector_.async_connect(socket_, asio::inet_address_v4(port, host),
|
||||
boost::bind(&chat_client::handle_connect, this, _1));
|
||||
}
|
||||
|
||||
void send(const chat_message& msg)
|
||||
{
|
||||
demuxer_.operation_immediate(
|
||||
boost::bind(&chat_client::do_send, this, msg));
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
demuxer_.operation_immediate(boost::bind(&chat_client::do_close, this));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void handle_connect(const asio::socket_error& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
async_recv_chat_message(socket_, recv_msg_,
|
||||
boost::bind(&chat_client::handle_recv, this, _1, _2, _3));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_recv(const asio::socket_error& error, size_t length,
|
||||
size_t last_length)
|
||||
{
|
||||
if (!error && last_length > 0)
|
||||
{
|
||||
std::cout.write(recv_msg_.body(), recv_msg_.body_length());
|
||||
std::cout << "\n";
|
||||
async_recv_chat_message(socket_, recv_msg_,
|
||||
boost::bind(&chat_client::handle_recv, this, _1, _2, _3));
|
||||
}
|
||||
}
|
||||
|
||||
void do_send(chat_message msg)
|
||||
{
|
||||
bool send_in_progress = !send_msgs_.empty();
|
||||
send_msgs_.push_back(msg);
|
||||
if (!send_in_progress)
|
||||
{
|
||||
async_send_chat_message(socket_, send_msgs_.front(),
|
||||
boost::bind(&chat_client::handle_send, this, _1, _2, _3));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_send(const asio::socket_error& error, size_t length,
|
||||
size_t last_length)
|
||||
{
|
||||
if (!error && last_length > 0)
|
||||
{
|
||||
send_msgs_.pop_front();
|
||||
if (!send_msgs_.empty())
|
||||
{
|
||||
async_send_chat_message(socket_, send_msgs_.front(),
|
||||
boost::bind(&chat_client::handle_send, this, _1, _2, _3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_close()
|
||||
{
|
||||
connector_.close();
|
||||
socket_.close();
|
||||
}
|
||||
|
||||
private:
|
||||
asio::demuxer& demuxer_;
|
||||
asio::socket_connector connector_;
|
||||
asio::stream_socket socket_;
|
||||
chat_message recv_msg_;
|
||||
chat_message_list send_msgs_;
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: chat_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::demuxer d;
|
||||
|
||||
using namespace std; // For atoi, strlen, strcpy and sprintf.
|
||||
chat_client c(d, atoi(argv[2]), argv[1]);
|
||||
|
||||
asio::detail::thread t(boost::bind(&asio::demuxer::run, &d));
|
||||
|
||||
char line[chat_message::max_body_length + 1];
|
||||
while (std::cin.getline(line, chat_message::max_body_length + 1))
|
||||
{
|
||||
chat_message msg;
|
||||
msg.length(chat_message::header_length + strlen(line));
|
||||
sprintf(msg.data(), "%4d", msg.body_length());
|
||||
strncpy(msg.body(), line, msg.body_length());
|
||||
c.send(msg);
|
||||
}
|
||||
|
||||
c.close();
|
||||
t.join();
|
||||
}
|
||||
catch (asio::socket_error& e)
|
||||
{
|
||||
std::cerr << "Socket error: " << e.message() << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
131
asio/src/examples/chat/chat_message.hpp
Normal file
131
asio/src/examples/chat/chat_message.hpp
Normal file
@ -0,0 +1,131 @@
|
||||
#ifndef CHAT_MESSAGE_HPP
|
||||
#define CHAT_MESSAGE_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <strstream>
|
||||
#include <boost/bind.hpp>
|
||||
#include "asio.hpp"
|
||||
|
||||
class chat_message
|
||||
{
|
||||
public:
|
||||
enum { header_length = 4 };
|
||||
enum { max_body_length = 512 };
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return &data_[0];
|
||||
}
|
||||
|
||||
char* data()
|
||||
{
|
||||
return &data_[0];
|
||||
}
|
||||
|
||||
size_t length() const
|
||||
{
|
||||
return data_.size();
|
||||
}
|
||||
|
||||
void length(size_t l)
|
||||
{
|
||||
data_.resize(l);
|
||||
}
|
||||
|
||||
const char* body() const
|
||||
{
|
||||
return &data_[0] + header_length;
|
||||
}
|
||||
|
||||
char* body()
|
||||
{
|
||||
return &data_[0] + header_length;
|
||||
}
|
||||
|
||||
size_t body_length() const
|
||||
{
|
||||
return data_.size() - header_length;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<char> data_;
|
||||
};
|
||||
|
||||
template <typename Stream>
|
||||
size_t send_chat_message(Stream& s, chat_message& msg)
|
||||
{
|
||||
return asio::send_n(s, msg.data(), msg.length());
|
||||
}
|
||||
|
||||
template <typename Stream, typename Handler>
|
||||
void async_send_chat_message(Stream& s, chat_message& msg, Handler handler)
|
||||
{
|
||||
asio::async_send_n(s, msg.data(), msg.length(), handler);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
size_t recv_chat_message(Stream& s, chat_message& msg)
|
||||
{
|
||||
msg.length(chat_message::header_length);
|
||||
if (asio::recv_n(s, msg.data(), chat_message::header_length) == 0)
|
||||
return 0;
|
||||
std::istrstream is(msg.data(), chat_message::header_length);
|
||||
size_t body_length = 0;
|
||||
is >> body_length;
|
||||
if (!is || body_length > chat_message::max_body_length)
|
||||
return 0;
|
||||
msg.length(chat_message::header_length + body_length);
|
||||
return asio::recv_n(s, msg.body(), msg.body_length());
|
||||
}
|
||||
|
||||
template <typename Stream, typename Handler>
|
||||
class recv_chat_message_handler
|
||||
{
|
||||
public:
|
||||
recv_chat_message_handler(Stream& stream, chat_message& msg, Handler handler)
|
||||
: stream_(stream),
|
||||
msg_(msg),
|
||||
handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Error>
|
||||
void operator()(const Error& error, size_t length, size_t last_length)
|
||||
{
|
||||
if (!error && last_length > 0)
|
||||
{
|
||||
std::istrstream is(msg_.data(), chat_message::header_length);
|
||||
size_t body_length = 0;
|
||||
is >> body_length;
|
||||
if (is && body_length <= chat_message::max_body_length)
|
||||
{
|
||||
msg_.length(chat_message::header_length + body_length);
|
||||
asio::async_recv_n(stream_, msg_.body(), msg_.body_length(), handler_);
|
||||
}
|
||||
else
|
||||
{
|
||||
last_length = 0;
|
||||
handler_(error, length, last_length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
handler_(error, length, last_length);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Stream& stream_;
|
||||
chat_message& msg_;
|
||||
Handler handler_;
|
||||
};
|
||||
|
||||
template <typename Stream, typename Handler>
|
||||
void async_recv_chat_message(Stream& s, chat_message& msg, Handler handler)
|
||||
{
|
||||
msg.length(chat_message::header_length);
|
||||
asio::async_recv_n(s, msg.data(), chat_message::header_length,
|
||||
recv_chat_message_handler<Stream, Handler>(s, msg, handler));
|
||||
}
|
||||
|
||||
#endif // CHAT_MESSAGE_HPP
|
206
asio/src/examples/chat/chat_server.cpp
Normal file
206
asio/src/examples/chat/chat_server.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include "asio.hpp"
|
||||
#include "chat_message.hpp"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
typedef std::list<chat_message> chat_message_list;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_participant
|
||||
{
|
||||
public:
|
||||
virtual ~chat_participant() {}
|
||||
virtual void deliver(const chat_message& msg) = 0;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<chat_participant> chat_participant_ptr;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_room
|
||||
{
|
||||
public:
|
||||
void join(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.insert(participant);
|
||||
std::for_each(recent_msgs_.begin(), recent_msgs_.end(),
|
||||
boost::bind(&chat_participant::deliver, participant, _1));
|
||||
}
|
||||
|
||||
void leave(chat_participant_ptr participant)
|
||||
{
|
||||
participants_.erase(participant);
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
recent_msgs_.push_back(msg);
|
||||
while (recent_msgs_.size() > max_recent_msgs)
|
||||
recent_msgs_.pop_front();
|
||||
|
||||
std::for_each(participants_.begin(), participants_.end(),
|
||||
boost::bind(&chat_participant::deliver, _1, boost::ref(msg)));
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<chat_participant_ptr> participants_;
|
||||
enum { max_recent_msgs = 100 };
|
||||
chat_message_list recent_msgs_;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_session
|
||||
: public chat_participant,
|
||||
public boost::enable_shared_from_this<chat_session>
|
||||
{
|
||||
public:
|
||||
chat_session(asio::demuxer& d, chat_room& r)
|
||||
: socket_(d),
|
||||
room_(r)
|
||||
{
|
||||
}
|
||||
|
||||
asio::stream_socket& socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
room_.join(shared_from_this());
|
||||
async_recv_chat_message(socket_, recv_msg_, boost::bind(
|
||||
&chat_session::handle_recv, shared_from_this(), _1, _2, _3));
|
||||
}
|
||||
|
||||
void deliver(const chat_message& msg)
|
||||
{
|
||||
bool send_in_progress = !send_msgs_.empty();
|
||||
send_msgs_.push_back(msg);
|
||||
if (!send_in_progress)
|
||||
{
|
||||
async_send_chat_message(socket_, send_msgs_.front(), boost::bind(
|
||||
&chat_session::handle_send, shared_from_this(), _1, _2, _3));
|
||||
}
|
||||
}
|
||||
|
||||
void handle_recv(const asio::socket_error& error, size_t length,
|
||||
size_t last_length)
|
||||
{
|
||||
if (!error && last_length > 0)
|
||||
{
|
||||
room_.deliver(recv_msg_);
|
||||
async_recv_chat_message(socket_, recv_msg_, boost::bind(
|
||||
&chat_session::handle_recv, shared_from_this(), _1, _2, _3));
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
void handle_send(const asio::socket_error& error, size_t length,
|
||||
size_t last_length)
|
||||
{
|
||||
if (!error && last_length > 0)
|
||||
{
|
||||
send_msgs_.pop_front();
|
||||
if (!send_msgs_.empty())
|
||||
{
|
||||
async_send_chat_message(socket_, send_msgs_.front(), boost::bind(
|
||||
&chat_session::handle_send, shared_from_this(), _1, _2, _3));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
room_.leave(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
asio::stream_socket socket_;
|
||||
chat_room& room_;
|
||||
chat_message recv_msg_;
|
||||
chat_message_list send_msgs_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<chat_session> chat_session_ptr;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
class chat_server
|
||||
{
|
||||
public:
|
||||
chat_server(asio::demuxer& d, short port)
|
||||
: demuxer_(d),
|
||||
acceptor_(d, asio::inet_address_v4(port))
|
||||
{
|
||||
chat_session_ptr new_session(new chat_session(demuxer_, room_));
|
||||
acceptor_.async_accept(new_session->socket(),
|
||||
boost::bind(&chat_server::handle_accept, this, new_session, _1));
|
||||
}
|
||||
|
||||
void handle_accept(chat_session_ptr session, const asio::socket_error& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
session->start();
|
||||
chat_session_ptr new_session(new chat_session(demuxer_, room_));
|
||||
acceptor_.async_accept(new_session->socket(),
|
||||
boost::bind(&chat_server::handle_accept, this, new_session, _1));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
asio::demuxer& demuxer_;
|
||||
asio::socket_acceptor acceptor_;
|
||||
chat_room room_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<chat_server> chat_server_ptr;
|
||||
typedef std::list<chat_server_ptr> chat_server_list;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
std::cerr << "Usage: chat_server <port> [<port> ...]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
asio::demuxer d;
|
||||
|
||||
chat_server_list servers;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
using namespace std; // For atoi.
|
||||
chat_server_ptr server(new chat_server(d, atoi(argv[i])));
|
||||
servers.push_back(server);
|
||||
}
|
||||
|
||||
d.run();
|
||||
}
|
||||
catch (asio::socket_error& e)
|
||||
{
|
||||
std::cerr << "Socket error: " << e.message() << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user