C++ Chat Server Tutorial Part I _ Taywils
-
Upload
radhakrishnan-srinivasan -
Category
Documents
-
view
15 -
download
0
description
Transcript of C++ Chat Server Tutorial Part I _ Taywils
![Page 1: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/1.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 1/18
C++ Chat Server Tutorial Part I
Intro
Click here for Part II
Tutorial Assumes Boost Version >= 1.53
We'll be making use of the Boost C++ libraries for this
tutorial so before we begin make sure you have read
through the installation documentation on the Boost
website on. Below I have included some links for each
operating system on how to install a recent version of Boost;
its not extremely difficult but sometimes installing Boost can
be tricky for those of us not familiar with compiling C++
projects from source code or the difference between the
header-file only libraries and the rest of Boost.
Windows users Boost Installation Guide
![Page 2: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/2.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 2/18
Ubuntu/Debian Linux can use the aptitude Boost
Ubuntu/Debian Linux can also use the Upstream Boost
which is recommended
Mac OS X should use Homebrew
If you need extra help installing the Boost C++ libraries or
making sure that you can compile a project using Boost and
properly link the libraries Google and or Stackoverflow are
your best bets. Once you're comfortable building projects
and or compiling single files with Boost we're ready to begin.
Part I: The Chat Client Code
Our client code for the Chat application will rely upon the
use of threads to divide our code into three distinct sub-
routines.
A thread for displaying chat messages
Another thread for sending messages to the Chat server
And a third thread for receiving messages routed by the
![Page 3: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/3.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 3/18
server from other connected clients
There are many approaches to designing multi-threaded
applications and its a big topic that often rears its head
during interviews with top tech companies but for the sake
of this tutorial we'll only need to concern ourselves with just
a small subset of concurrent programming. For the purposes
of the application we're going to build in this tutorial series
we're going to be using what is known as the "Producer-
Consumer" pattern for organizing our code. As a side note
for the pedantics reading this article I'm not claiming that
this code will follow Producer-Consumer to the exact
sepcification but for the most part it will resemble a typical
Producer-Consumer setup.
Producer-Consumer Overview For The Chat Client
Quoting from the Wikipedia page on the pattern
In computing, the producer–consumer's problem
(also known as the bounded-buffer problem) is a
classic example of a multi-process synchronization
Blog Archive Projects About RSS
![Page 4: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/4.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 4/18
problem. The problem describes two processes, the
producer and the consumer, who share a common,
fixed-size buffer used as a queue.
Thus the following is a list of C++ objects used by our Client
to implement the Producer-Consumer pattern.
boost::thread_group to address multi-processing
std::queue< std::string > to represent the bounded-
buffer of messages
boost::thread objects for adding and removing messages
from the queue
Preprocessor Directives And Function Prototypes
Lets start off, create a new working directory called
chat_app wherever you want on your system. Next change
into the chat_app directory and create a new file, name it
boostChatClient.cpp. First we'll need to include a few
libraries; some from the C++ standard template library and
others from Boost.
![Page 5: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/5.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 5/18
/* boostChatClient.cpp */
#include<iostream>
#include<queue>
#include<string>
#include<cstdlib>
#include<boost/thread.hpp>
#include<boost/bind.hpp>
#include<boost/asio.hpp>
#include<boost/asio/ip/tcp.hpp>
#include<boost/algorithm/string.hpp>
The above library includes are fairly basic for a C++ console
application but unfamiliar to most are probably the Boost
includes.
boost/thread Multithreading support
boost/bind A library for functional programming but
used here to create sub-rountines for threads
boost/asio System socket and network programming
library
boost/algorithm/string Pretty self-explainatory; gives
use some new string methods
I will be using namespace aliasing in this application, it can be
a pain sometimes to read code without namespace aliasing
so lets at least make an effort to strive for clean human
![Page 6: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/6.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 6/18
readable code. So add the lines below to the current file
right after the preprocessor library includes.
/* boostChatClient.cpp */
using namespace std;
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
Next we define some typedefs for quickly describing some
boost shared pointers. Shared pointers are now apart of the
C++ standard but there are problems abound when you try
and mix and match C++ std shared pointers with Boost
library shared pointers. Not so much for performance but
more from the fact that using Boost shared pointers
introduces a dependency on the Boost libraries; so to keep
your code Boost friendly just stick with the Boost versions
of the smart pointer collection.
/* boostChatClient.cpp */
typedef boost::shared_ptr<tcp::socket> socket_ptr;
typedef boost::shared_ptr<string> string_ptr;
typedef boost::shared_ptr< queue<string_ptr> > messageQueue_ptr;
In order to initialize the boost::asio networking methods we
need to create a special object called io_service. The best
![Page 7: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/7.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 7/18
way to think of io_service is as shared queue which only
accepts functions that deal with asynchronous I/O. Thus you
can represent a socket bound to a network port within your
application and in order to send the socket a method such as
connect() the method must get enqueued within the
io_service before its sent down to the operating system.
The documentation on the anatomy of Boost::asio is
the most helpful for understanding the architecture
of the library. Basic Boost.Asio Anatomy
/* boostChatClient.cpp */
io_service service; // Boost Asio io_service
messageQueue_ptr messageQueue(new queue<string_ptr>); // Queue for producer consumer pattern
tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 8001); // TCP socket for connecting to server
const int inputSize = 256; // Maximum size for input buffer
string_ptr promptCpy; // Terminal prompt displayed to chat users
Add the following function prototypes; we'll discuss the
functions as they get implemented. As you can already guess
by the descriptive names of each function the function's
with the suffix Loop will be ran on threads and interact with
the messageQueue we defined earlier.
![Page 8: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/8.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 8/18
/* boostChatClient.cpp */
// Function Prototypes
bool isOwnMessage(string_ptr);
void displayLoop(socket_ptr);
void inboundLoop(socket_ptr, string_ptr);
void writeLoop(socket_ptr, string_ptr);
string* buildPrompt();
// End of Function Prototypes
The main() Function, Thread Creation And Socket
Initialization
From the explaination earlier in the article we create a
thread_group facilitate the all of our async functions. In
regards to the producer-consumer pattern,
inboundLoop() Will push items from the socket to our
messageQueue; i.e producer
displayLoop() Removes items from messageQueue to
display on the client terminal; i.e consumer
/* boostChatClient.cpp */
int main(int argc, char** argv)
{
try
{
boost::thread_group threads;
socket_ptr sock(new tcp::socket(service));
![Page 9: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/9.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 9/18
string_ptr prompt( buildPrompt() );
promptCpy = prompt;
sock->connect(ep);
cout << "Welcome to the ChatApplication\nType \"exit\" to quit" << endl;
threads.create_thread(boost::bind(displayLoop, sock));
threads.create_thread(boost::bind(inboundLoop, sock, prompt));
threads.create_thread(boost::bind(writeLoop, sock, prompt));
threads.join_all();
}
catch(std::exception& e)
{
cerr << e.what() << endl;
}
puts("Press any key to continue...");
getc(stdin);
return EXIT_SUCCESS;
}
Function Definitions
The first function buildPrompt is a function which handles
the display of the terminal input for clients. Its fairly simple
in that it takes a string of the clients name and assigns it to
the value of the prompt pointer we declared earlier.
/* boostChatClient.cpp */
string* buildPrompt()
{
![Page 10: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/10.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 10/18
const int inputSize = 256;
char inputBuf[inputSize] = {0};
char nameBuf[inputSize] = {0};
string* prompt = new string(": ");
cout << "Please input a new username: ";
cin.getline(nameBuf, inputSize);
*prompt = (string)nameBuf + *prompt;
boost::algorithm::to_lower(*prompt);
return prompt;
}
Following the buildPrompt() function the first of the
threaded functions is the inboundLoop().
/* boostChatClient.cpp */
void inboundLoop(socket_ptr sock, string_ptr prompt)
{
int bytesRead = 0;
char readBuf[1024] = {0};
for(;;)
{
if(sock->available())
{
bytesRead = sock->read_some(buffer(readBuf, inputSize));
string_ptr msg(new string(readBuf, bytesRead));
messageQueue->push(msg);
}
boost::this_thread::sleep( boost::posix_time::millisec(1000));
}
}
![Page 11: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/11.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 11/18
Our code for the inboundLoop() is self-explainatory but in
particular it creates a loop which only inserts into the thread
when a message is available on the socket connected to the
server. Reading from the socket object is an operation which
may potentially interfere with writing to the socket so we
put a one second delay on checks for reading.
As for writting mesasges to the socket to send off to other
members of the Chat session we need a loop that will
constantly poll for user input. Once the user input is read
write the message to the socket wait for the next input.
Recall that this operation is threaded so in-comming
messages can still be displayed since that happens on an
entirely different thread.
/* boostChatClient.cpp */
void writeLoop(socket_ptr sock, string_ptr prompt)
{
char inputBuf[inputSize] = {0};
string inputMsg;
for(;;)
{
cin.getline(inputBuf, inputSize);
inputMsg = *prompt + (string)inputBuf + '\n';
if(!inputMsg.empty())
![Page 12: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/12.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 12/18
{
sock->write_some(buffer(inputMsg, inputSize));
}
// The string for quitting the application
// On the server‐side there is also a check for "quit" to terminate the TCP socket
if(inputMsg.find("exit") != string::npos)
exit(1); // Replace with cleanup code if you want but for this tutorial exit is enough
inputMsg.clear();
memset(inputBuf, 0, inputSize);
}
}
For the extra pedantic, you might be wondering why there is
no extraneous clean-up code and instead we just call exit(1);
for the sake of keeping this tutorial brief and to the point we
are not launching a production ready scalable service
oriented distributed ChatApplication to be used by
thousands of clients. Anyhow moving on the last of the
threaded funtions is for displaying the messages read from
the socket to the terminal.
/* boostChatClient.cpp */
void displayLoop(socket_ptr sock)
{
for(;;)
{
if(!messageQueue->empty())
{
// Can you refactor this code to handle multiple users with the same prompt?
![Page 13: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/13.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 13/18
if(!isOwnMessage(messageQueue->front()))
{
cout << "\n" + *(messageQueue->front());
}
messageQueue->pop();
}
boost::this_thread::sleep( boost::posix_time::millisec(1000));
}
}
The displayLoop() function is fairly crude but it gets the job
done. We rely on the fact that every message begins with a
user prompt in order to determine if the message belonged
to the client or not. When I say crude I mean that a proper
chat application with tag each user with a specific id number
because our code fails to handle the error when multiple
users share the same prompt. Speaking of which here is the
last of the utility functions; the one which checks if the
prompt from buildPrompt() is found within the string
arriving from the socket.
bool isOwnMessage(string_ptr message)
{
if(message->find(*promptCpy) != string::npos)
return true;
else
return false;
![Page 14: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/14.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 14/18
}
Thanks for reading my tutorial on how to setup a chat client
using C++ and the Boost Libraries; this code deserves a
refactor considering that many of the Boost code used is
now apart of the latest C++ standard. In addition the
introduction of a protocol could be useful for unique
identification of clients and other things as well. Stay tuned
for the second part of this tutorial where we code the server
side of the Chat applciation.
Full Source Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<iostream>
#include<queue>
#include<string>
#include<cstdlib>
#include<boost/thread.hpp>
#include<boost/bind.hpp>
#include<boost/asio.hpp>
#include<boost/asio/ip/tcp.hpp>
#include<boost/algorithm/string.hpp>
using namespace std;
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
typedef boost::shared_ptr<tcp::socket> socket_ptr;
typedef boost::shared_ptr<string> string_ptr;
typedef boost::shared_ptr< queue<string_ptr> > messageQueue_ptr;
![Page 15: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/15.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 15/18
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
io_service service;
messageQueue_ptr messageQueue(new queue<string_ptr>);
tcp::endpoint ep(ip::address::from_string("127.0.0.1"), 8001);
const int inputSize = 256;
string_ptr promptCpy;
// Function Prototypes
bool isOwnMessage(string_ptr);
void displayLoop(socket_ptr);
void inboundLoop(socket_ptr, string_ptr);
void writeLoop(socket_ptr, string_ptr);
string* buildPrompt();
// End of Function Prototypes
int main(int argc, char** argv)
{
try
{
boost::thread_group threads;
socket_ptr sock(new tcp::socket(service));
string_ptr prompt( buildPrompt() );
promptCpy = prompt;
sock->connect(ep);
cout << "Welcome to the ChatServer\nType \"exit\" to quit" << endl;
threads.create_thread(boost::bind(displayLoop, sock));
threads.create_thread(boost::bind(inboundLoop, sock, prompt));
threads.create_thread(boost::bind(writeLoop, sock, prompt));
threads.join_all();
}
catch(std::exception& e)
{
cerr << e.what() << endl;
}
puts("Press any key to continue...");
getc(stdin);
return EXIT_SUCCESS;
}
string* buildPrompt()
![Page 16: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/16.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 16/18
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
{
const int inputSize = 256;
char inputBuf[inputSize] = {0};
char nameBuf[inputSize] = {0};
string* prompt = new string(": ");
cout << "Please input a new username: ";
cin.getline(nameBuf, inputSize);
*prompt = (string)nameBuf + *prompt;
boost::algorithm::to_lower(*prompt);
return prompt;
}
void inboundLoop(socket_ptr sock, string_ptr prompt)
{
int bytesRead = 0;
char readBuf[1024] = {0};
for(;;)
{
if(sock->available())
{
bytesRead = sock->read_some(buffer(readBuf, inputSize));
string_ptr msg(new string(readBuf, bytesRead));
messageQueue->push(msg);
}
boost::this_thread::sleep( boost::posix_time::millisec(1000));
}
}
void writeLoop(socket_ptr sock, string_ptr prompt)
{
char inputBuf[inputSize] = {0};
string inputMsg;
for(;;)
{
cin.getline(inputBuf, inputSize);
inputMsg = *prompt + (string)inputBuf + '\n';
if(!inputMsg.empty())
{
![Page 17: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/17.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 17/18
view raw
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
boostChatClient.cpp hosted with ❤ by GitHub
Click here for Part II
sock->write_some(buffer(inputMsg, inputSize));
}
if(inputMsg.find("exit") != string::npos)
exit(1);
inputMsg.clear();
memset(inputBuf, 0, inputSize);
}
}
void displayLoop(socket_ptr sock)
{
for(;;)
{
if(!messageQueue->empty())
{
if(!isOwnMessage(messageQueue->front()))
{
cout << "\n" + *(messageQueue->front());
}
messageQueue->pop();
}
boost::this_thread::sleep( boost::posix_time::millisec(1000));
}
}
bool isOwnMessage(string_ptr message)
{
if(message->find(*promptCpy) != string::npos)
return true;
else
return false;
}
![Page 18: C++ Chat Server Tutorial Part I _ Taywils](https://reader036.fdocuments.net/reader036/viewer/2022072110/563db79b550346aa9a8c9a40/html5/thumbnails/18.jpg)
10/23/2015 C++ Chat Server Tutorial Part I | Taywils.me
http://www.taywils.me/2014/11/15/boostchatclient.html 18/18
Ruby Cramp tutorial build an RSS API withRuby and Redis3 comments • 3 years ago
Ahmad Hasan Khan — No problem
Java Spark Framework Tutorial2 comments • 2 years ago
papaj — this article is old and things havechanged since then, please update it if youcould. Thx.
Build A Text Based Multiplayer RPG Part V1 comment • 3 years ago
Jon — When I download all the files, put themin the same folder and open main.cpp and tryto compile, I get 50+ errors all of them starting"undefined reference to MUD::...."
Install Python, Numpy and Pandas onWindows 73 comments • 3 years ago
Mahasish Shome — What an awesomeTutorial!!! Thanks very much for sharing.
ALSO ON TAYWILS.ME
2 Comments taywils.me Login1
Share⤤ Sort by Best
Join the discussion…
• Reply •
Lelek • a month agoCould you help with compiling@Ubuntu? △ ▽
• Reply •
Waseem Ahmad Naeem • 3 months agoNot useful. I Need Tutorial Not Source Code △ ▽
WHAT'S THIS?
Subscribe✉ Add Disqus to your sited Privacyὑ�
Recommend
Share ›
Share ›
Copyright © 2015 Demetrious Taylor Wilson. All rights reserved.