Hi guys, i wanted to post this tutorial on a few sites, i didn't really like the old code so here's the old tutorial revisited :D... that means there's updated(and much improved) code amongst other things :P. I have also heavily commented it for the newbies so let me know what you think :D. Enjoy, KOrUPt. Back to the tutorial: [b]------------------------------------------------------------[/b] Hi guys. Before we start I'm going to assume you know how to create single threaded servers(if you don't I'm sure this tut will still help you) and have a basic knowledge of Winsock and what threads are(if you don't see below)... Now it really isn't that hard to create multi-threaded servers once you get used to them... So firstly I'm going to show you a basic single threaded server, then I'll show you how to upgrade it to a multi-threaded server, but first a bit of Questions and Answers is in order :P... Q. What are the benefits of using a multi-threaded server over a single threaded server? A. A multi-threaded server allows you to have more than 1 client connected at any one time, at some point you may want to create a game server or other, you will find that you need to be able to handle multiple connections, threads are a solution to handling multiple clients. Quick and (very)simple overview of what threads are: Threads allow you to have more than 1 action executing at the same time, for instance you could have 2 infinite loops executing at the same time(please note i attempted to put this into plain English if you know what i mean)... Ok i couldn't think of any more questions... So if you have any let me know and I'll answer them and merge them into this topic... Right below is the code to a very basic single threaded Winsock server that I just whipped up: [code] #include #include #include int main() { WSADATA wsaData; SOCKADDR_IN server, from; SOCKET sock, client; printf("Starting up TCP server by KOrUPt\n"); int ret = WSAStartup(0x101, &wsaData); // use highest version of winsock avaliable if(ret != NO_ERROR) return 0; server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(23); sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sock == INVALID_SOCKET || sock == SOCKET_ERROR) return 1; if(bind(sock, (sockaddr*)&server, sizeof(server)) != 0) return 1; if(listen(sock, 5) != 0) return 1; int len = sizeof(from); while((client = accept(sock, (struct sockaddr*)&from, &len)) == INVALID_SOCKET) Sleep(10000); while(true){ // send/recv data break; } closesocket(client); closesocket(sock); WSACleanup(); return 0; }[/code] Now that code can only accept one connection at a time, lets modify it so it can accept and handle as many clients as you choose ;). NOTE: This is the old code: [code] #include #include #include #include // our thread for recving commands DWORD WINAPI receive_cmds(LPVOID lpParam) { printf("thread created\r\n"); // set our socket to the socket passed in as a parameter SOCKET current_client = (SOCKET)lpParam; // buffer to hold our recived data char buf[100]; // buffer to hold our sent data char sendData[100]; // for error checking int res; // our recv loop while(true) { res = recv(current_client,buf,sizeof(buf),0); // recv cmds Sleep(10); if(res == 0) { MessageBox(0,"error","error",MB_OK); closesocket(current_client); ExitThread(0); } if(strstr(buf,"hello")) { // greet this user printf("\nrecived hello cmd"); strcpy(sendData,"hello, greetz from KOrUPt\n"); Sleep(10); send(current_client,sendData,sizeof(sendData),0); } else if(strstr(buf,"bye")) { // dissconnected this user printf("\nrecived bye cmd\n"); strcpy(sendData,"cya\n"); Sleep(10); send(current_client,sendData,sizeof(sendData),0); // close the socket associted with this client and end this thread closesocket(current_client); ExitThread(0); } else { strcpy(sendData,"Invalid cmd\n"); Sleep(10); send(current_client,sendData,sizeof(sendData),0); } // clear buffers strcpy(sendData,""); strcpy(buf,""); } } int main() { printf("Starting up multi-threaded TCP server by KOrUPt\r\n"); // our masterSocket(socket that listens for connections) SOCKET sock; // for our thread DWORD thread; WSADATA wsaData; sockaddr_in server; // start winsock int ret = WSAStartup(0x101,&wsaData); // use highest version of winsock avalible if(ret != 0) { return 0; } // fill in winsock struct ... server.sin_family=AF_INET; server.sin_addr.s_addr=INADDR_ANY; server.sin_port=htons(123); // listen on telnet port 23 // create our socket sock=socket(AF_INET,SOCK_STREAM,0); if(sock == INVALID_SOCKET) { return 0; } // bind our socket to a port(port 123) if( bind(sock,(sockaddr*)&server,sizeof(server)) !=0 ) { return 0; } // listen for a connection if(listen(sock,5) != 0) { return 0; } // socket that we snedzrecv data on SOCKET client; sockaddr_in from; int fromlen = sizeof(from); // loop forever while(true) { // accept connections client = accept(sock,(struct sockaddr*)&from,&fromlen); printf("Client connected\r\n"); // create our recv_cmds thread and parse client socket as a parameter CreateThread(NULL, 0,receive_cmds,(LPVOID)client, 0, &thread); } // shutdown winsock closesocket(sock); WSACleanup(); // exit return 0; }[/code] Since then a lot has been learned not to mention my coding style has changed :P. This is the version i whipped up today :D, enjoy... just follow along and read the comments :D... [code] // linker options(MSVC only) #pragma comment(lib, "ws2_32.lib") // headers #include #include #include const int MAX_CLIENTS = 10; // number of max allowed clients to connect int clients_connected = 0; // number of currently connected clients DWORD thread; // for our thread(s) // our client object definintion typedef struct _client{ sockaddr_in address; // internal data structure regarding this client SOCKET socket; // this clients socket bool connected; // is this client connected char IP[20]; // this clients IP address int address_length; // internal data structure regarding this client } CLIENT, *PCLIENT; // create client object(s) CLIENT client[MAX_CLIENTS]; // declarations sockaddr_in server_address; sockaddr server_socket_address; SOCKET server_socket; // function declarations DWORD WINAPI ReceiveCmds(LPVOID lpParam); int start_server(int port); bool accept_connections(); bool AcceptClient(PCLIENT current_client); void disconnect_client(PCLIENT current_client); void end_server(); bool EchoMessage(char *message); bool send_data(PCLIENT current_client, char *buffer, int size); bool receive_data(PCLIENT current_client, char *buffer, int size); // start a server int start_server(int port) { int res, i = 1; // fill in socket structures server_address.sin_family = AF_INET; // use INET address family(IPv4) server_address.sin_addr.s_addr = INADDR_ANY; // allow anyone to connect server_address.sin_port = htons(port); // listen on port 23(telnet) memcpy(&server_socket_address, &server_address, sizeof(SOCKADDR_IN)); // setup our socket server_socket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); if(server_socket == INVALID_SOCKET || server_socket == SOCKET_ERROR){ MessageBox(0, "Invalid socket", "Error", MB_ICONSTOP | MB_OK); return 1; } // bind our socket to our port res = bind(server_socket, &server_socket_address, sizeof(server_socket_address)); if(res == SOCKET_ERROR){ MessageBox(0, "Error Binding socket to port", "Error", MB_ICONWARNING | MB_OK); return 1; } // listen for connections if(listen(server_socket, MAX_CLIENTS) == SOCKET_ERROR){ MessageBox(0, "Error listening on socket", "Error", MB_ICONWARNING | MB_OK); return 1; } printf("Listening for connections on port %d\n", port); return 0; } // accept a connection and fill in CLIENT structure bool AcceptClient(PCLIENT current_client) { // accept incoming connection current_client->address_length = sizeof(sockaddr); current_client->socket = accept(server_socket, (sockaddr*)¤t_client->address, ¤t_client->address_length); // check there were no errors if(current_client->socket == 0 || current_client->socket == SOCKET_ERROR) return false; // this client is now connected, you know what to do... current_client->connected = true; // start the receive commands thread and pass our client object to it CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReceiveCmds, (LPVOID)current_client, 0, &thread); return true; } // disconnect a connected client void disconnect_client(PCLIENT current_client) { // declare a variable to store the disconnect message char info[250]; // only continue if client is connected if(current_client->connected == false) return; // alert user that client disconnected wsprintf(info, "\n%s has disconnected...\n", current_client->IP); printf(info); // close the socket associated with this client closesocket(current_client->socket); // decrement the current number of connected clients clients_connected--; // set the new client connected state current_client->connected = false; // clear the address length current_client->address_length = -1; // clear info members memset(current_client->IP, 0, 20); return; } // handle incoming connections bool accept_connections() { // have we reached maximum amount of allowed connections if(clients_connected <= MAX_CLIENTS){ // loop until an empty slot is found for(int j = 0; j < MAX_CLIENTS; j++){ if(client[j].connected == false){ if(AcceptClient(&client[j])){ clients_connected++; // grab the ip address of the client char *client_ip_address = inet_ntoa(client[j].address.sin_addr); strncpy(client[j].IP, client_ip_address, 19); // fill in IP address info // output connection printf("%s has connected\n", client_ip_address); return true; } } } } return false; } // shutdown the server by disconnecting all connected clients void end_server() { // disconnect all clients for (int j = 0; j < MAX_CLIENTS; j++) disconnect_client(&client[j]); // close the listening socket for the server closesocket(server_socket); } // receive data bool receive_data(PCLIENT current_client, char *buffer, int size) { // store the return data of what we have sent current_client->address_length = recv(current_client->socket, buffer, size, 0); // error checking if(current_client->address_length <= 0){ disconnect_client(current_client); return false; } return true; } // send data bool send_data(PCLIENT current_client, char *buffer, int size) { // store the return information current_client->address_length = send(current_client->socket, buffer, size, 0); // check for errors while sending if((current_client->address_length == SOCKET_ERROR) || (current_client->address_length == 0 )){ disconnect_client(current_client); // disconnect client return false; } return true; } // send a message to all connected clients bool EchoMessage(char *message) { for(int j = 0; j < MAX_CLIENTS; j++){ if(client[j].connected){ if(send_data(&client[j], message, strlen(message)) != 0) return false; } } return true; } // our thread for receiving commands DWORD WINAPI ReceiveCmds(LPVOID lpParam) { PCLIENT current_client = (PCLIENT)lpParam; char buffer[512], *buf = NULL, *cmd = NULL; // receive commands loop while(1) { // if client connected... if(current_client->connected){ memset(buffer, 0, 512); // recevice a command if(receive_data(current_client, buffer, 511)){ // if we've recevied a command... if(strncmp(buffer, "/cmd ", 5) == 0){ buf = strtok(buffer, " "); if(buf){ cmd = strtok(NULL, "\r\n"); if(cmd){ printf("Recevied command: %s\n", cmd); // shutdown server if(!strncmp(cmd, "exit", 4)){ printf("Shutting down server in 5 seconds...\n"); EchoMessage("Shutting down server in 5 seconds...\n"); for(int i = 5; i != 0; i--){ printf("%d...\n", i); Sleep(1000); } // cleanup and exit server end_server(); WSACleanup(); printf("Server shutdown. exiting thread...\n"); ExitThread(0); } // echo message to all clients else if(!strncmp(cmd, "Echo ", 5)){ char* message = strtok(cmd, " "); if(message){ message = strtok(cmd, "\r\n"); if(message){ printf("Echoing message to all clients:\n\t\t %s\n\n", message); EchoMessage(message); } } continue; } else if(!strncmp(cmd, "DropClient", 10)){ send_data(current_client, "You will be disconnected\n", strlen("You will be disconnected\n")); disconnect_client(current_client); continue; } } continue; } } } } } ExitThread(0); } int main(void) { HANDLE hMutex; // for out mutex HANDLE hStdout; // for console colour/title WSADATA wsaData; // for winsock // create a mutex so we cant run more than once at the same time hMutex = CreateMutex(0, FALSE, ("K-TCP Tutorial")); if(GetLastError() == ERROR_ALREADY_EXISTS || GetLastError() == ERROR_ACCESS_DENIED){ CloseHandle(hMutex); return 1; } // never liked that horrible grey colour... hStdout = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hStdout, FOREGROUND_GREEN | FOREGROUND_INTENSITY); SetConsoleTitle("Multi-Threaded TCP server by KOrUPt"); printf("Starting Up Multi-Threaded TCP server by KOrUPt\n"); // start winsock if(WSAStartup(0x101, &wsaData) != NO_ERROR){ printf("Could not start winsock...\n"); return 1; } // start server if(start_server(23) != 0){ printf("Failed to start server\n"); return 0; } // start accepting connections while(1){ accept_connections(); Sleep(20000); } // disconnect clients shutdown winsock end_server(); WSACleanup(); // close mutex handle CloseHandle(hMutex); // exit return 0; }[/code] Well assuming you have a basic understanding of threads and winsock you should be able to follow and understand the above code and hopefully after a bit of experimenting build it into your own server application... I suggest you try and create a basic multi-threaded text file transfer server :)... If you have any questions relating to this tutorial reply within this thread or PM me. Hopefully this tutorial helped you out to some degree ;). Also if you want to test out the server just use the client I built. Client code attached below: NOTE: Old client code: [code]/*-------------------Echo_client.cpp-------------------*/ #include #include #include #include #include #include #include //DECLARATIONS //error trapping signals #define SIGINT 2 #define SIGKILL 9 #define SIGQUIT 3 // SOCKETS SOCKET sock,client; void s_handle(int s) { if(sock) closesocket(sock); if(client) closesocket(client); WSACleanup(); Sleep(1000); cout<<"EXIT SIGNAL :"<\n"; fgets(buf,sizeof(buf),stdin); Sleep(5); res = send(sock,buf,sizeof(buf),0); if(res==0) { //0==other side terminated conn printf("\nSERVER terminated connection\n"); Sleep(40); closesocket(client); client = 0; break; } else if(res==SOCKET_ERROR) { //-1 == send error printf("Socket error\n"); Sleep(40); s_handle(res); break; } ret = recv(sock,RecvdData,sizeof(RecvdData),0); if(ret > 0) { cout< #include #include #include // error trapping signals #define SIGKILL 9 #define SIGQUIT 3 #define port 23 // our port // sockets SOCKET sock, client; void HandleSignal(int s) { printf("Exit signal %d received\n", s); if(sock) closesocket(sock); if(client) closesocket(client); WSACleanup(); exit(0); } int main() { WSADATA data; SOCKADDR_IN server; SOCKADDR addr; FD_SET sockData; char ip[20], buff[512]; int blocking = 1; // non blocking sockets HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_INTENSITY); SetConsoleTitle(".:: Echo Client By KOrUPt 08 ::."); // so we can cleanup should the user exit whilist connected signal(SIGKILL, HandleSignal); signal(SIGQUIT, HandleSignal); printf("\n\n\t\tEcho Client by KOrUPt\n"); printf("\n\n\t\tEnter IP to connect to: "); fgets(ip, 19, stdin); memset(ip + (strlen(ip) - 1), 0, 1); // strip new line char server.sin_family = AF_INET; // use INET address family server.sin_port = htons(port); // use specifed port server.sin_addr.s_addr = inet_addr(ip); // use specifed IP address memcpy(&addr, &server, sizeof(SOCKADDR_IN)); // start Winsock if(WSAStartup(MAKEWORD(2, 2), &data) != NO_ERROR){ printf("Could not start winsock\n"); return 1; } // create the socket sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sock == INVALID_SOCKET || sock == SOCKET_ERROR){ printf("Could not create socket\n"); return 0; } // connect to the server if(connect(sock, &addr, sizeof(addr)) == SOCKET_ERROR){ printf("Could not connect to %s on port %d\n", ip, port); getchar(); return 1; } // print some info printf("Connected to Server %s\n", ip); memcpy(&server, &addr, sizeof(SOCKADDR)); // we want non blocking sockets FD_ZERO(&sockData); FD_SET(sock, &sockData); if(ioctlsocket(sock, FIONBIO, (u_long FAR*)&blocking) == SOCKET_ERROR){ printf("Could not set socket to blocking mode\n"); return 1; } // enter send/receive data loop while(true){ memset(buff, 0, 512); printf("\nEnter message to send ->\n\t\t"); fgets(buff, 511, stdin); // send command int ret = send(sock, buff, strlen(buff), 0); if(ret == 0){ printf("\nServer terminated connection\n"); HandleSignal(1); }else if(ret == SOCKET_ERROR){ printf("\nSocket error\n"); HandleSignal(1); }else{ memset(buff + (strlen(buff) - 1), 0, 1); // strip new line char printf("\nCommand %s sent\n", buff); } memset(buff, 0, 512); if(FD_ISSET(sock, &sockData)){ ret = recv(sock, buff, 511, 0); if(ret > 0) printf("Received data: %s\n", buff); } } closesocket(sock); WSACleanup(); return 0; }[/code] Well hope that helps, just thought I'd code it quickly so you could test out the server... well this is KOrUPt signing off... Cya in my next tutorial :D. P.S sorry about any typo's...