09-08-2012, 06:57 PM
This is a continuation of the previous Tutorial:
2
I in this tutorial I'll show how to make a server to handle many clients.
Threads
First thing you need to know is how to handle threads. There are many libraries that handle threads(windows threds, POSIX threds, Boost threds, std library from C++0x/11, unix fork, etc), but I'm gonna post about POSIX Threads, because they are not limited to the platform, they work the same as windows threads too.
Creating Threads:
#include <pthread.h>
#include <stdio.h>
// this function is he thred Entry point
// when creating a thread it will run this function in a separate thread
void* EntryPoint(void* arg){
// pocess data
int tid = (int) arg;
printf("Thread ID=%d", tid);
// close the thread
pthread_exit(NULL);
return NULL; //to remove somse compiler warnings
}
//main function
int main()
{
// create 10 threads handles
pthread_t threads[10];
for(int i = 0; i < 10; i++){
// creating a thread with the entrypoint on EntryPoint function
// and giving i as parameter
int rc = pthread_create(&thread, NULL, EntryPoint, (void *)i );
// check if thread was successfully ceated
if (rc){
printf("Cannot create thread");
exit(-1);
}
}
// terminate main thread, usually waits for other threads to finish
pthread_exit(NULL);
return 0;
}
For thread syncronization there are many methods in phtreads, so I'll post the mutex version.
#include <pthread.h>
#include <stdio.h>
//our mutex used for syncronization either make it a global variable or a staic one
pthread_mutex_t mutex;
// this function is he thred Entry point
// when creating a thread it will run this function in a separate thread
void* EntryPoint(void* arg){
// pocess data
int tid = (int) arg;
printf("Thread ID=%d\n", tid);
// lock for synchronization
pthread_mutex_lock (&mutex);
static int x = 0;
x +=tid;
printf("x=%d\n", x);
pthread_mutex_unlock (&mutex);
// close the thread
pthread_exit((void*)tid);
return NULL; //to remove somse compiler warnings
}
//main function
int main()
{
// create a thread handle, and attributes
pthread_t threads[10];
//this variable will hold the thread termination status
//(when the thread reaches pthread_exit(NULL)
void* status;
int error;
//initialize the mutex
pthread_mutex_init(&mutex, NULL);
for(int i = 0; i < 10; i++){
// creating a thread with the entrypoint on EntryPoint function
// and giving tid as parameter
error = pthread_create(&threads[i], NULL, EntryPoint, (void *)i );
// check if thread was successfully ceated
if (error){
printf("Cannot create thread");
exit(-1);
}
}
// wait for other threasd to finish, usually requires to initialize threads as joinable first
for(int i = 0; i < 10; i++)
error = pthread_join(threads[i], &status);
//destroy the mutex
pthread_mutex_destroy(&mutex);
// terminate main thread, usually waits for other threads to finish
pthread_exit(NULL);
return 0;
}
For more info about POSIX Threads read this:
2
Server that holds many clients:
When talking about networking there are 2 basic types of functions( blocking and nonblocking). Blocking function blocks the application untill the function terminates, a nonblocking function lets the program runing while the function completes.
For example the accept() function, when it is called will block the program untill it recieves a connection from a user(client). To fix this we need to make a thread that will accept connections form clients. The similar problem is with recv() function it blocks program functionality untill it receives data from the client from a socket(if we had a socket that will not receive data this will block all other program functionality).
To solve this problem the first thing we are going to do is to create a thread that will accept client connections, the second problem is to either using nonBlocin sockets, or using the select() to check sockets if they have data on them before calling the recv() function( this way we will not block with recv()).
The select function will create a list of sockets that has data to receive(we will have to create a socket set with the clent sockets and select from that set all the sockets with data on them.
The 2 function once it reads the FD_SET structure it will alter it(removes the unnecessary sockets from it). and returns the number of sockets.
Read more on FD_SET, FD_CLR, FD_ISSET, FD_ZERO
#include <pthread.h>
#include <stdio.h>
// declare globally master socket set
FD_SET masterSet;
// declare mutex for shared data
pthread_mutex_t mutex = NULL;
// thread entrypoint for accepting new clients
void* acceptClientThread(void* arg){
int serverSocket = (int)arg;
//starting a socket wheel to acept clients
bool bDone = true;
while(bDone){
// accept new clients
unsigned int clientSocket = accept(serverSocket, 0, 0);
// check for errors
if (clientSocket < 0) {
printf("Accept Failed!\n");
// exit the wheel
bDone = false;
}else{
// if a client connected
// lock for synchronization
pthread_mutex_lock (&mutex);
// add the socket to the master set
FD_SET(clientSocket, &masterSet);
// unloch when done
pthread_mutex_unlock (&mutex);
}
}
return NULL;
}
bool checkRecvData(int nBytes,unsigned int clientSocket){
switch(nBytes){
case -1:{
//error
#ifdef WIN32
//get the last error
int error = WSAGetLastError();
//means the lient shut down the application
//or it lost connection
if (error == WSAECONNRESET){}
else break;
#else
if(errno == 10054){}
else break;
#endif
}
case 0:{
//client disconnected,
//we need to remove the socket from the set
pthread_mutex_lock (&mutex);
FD_CLR(clientSocket, &masterSet);
pthread_mutex_unlock (&mutex);
//close socket
#ifdef WIN32
closesocket(clientSocket);
#else
close(clientSocket);
#endif
}
default:{
return false;
}
}
return true;
}
//main function
int main()
{
int Socket = startupServer("10.0.0.1", 12345);
if(Socket < 0){
// error, shuting down socket and close app
shutdownSocket(Socket);
return 1;
}
// handle for server connection accepting thread
pthread_t acceptThread;
int error;
//initialize the mutex
pthread_mutex_init(&mutex, NULL);
// create a thread for server connection accepting thread
error = pthread_create(&acceptThread, NULL, acceptClientThread, (void *)Socket );
// check if thread was successfully ceated
if (error){
printf("Cannot create thread");
exit(-1);
}
// wait for the thread to start, to not create other problems
Sleep(100);
//initialize the socket set with 0
FD_ZERO(&masterSet);
// wait for other threasd to finish
// the program main loop now
bool bQuit = true;
while(bQuit){
// now we need to look int the master set for client sockets
// who have data and then read their data.
// since we want to look into our master set which is accessed by
// acceptClientThread so we will need to lock this.
pthread_mutex_lock (&mutex);
//instead of locing the intire procedure and reading from master set
//we will just copy the master set
FD_SET selectedSet = masterSet;
pthread_mutex_unlock (&mutex);
//check if there are sockets into masterset
if(selectedSet.fd_count == 0){
//continue loop
continue;
}
// usually the select function has 5 parameters, the last parameter in the select
// function is ther wait time, if you don't want to wait for new connections
// just set the wait time to 0
timeval waitTime = {0};
int result = select(selectedSet.fd_count, &selectedSet, NULL, NULL, &waitTime);
// the return value that the select function has
// is the sockets that has data
//if the value is negative this means it has errors
if (result <= 0) {
// no sockets have data
continue;
}
//now that we have the sockets that have data
//we should check them
for(int i = 0; i < selectedSet.fd_count; i++){
//getting the socket from the socket list
int clientSocket = selectedSet.fd_array[i];
char header[12] = {0};
//receiving the data from the socket
int nBytes = recv(clientSocket, header, 12,0 );
//ceeck data
if(checkRecvData(nBytes, clientSocket)){
//something happened continue with the next socket
continue;
}
//get the size from the header,
//usually youn can convert the 4 last bytes from into int32
int size = header[11];
char* data = new char[size];
//receiving the data from the socket
nBytes = recv(clientSocket, data, size,0 );
//ceeck data
if(checkRecvData(nBytes, clientSocket)){
//something happened continue with the next socket
continue;
}
//process data
}
}
//destroy the mutex, tou can add this into shutdown server
pthread_mutex_destroy(&mutex);
// terminate main thread, usually waits for other threads to finish
pthread_exit(NULL);
return 0;
}

