C++ Chat Server Tutorial Part I _ Taywils

18
& &KDW 6HUYHU 7XWRULDO 3DUW , _ 7D\ZLOVPH KWWSZZZWD\ZLOVPHERRVWFKDWFOLHQWKWPO 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

description

ASIO C++

Transcript of C++ Chat Server Tutorial Part I _ Taywils

Page 1: C++ Chat Server Tutorial Part I _ Taywils

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.