Communication over sockets involves 2 programs running on the same machine or on separate machines across a network.
First is a socket server and the other is a socket client. Tcp stands for Transmission control protocol and it is the most common protocol being used for most of the network communication that takes place over internet or a lan.
There are other important protocols like udp, arp, icmp and they are used mandatorily but in small quantities.
These protocols are technically speaking, define the format how data should be structured into packets when sending them across a network.
So every protocol has a different format and most importantly has a different purpose.
For example for all kinds of content download and upload, tcp is used. For network analysis the icmp and arp protocols are used and so on. Every protocol has been designed to serve a different purpose.
So in this post we shall see how to write a socket server on windows using the winsock api. Winsock is the socket api on windows and can be used to write socket speaking applications. The code shall be in C.
So a socket server is an application that runs on a machine and expects clients to connect to it so that it can serve them.
The steps to write a socket server are simple.
1. Create a socket. 2. Bind it to an ip and port. 3. Start listening on it 4. Accept incoming connections and process them.
Each of the above steps has an associated function as we shall soon see in the code.
Server Code
/* Bind socket to port 8888 on localhost */ #include<io.h> #include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; SOCKET s , new_socket; struct sockaddr_in server , client; int c; char *message; printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); return 1; } printf("Initialised.\n"); //Create a socket if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET) { printf("Could not create socket : %d" , WSAGetLastError()); } printf("Socket created.\n"); //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons( 8888 ); //Bind if( bind(s ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR) { printf("Bind failed with error code : %d" , WSAGetLastError()); } puts("Bind done"); //Listen to incoming connections listen(s , 3); //Accept and incoming connection puts("Waiting for incoming connections..."); c = sizeof(struct sockaddr_in); new_socket = accept(s , (struct sockaddr *)&client, &c); if (new_socket == INVALID_SOCKET) { printf("accept failed with error code : %d" , WSAGetLastError()); } puts("Connection accepted"); //Reply to client message = "Hello Client , I have received your connection. But I have to go now, bye\n"; send(new_socket , message , strlen(message) , 0); getchar(); closesocket(s); WSACleanup(); return 0; }
The above code will start a socket server on port 8888. Run it from one console and then in another console connect to it using the telnet command. The output of the telnet should look like this
Hello Client , I have received your connection. But I have to go now, bye
The server sends this message to the connected client. The server is built using the following important functions
1. socket - create a socket 2. bind - bind the socket to a local address and port 3. listen - make the socket listen to incoming connections. 4. accept - accept any incoming connections.
The accept function returns a new socket which indicates the connection between this program and the remote client program. The master socket can continue to receive the next connections. The above program demonstrates how a socket program can work, but it is very limited in its functionality. It can just accept 1 incoming connection and after that it is dead.
Handle multiple connections - Asynchronous socket programming
For this server to be any useful, it must be able to accept multiple incoming connections and keep processing them till the clients want. So the next attempt shall be to write a server that can handle multiple connections and tackle all of them simultaneously.
There are many ways to handle multiple client connections. The first and most intuitive one is using threads. As soon as a client connects, assign a separate thread to process each client. However threads are too much work and difficult to code properly.
There are other techniques like polling. Polling involves monitoring multiple sockets to see if "something" happened on any of them. For example, the server could be monitoring the sockets of 5 connected clients, and as soon as any of them send a message, the server gets notified of the event and then processes it. In this way it can handle multiple sockets. The winsock api provides a function called "select" which can monitor multiple sockets for some activity.
Since we are able to handle all sockets together at once it is called asynchronous socket programming. It is also called event-driven socket programming or select()-based multiplexing.
The select function prototype is like this
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
The first parameter is a dummy one. The readfds parameter is a pointer to an array of sockets which should be monitored to be readable. This means that if any socket in the readfds set receives some data, it becomes readable. Similarly the writefds sockets would be monitored to be writable and the exceptfds sockets shall be monitored for any error. The last parameter is the timeout parameter, which indicates the length of time which the select function shall wait for before returning.
Now after a select function returns, it re-fills the same readfds array with the readable sockets. Same with writefds and exceptfds. This means that we have to keep calling select function in a loop, and everytime have to prepare our list of readfds, writefds and exceptfds array of sockets to pass.
The socket arrays are variables of type fd_set. fd_set is basically a structure that looks like this
typedef struct fd_set { u_int fd_count; SOCKET fd_array[FD_SETSIZE]; } fd_set;
To work with fd_set array the following macros have to be used.
FD_CLR(s, *set) - Removes a socket from an fd_set structure FD_ISSET(s, *set) - Checks if a socket is present in an fd_set structure FD_SET(s, *set) - Adds a socket to an fd_set structure FD_ZERO(*set) - Initializes the set to the null set. This will empty an fd_set structure
Now that is a lot of theory. Lets get to the final code that uses all that theory to get something working.
/* TCP Echo server example in winsock Live Server on port 8888 */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib, "ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; SOCKET master , new_socket , client_socket[30] , s; struct sockaddr_in server, address; int max_clients = 30 , activity, addrlen, i, valread; char *message = "ECHO Daemon v1.0 \r\n"; //size of our receive buffer, this is string length. int MAXRECV = 1024; //set of socket descriptors fd_set readfds; //1 extra for null character, string termination char *buffer; buffer = (char*) malloc((MAXRECV + 1) * sizeof(char)); for(i = 0 ; i < 30;i++) { client_socket[i] = 0; } printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); exit(EXIT_FAILURE); } printf("Initialised.\n"); //Create a socket if((master = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET) { printf("Could not create socket : %d" , WSAGetLastError()); exit(EXIT_FAILURE); } printf("Socket created.\n"); //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons( 8888 ); //Bind if( bind(master ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR) { printf("Bind failed with error code : %d" , WSAGetLastError()); exit(EXIT_FAILURE); } puts("Bind done"); //Listen to incoming connections listen(master , 3); //Accept and incoming connection puts("Waiting for incoming connections..."); addrlen = sizeof(struct sockaddr_in); while(TRUE) { //clear the socket fd set FD_ZERO(&readfds); //add master socket to fd set FD_SET(master, &readfds); //add child sockets to fd set for ( i = 0 ; i < max_clients ; i++) { s = client_socket[i]; if(s > 0) { FD_SET( s , &readfds); } } //wait for an activity on any of the sockets, timeout is NULL , so wait indefinitely activity = select( 0 , &readfds , NULL , NULL , NULL); if ( activity == SOCKET_ERROR ) { printf("select call failed with error code : %d" , WSAGetLastError()); exit(EXIT_FAILURE); } //If something happened on the master socket , then its an incoming connection if (FD_ISSET(master , &readfds)) { if ((new_socket = accept(master , (struct sockaddr *)&address, (int *)&addrlen))<0) { perror("accept"); exit(EXIT_FAILURE); } //inform user of socket number - used in send and receive commands printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); //send new connection greeting message if( send(new_socket, message, strlen(message), 0) != strlen(message) ) { perror("send failed"); } puts("Welcome message sent successfully"); //add new socket to array of sockets for (i = 0; i < max_clients; i++) { if (client_socket[i] == 0) { client_socket[i] = new_socket; printf("Adding to list of sockets at index %d \n" , i); break; } } } //else its some IO operation on some other socket :) for (i = 0; i < max_clients; i++) { s = client_socket[i]; //if client presend in read sockets if (FD_ISSET( s , &readfds)) { //get details of the client getpeername(s , (struct sockaddr*)&address , (int*)&addrlen); //Check if it was for closing , and also read the incoming message //recv does not place a null terminator at the end of the string (whilst printf %s assumes there is one). valread = recv( s , buffer, MAXRECV, 0); if( valread == SOCKET_ERROR) { int error_code = WSAGetLastError(); if(error_code == WSAECONNRESET) { //Somebody disconnected , get his details and print printf("Host disconnected unexpectedly , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); //Close the socket and mark as 0 in list for reuse closesocket( s ); client_socket[i] = 0; } else { printf("recv failed with error code : %d" , error_code); } } if ( valread == 0) { //Somebody disconnected , get his details and print printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); //Close the socket and mark as 0 in list for reuse closesocket( s ); client_socket[i] = 0; } //Echo back the message that came in else { //add null character, if you want to use with printf/puts or other string handling functions buffer[valread] = '/* TCP Echo server example in winsock Live Server on port 8888 */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib, "ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; SOCKET master , new_socket , client_socket[30] , s; struct sockaddr_in server, address; int max_clients = 30 , activity, addrlen, i, valread; char *message = "ECHO Daemon v1.0 \r\n"; //size of our receive buffer, this is string length. int MAXRECV = 1024; //set of socket descriptors fd_set readfds; //1 extra for null character, string termination char *buffer; buffer = (char*) malloc((MAXRECV + 1) * sizeof(char)); for(i = 0 ; i < 30;i++) { client_socket[i] = 0; } printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); exit(EXIT_FAILURE); } printf("Initialised.\n"); //Create a socket if((master = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET) { printf("Could not create socket : %d" , WSAGetLastError()); exit(EXIT_FAILURE); } printf("Socket created.\n"); //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons( 8888 ); //Bind if( bind(master ,(struct sockaddr *)&server , sizeof(server)) == SOCKET_ERROR) { printf("Bind failed with error code : %d" , WSAGetLastError()); exit(EXIT_FAILURE); } puts("Bind done"); //Listen to incoming connections listen(master , 3); //Accept and incoming connection puts("Waiting for incoming connections..."); addrlen = sizeof(struct sockaddr_in); while(TRUE) { //clear the socket fd set FD_ZERO(&readfds); //add master socket to fd set FD_SET(master, &readfds); //add child sockets to fd set for ( i = 0 ; i < max_clients ; i++) { s = client_socket[i]; if(s > 0) { FD_SET( s , &readfds); } } //wait for an activity on any of the sockets, timeout is NULL , so wait indefinitely activity = select( 0 , &readfds , NULL , NULL , NULL); if ( activity == SOCKET_ERROR ) { printf("select call failed with error code : %d" , WSAGetLastError()); exit(EXIT_FAILURE); } //If something happened on the master socket , then its an incoming connection if (FD_ISSET(master , &readfds)) { if ((new_socket = accept(master , (struct sockaddr *)&address, (int *)&addrlen))<0) { perror("accept"); exit(EXIT_FAILURE); } //inform user of socket number - used in send and receive commands printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); //send new connection greeting message if( send(new_socket, message, strlen(message), 0) != strlen(message) ) { perror("send failed"); } puts("Welcome message sent successfully"); //add new socket to array of sockets for (i = 0; i < max_clients; i++) { if (client_socket[i] == 0) { client_socket[i] = new_socket; printf("Adding to list of sockets at index %d \n" , i); break; } } } //else its some IO operation on some other socket :) for (i = 0; i < max_clients; i++) { s = client_socket[i]; //if client presend in read sockets if (FD_ISSET( s , &readfds)) { //get details of the client getpeername(s , (struct sockaddr*)&address , (int*)&addrlen); //Check if it was for closing , and also read the incoming message //recv does not place a null terminator at the end of the string (whilst printf %s assumes there is one). valread = recv( s , buffer, MAXRECV, 0); if( valread == SOCKET_ERROR) { int error_code = WSAGetLastError(); if(error_code == WSAECONNRESET) { //Somebody disconnected , get his details and print printf("Host disconnected unexpectedly , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); //Close the socket and mark as 0 in list for reuse closesocket( s ); client_socket[i] = 0; } else { printf("recv failed with error code : %d" , error_code); } } if ( valread == 0) { //Somebody disconnected , get his details and print printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); //Close the socket and mark as 0 in list for reuse closesocket( s ); client_socket[i] = 0; } //Echo back the message that came in else { //add null character, if you want to use with printf/puts or other string handling functions buffer[valread] = '\0'; printf("%s:%d - %s \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port), buffer); send( s , buffer , valread , 0 ); } } } } closesocket(s); WSACleanup(); return 0; }'; printf("%s:%d - %s \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port), buffer); send( s , buffer , valread , 0 ); } } } } closesocket(s); WSACleanup(); return 0; }
Does that look like a big program. Compile and run it. It should show an output like this
Initialising Winsock...Initialised. Socket created. Bind done Waiting for incoming connections...
Now the socket server is ready and waiting for incoming connection. At this point we need to connect to it using some client like telnet. But wait, we are not going to use telnet. Telnet has a problem that it always operates in character mode and that will screw up our interaction with this simple program. So get another utility called putty or ncat. Ncat is the netcat version that comes with nmap. Download it from their website. Or download puttytel , the putty telnet.
If you are using ncat then connect to the socket server like this
C:\>ncat localhost 8888
If you are using puttytel, the launch it and go to Connection > Telnet and select Passive mode. This will make putty line mode. Then come back to Session tab and enter the hostname and port and click open. it will connect to the server and start a black telnet like terminal.
Once the client program is connected with the server, try sending some message by typing first and then hit enter. The server will reply back with the same message.
C:\>ncat localhost 8888 ECHO Daemon v1.0 hello hello how are you how are you i am fine i am fine
The server terminal would look like this
Initialising Winsock...Initialised. Socket created. Bind done Waiting for incoming connections... New connection , socket fd is 3972 , ip is : 127.0.0.1 , port : 1129 Welcome message sent successfully Adding to list of sockets at index 0 127.0.0.1:1129 - hello 127.0.0.1:1129 - how are you 127.0.0.1:1129 - i am fine
And now, try to open multiple client terminals and connect at the same time to server. The server would be able to process requests from all the clients together.
Initialising Winsock...Initialised. Socket created. Bind done Waiting for incoming connections... New connection , socket fd is 3972 , ip is : 127.0.0.1 , port : 1129 Welcome message sent successfully Adding to list of sockets at index 0 127.0.0.1:1129 - hello 127.0.0.1:1129 - how are you 127.0.0.1:1129 - i am fine New connection , socket fd is 3956 , ip is : 127.0.0.1 , port : 1130 Welcome message sent successfully Adding to list of sockets at index 1 127.0.0.1:1130 - i am the second client New connection , socket fd is 3944 , ip is : 127.0.0.1 , port : 1131 Welcome message sent successfully Adding to list of sockets at index 2 127.0.0.1:1131 - and i am the third 127.0.0.1:1131 - ha ha ha
Now thats a long run. I will go and take a cup of coffee, and meanwhile you check if the programs are running fine.
Thanks a lot
looking for this for a long time
and finally found what I exactly need here :)
It is a wonderful blog! Thank you so much!
Won’t even compile. “amp” is undefined. the &… symbols are weird. Waste of time.
Replace & by & and replace " by ” everything will work.
sorry for the wrong characters in the code.
i have fixed it. should run fine now
As we already have added master to the readfds set, will it not always return 1 for the below command
//If something happened on the master socket , then its an incoming connection
if (FD_ISSET(master , &readfds))
{
this code compiles with about 3000 errors, why?
Client Program Please.
By the way, there is no problem with telnet on Mac OS X sending connections to the server app running on windows.It works just fine.
Thanks, Saved me a great, great deal!
Nice, very nice. After two days trying to find some C examples showing how to do such things (suprisingly uncommon, considering how much such things are done!) This was built and run in CodeBlocks 16.01 just fine, once I’d also figured out what library linkage to add to the projects build options. (..\..\..\..\MinGW\lib\libws2_32.a) In both the Release and Debug contexts, using C::B 16.01 on Windows 7 Pro 64 bit. (The resulting exe is only 10k in size!)
Re the comment about “While(True)” and potential CPU usage. That’s as maybe, but I see no such huge CPU usage, just a brief spike when the project first loads, then the CPU usage for the project drops to zero while nothing is happening. I’m using PuTTY as the client for testing, that sucks more CPU (0.01%!) than the echo server when it’s idle. Using the excelent Process Explorer tool to examine whats going on in the system. https://technet.microsoft.com/en-gb/sysinternals/bb896653 By Mark Russinovich.
What would be good to know how to do, is to bind to only the loopback address, and/or only accept incoming connections FROM the loopback address (for some sort of simple security, rather than expose it to the external LAN, or much wider world!)
Many thanks again from a relative C novice.
Dave B. (Yes you may contact me if needed.)
Opening 32 sockets is not a simple example.
good as a temporary solution
the while (true) instruction will occupy the whole cpu ressources and this is not acceptable, an event based solution should be considered.
this is the best tutorial i have ever seen .. it helps me a lot . but , is there any tutorial on how to transport files through socket ,,if any ,please give me a link.
Thanks for this. However, the telnet client is sending only one byte at a time. I’m not able to figure out why though. When I start typing in the telnet client, say “ABC”, each character is sent, printed at the server and returned to the client. So, when I type say “ABC” in the client, it actually reads “AABBCC”. Any ideas? Thanks for your help.
this happens on windows, because the windows telnet client is in character mode.
in character mode the telnet client will send the individual characters the moment they are typed.
the only solution to this is to set the windows telnet client to “line mode”, which I dont know how to do.
best solution is to use ncat program which comes with nmap. It is a telnet like utility with similar syntax.
C:> ncat localhost 5000
ncat will send full lines on pressing the enter key. Or use putty in “passive negotiation mode” and it will send only full lines on pressing enter key.
Actually, the telnet protocol works that way, a character at a time. This happens both in Windows and in Unix.