![]() |
|
[C/C++]Basic Socket Programming - Part 2 - Printable Version +- LCKB (https://lckb.dev/forum) +-- Forum: ** OLD LCKB DATABASE ** (https://lckb.dev/forum/forumdisplay.php?fid=109) +--- Forum: Programmers Gateway (https://lckb.dev/forum/forumdisplay.php?fid=196) +---- Forum: Coders Talk (https://lckb.dev/forum/forumdisplay.php?fid=192) +---- Thread: [C/C++]Basic Socket Programming - Part 2 (/showthread.php?tid=1097) |
- someone - 09-08-2012 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; } - Wizatek - 09-08-2012 This looks pretty usefull, exactley what i was looking for I also like the continue statement, never knew about that, makes things a whole lot shorter - someone - 09-08-2012 2 things need to be added: if(selectedSet.fd_count == 0){ //continue loop Sleep(100); continue; } //... timeval waitTime = {0}; waitTime.tv_usec = 100000; If it will be set to 0 will be CPU intensive, and 100 milliseconds will be enough. |