Added chat example.

This commit is contained in:
chris 2003-11-12 13:51:04 +00:00
parent 35c6e23638
commit d0c3eb74dd
8 changed files with 495 additions and 0 deletions

View File

@ -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

View File

@ -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 \

View File

@ -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 \

View File

@ -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

View File

@ -0,0 +1,8 @@
.deps
.dirstamp
*.exe
*_server
*_client
*.ilk
*.pdb
*.tds

View 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;
}

View 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

View 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;
}