Socket programming with winsock
This is a quick guide/tutorial to learning socket programming in C language on Windows. «Windows» because the code snippets shown over here will work only on Windows. The windows api to socket programming is called winsock.
Sockets are the fundamental «things» behind any kind of network communications done by your computer. For example when you type www.google.com in your web browser, it opens a socket and connects to google.com to fetch the page and show it to you.
Same with any chat client like gtalk or skype. Any network communication goes through a socket.
Before you begin
This tutorial assumes that you have basic knowledge of C and pointers. Also download Visual C++ 2010 Express Edition.
Initialising Winsock
Winsock first needs to be initialiased like this :
/* Initialise Winsock */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); return 1; } printf("Initialised."); return 0; }
winsock2.h is the header file to be included for winsock functions. ws2_32.lib is the library file to be linked with the program to be able to use winsock functions.
The WSAStartup function is used to start or initialise winsock library. It takes 2 parameters ; the first one is the version we want to load and second one is a WSADATA structure which will hold additional information after winsock has been loaded.
If any error occurs then the WSAStartup function would return a non zero value and WSAGetLastError can be used to get more information about what error happened.
OK , so next step is to create a socket.
Creating a socket
The socket()
function is used to create a socket.
Here is a code sample :
/* Create a TCP socket */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; SOCKET s; printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); return 1; } printf("Initialised.\n"); if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET) { printf("Could not create socket : %d" , WSAGetLastError()); } printf("Socket created.\n"); return 0; }
Function socket()
creates a socket and returns a socket descriptor which can be used in other network commands. The above code will create a socket of :
Address Family : AF_INET (this is IP version 4)
Type : SOCK_STREAM (this means connection oriented TCP protocol)
Protocol : 0 [ or IPPROTO_TCP , IPPROTO_UDP ]
It would be a good idea to read some documentation here
Ok , so you have created a socket successfully. But what next ? Next we shall try to connect to some server using this socket. We can connect to www.google.com
Note
Apart from SOCK_STREAM type of sockets there is another type called SOCK_DGRAM which indicates the UDP protocol. This type of socket is non-connection socket. In this tutorial we shall stick to SOCK_STREAM or TCP sockets.
Connect to a Server
We connect to a remote server on a certain port number. So we need 2 things , IP address and port number to connect to.
To connect to a remote server we need to do a couple of things. First is create a sockaddr_in structure with proper values filled in. Lets create one for ourselves :
struct sockaddr_in server;
Have a look at the structures
// IPv4 AF_INET sockets: struct sockaddr_in { short sin_family; // e.g. AF_INET, AF_INET6 unsigned short sin_port; // e.g. htons(3490) struct in_addr sin_addr; // see struct in_addr, below char sin_zero[8]; // zero this if you want to }; typedef struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; } IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR; struct sockaddr { unsigned short sa_family; // address family, AF_xxx char sa_data[14]; // 14 bytes of protocol address };
The sockaddr_in has a member called sin_addr of type in_addr which has a s_addr which is nothing but a long. It contains the IP address in long format.
Function inet_addr
is a very handy function to convert an IP address to a long format. This is how you do it :
server.sin_addr.s_addr = inet_addr("74.125.235.20");
So you need to know the IP address of the remote server you are connecting to. Here we used the ip address of google.com as a sample. A little later on we shall see how to find out the ip address of a given domain name.
The last thing needed is the connect
function. It needs a socket and a sockaddr structure to connect to. Here is a code sample.
/* Create a TCP socket */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; SOCKET s; struct sockaddr_in server; 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"); server.sin_addr.s_addr = inet_addr("74.125.235.20"); server.sin_family = AF_INET; server.sin_port = htons( 80 ); //Connect to remote server if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0) { puts("connect error"); return 1; } puts("Connected"); return 0; }
It cannot be any simpler. It creates a socket and then connects. If you run the program it should show Connected.
Try connecting to a port different from port 80 and you should not be able to connect which indicates that the port is not open for connection.
OK , so we are now connected. Lets do the next thing , sending some data to the remote server.
Quick Note
The concept of «connections» apply to SOCK_STREAM/TCP type of sockets. Connection means a reliable «stream» of data such that there can be multiple such streams each having communication of its own. Think of this as a pipe which is not interfered by other data.
Other sockets like UDP , ICMP , ARP dont have a concept of «connection». These are non-connection based communication. Which means you keep sending or receiving packets from anybody and everybody.
Sending Data
Function send
will simply send data. It needs the socket descriptor , the data to send and its size.
Here is a very simple example of sending some data to google.com ip :
/* Create a TCP socket */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; SOCKET s; struct sockaddr_in server; 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"); server.sin_addr.s_addr = inet_addr("74.125.235.20"); server.sin_family = AF_INET; server.sin_port = htons( 80 ); //Connect to remote server if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0) { puts("connect error"); return 1; } puts("Connected"); //Send some data message = "GET / HTTP/1.1\r\n\r\n"; if( send(s , message , strlen(message) , 0) < 0) { puts("Send failed"); return 1; } puts("Data Send\n"); return 0; }
In the above example , we first connect to an ip address and then send the string message «GET / HTTP/1.1\r\n\r\n» to it.
The message is actually a http command to fetch the mainpage of a website.
Now that we have send some data , its time to receive a reply from the server. So lets do it.
Receiving Data
Function recv
is used to receive data on a socket. In the following example we shall send the same message as the last example and receive a reply from the server.
/* Create a TCP socket */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; SOCKET s; struct sockaddr_in server; char *message , server_reply[2000]; int recv_size; 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"); server.sin_addr.s_addr = inet_addr("74.125.235.20"); server.sin_family = AF_INET; server.sin_port = htons( 80 ); //Connect to remote server if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0) { puts("connect error"); return 1; } puts("Connected"); //Send some data message = "GET / HTTP/1.1\r\n\r\n"; if( send(s , message , strlen(message) , 0) < 0) { puts("Send failed"); return 1; } puts("Data Send\n"); //Receive a reply from the server if((recv_size = recv(s , server_reply , 2000 , 0)) == SOCKET_ERROR) { puts("recv failed"); } puts("Reply received\n"); //Add a NULL terminating character to make it a proper string before printing server_reply[recv_size] = '/* Create a TCP socket */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; SOCKET s; struct sockaddr_in server; char *message , server_reply[2000]; int recv_size; 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"); server.sin_addr.s_addr = inet_addr("74.125.235.20"); server.sin_family = AF_INET; server.sin_port = htons( 80 ); //Connect to remote server if (connect(s , (struct sockaddr *)&server , sizeof(server)) < 0) { puts("connect error"); return 1; } puts("Connected"); //Send some data message = "GET / HTTP/1.1\r\n\r\n"; if( send(s , message , strlen(message) , 0) < 0) { puts("Send failed"); return 1; } puts("Data Send\n"); //Receive a reply from the server if((recv_size = recv(s , server_reply , 2000 , 0)) == SOCKET_ERROR) { puts("recv failed"); } puts("Reply received\n"); //Add a NULL terminating character to make it a proper string before printing server_reply[recv_size] = '\0'; puts(server_reply); return 0; }';
puts(server_reply);return 0;
}Here is the output of the above code :
Initialising Winsock...Initialised. Socket created. Connected Data Send Reply received HTTP/1.1 302 Found Location: http://www.google.co.in/ Cache-Control: private Content-Type: text/html; charset=UTF-8 Set-Cookie: PREF=ID=7da819edfd7af808:FF=0:TM=1324882923:LM=1324882923:S=PdlMu0TE E3QKrmdB; expires=Wed, 25-Dec-2013 07:02:03 GMT; path=/; domain=.google.com Date: Mon, 26 Dec 2011 07:02:03 GMT Server: gws Content-Length: 221 X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>302 Moved</TITLE></HEAD><BODY> <H1>302 Moved</H1> The document has moved <A HREF="http://www.google.co.in/">here</A>. </BODY></HTML> Press any key to continueWe can see what reply was send by the server. It looks something like Html, well IT IS html. Google.com replied with the content of the page we requested. Quite simple!
Now that we have received our reply, its time to close the socket.
Close socket
Function
closesocket
is used to close the socket. Also WSACleanup must be called to unload the winsock library (ws2_32.dll).closesocket(s); WSACleanup();Thats it.
Lets Revise
So in the above example we learned how to :
1. Create a socket
2. Connect to remote server
3. Send some data
4. Receive a replyIts useful to know that your web browser also does the same thing when you open www.google.com
This kind of socket activity represents a CLIENT. A client is a system that connects to a remote system to fetch or retrieve data.The other kind of socket activity is called a SERVER. A server is a system that uses sockets to receive incoming connections and provide them with data. It is just the opposite of Client. So www.google.com is a server and your web browser is a client. Or more technically www.google.com is a HTTP Server and your web browser is an HTTP client.
Now its time to do some server tasks using sockets. But before we move ahead there are a few side topics that should be covered just incase you need them.
Get IP address of a hostname/domain
When connecting to a remote host , it is necessary to have its IP address. Function
gethostbyname
is used for this purpose. It takes the domain name as the parameter and returns a structure of type hostent. This structure has the ip information. It is present innetdb.h
. Lets have a look at this structure/* Description of data base entry for a single host. */ struct hostent { char *h_name; /* Official name of host. */ char **h_aliases; /* Alias list. */ int h_addrtype; /* Host address type. */ int h_length; /* Length of address. */ char **h_addr_list; /* List of addresses from name server. */ };The
h_addr_list
has the IP addresses. So now lets have some code to use them./* Get IP address from domain name */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; char *hostname = "www.google.com"; char ip[100]; struct hostent *he; struct in_addr **addr_list; int i; printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); return 1; } printf("Initialised.\n"); if ( (he = gethostbyname( hostname ) ) == NULL) { //gethostbyname failed printf("gethostbyname failed : %d" , WSAGetLastError()); return 1; } //Cast the h_addr_list to in_addr , since h_addr_list also has the ip address in long format only addr_list = (struct in_addr **) he->h_addr_list; for(i = 0; addr_list[i] != NULL; i++) { //Return the first one; strcpy(ip , inet_ntoa(*addr_list[i]) ); } printf("%s resolved to : %s\n" , hostname , ip); return 0; return 0; }Output of the code would look like :
www.google.com resolved to : 74.125.235.20So the above code can be used to find the ip address of any domain name. Then the ip address can be used to make a connection using a socket.
Function
inet_ntoa
will convert an IP address in long format to dotted format. This is just the opposite ofinet_addr
.So far we have see some important structures that are used. Lets revise them :
1.
sockaddr_in
— Connection information. Used by connect , send , recv etc.
2.in_addr
— Ip address in long format
3.sockaddr
4.hostent
— The ip addresses of a hostname. Used by gethostbynameServer Concepts
OK now onto server things. Servers basically do the following :
1. Open a socket
2. Bind to a address(and port).
3. Listen for incoming connections.
4. Accept connections
5. Read/SendWe have already learnt how to open a socket. So the next thing would be to bind it.
Bind a socket
Function
bind
can be used to bind a socket to a particular address and port. It needs a sockaddr_in structure similar to connect function.Lets see a code example :
/* Bind socket to port 8888 on localhost */ #include<stdio.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library int main(int argc , char *argv[]) { WSADATA wsa; SOCKET s; struct sockaddr_in server; 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"); closesocket(s); return 0; }Now that bind is done, its time to make the socket listen to connections. We bind a socket to a particular IP address and a certain port number. By doing this we ensure that all incoming data which is directed towards this port number is received by this application.
This makes it obvious that you cannot have 2 sockets bound to the same port.
Listen for connections
After binding a socket to a port the next thing we need to do is listen for connections. For this we need to put the socket in listening mode. Function
listen
is used to put the socket in listening mode. Just add the following line after bind.//Listen listen(s , 3);Thats all. Now comes the main part of accepting new connections.
Accept connection
Function
accept
is used for this. Here is the code/* Bind socket to port 8888 on localhost */ #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; 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"); closesocket(s); WSACleanup(); return 0; }Output
Run the program. It should show
Initialising Winsock...Initialised. Socket created. Bind done Waiting for incoming connections...So now this program is waiting for incoming connections on port 8888. Dont close this program , keep it running.
Now a client can connect to it on this port. We shall use the telnet client for testing this. Open a terminal and typetelnet localhost 8888And the server output will show
Initialising Winsock...Initialised. Socket created. Bind done Waiting for incoming connections... Connection accepted Press any key to continueSo we can see that the client connected to the server. Try the above process till you get it perfect.
Note
You can get the ip address of client and the port of connection by using the sockaddr_in structure passed to accept function. It is very simple :
char *client_ip = inet_ntoa(client.sin_addr); int client_port = ntohs(client.sin_port);We accepted an incoming connection but closed it immediately. This was not very productive. There are lots of things that can be done after an incoming connection is established. Afterall the connection was established for the purpose of communication. So lets reply to the client.
Here is an example :
/* 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; }Run the above code in 1 terminal. And connect to this server using telnet from another terminal and you should see this :
Hello Client , I have received your connection. But I have to go now, byeSo the client(telnet) received a reply from server. We had to use a getchar because otherwise the output would scroll out of the client terminal without waiting
We can see that the connection is closed immediately after that simply because the server program ends after accepting and sending reply. A server like www.google.com is always up to accept incoming connections.
It means that a server is supposed to be running all the time. Afterall its a server meant to serve. So we need to keep our server RUNNING non-stop. The simplest way to do this is to put the
accept
in a loop so that it can receive incoming connections all the time.Live Server
So a live server will be alive for all time. Lets code this up :
/* Live Server on port 8888 */ #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()); exit(EXIT_FAILURE); } puts("Bind done"); //Listen to incoming connections listen(s , 3); //Accept and incoming connection puts("Waiting for incoming connections..."); c = sizeof(struct sockaddr_in); while( (new_socket = accept(s , (struct sockaddr *)&client, &c)) != INVALID_SOCKET ) { puts("Connection accepted"); //Reply to the client message = "Hello Client , I have received your connection. But I have to go now, bye\n"; send(new_socket , message , strlen(message) , 0); } if (new_socket == INVALID_SOCKET) { printf("accept failed with error code : %d" , WSAGetLastError()); return 1; } closesocket(s); WSACleanup(); return 0; }We havent done a lot there. Just the accept was put in a loop.
Now run the program in 1 terminal , and open 3 other terminals. From each of the 3 terminal do a telnet to the server port.
Run telnet like this. It will launch the interactive prompt.
C:\>telnetAt the telnet shell, run the command «open localhost 8888». This command will try to connect to localhost on port number 8888.
Welcome to Microsoft Telnet Client Escape Character is 'CTRL+]' Microsoft Telnet> open localhost 8888Next you should see the following message at the telnet prompt. This message is received from the socket server running on port 8888.
Hello Client , I have received your connection. But I have to go now, byeOn the other hand, the server terminal would show the following messages, indicating that a client connected to it.
Initialising Winsock...Initialised. Socket created. Bind done Waiting for incoming connections... Connection accepted Connection acceptedSo now the server is running nonstop and the telnet terminals are also connected nonstop. Now close the server program.
All telnet terminals would show «Connection to host lost.»
Good so far. But still there is not effective communication between the server and the client.The server program accepts connections in a loop and just send them a reply, after that it does nothing with them. Also it is not able to handle more than 1 connection at a time. So now its time to handle the connections , and handle multiple connections together.
Handling Connections
To handle every connection we need a separate handling code to run along with the main server accepting connections.
One way to achieve this is using threads. The main server program accepts a connection and creates a new thread to handle communication for the connection, and then the server goes back to accept more connections.We shall now use threads to create handlers for each connection the server accepts. Lets do it pal.
Run the above server and open 3 terminals like before. Now the server will create a thread for each client connecting to it.
The telnet terminals would show :
This one looks good , but the communication handler is also quite dumb. After the greeting it terminates. It should stay alive and keep communicating with the client.
One way to do this is by making the connection handler wait for some message from a client as long as the client is connected. If the client disconnects , the connection handler ends.
So the connection handler can be rewritten like this :
The above connection handler takes some input from the client and replies back with the same. Simple! Here is how the telnet output might look
So now we have a server thats communicative. Thats useful now.
Conclusion
The winsock api is quite similar to Linux sockets in terms of function name and structures. Few differences exist like :
1. Winsock needs to be initialised with the WSAStartup function. No such thing in linux.
2. Header file names are different. Winsock needs winsock2.h , whereas Linux needs socket.h , apra/inet.h , unistd.h and many others.
3. Winsock function to close a socket is
closesocket
, whereas on Linux it isclose
.
On Winsock WSACleanup must also be called to unload the winsock dll.4. On winsock the error number is fetched by the function
WSAGetLastError()
. On Linux the errno variable from errno.h file is filled with the error number.And there are many more differences as we go deep.
By now you must have learned the basics of socket programming in C. You can try out some experiments like writing a chat client or something similar.
If you think that the tutorial needs some addons or improvements or any of the code snippets above dont work then feel free to make a comment below so that it gets fixed.
This article is for programmers with the following requirements:
Before you start learning socket programming, make sure you already have a certain basic knowledge of network such as understanding what is IP address, TCP, UDP.
Before we start our tutorial, keep in mind that the following tutorial only works for Linux OS environment. If you are using Windows, I have to apologize to you because Windows has its own socket programming and it is different from Linux even though the connection concept is the same. Well, first copy and paste the following code and run it on server and client, respectively.
Both codes can be run on the same computer.
It is always easy to understand after getting the code to work.
Socket-server.c
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> int main(void) { int listenfd = 0,connfd = 0; struct sockaddr_in serv_addr; char sendBuff[1025]; int numrv; listenfd = socket(AF_INET, SOCK_STREAM, 0); printf("socket retrieve success\n"); memset(&serv_addr, '0', sizeof(serv_addr)); memset(sendBuff, '0', sizeof(sendBuff)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(5000); bind(listenfd, (struct sockaddr*)&serv_addr,sizeof(serv_addr)); if(listen(listenfd, 10) == -1){ printf("Failed to listen\n"); return -1; } while(1) { connfd = accept(listenfd, (struct sockaddr*)NULL ,NULL); strcpy(sendBuff, "Message from server"); write(connfd, sendBuff, strlen(sendBuff)); close(connfd); sleep(1); } return 0; }
Socket-client.c
#include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <arpa/inet.h> int main(void) { int sockfd = 0,n = 0; char recvBuff[1024]; struct sockaddr_in serv_addr; memset(recvBuff, '0' ,sizeof(recvBuff)); if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0) { printf("\n Error : Could not create socket \n"); return 1; } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(5000); serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0) { printf("\n Error : Connect Failed \n"); return 1; } while((n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0) { recvBuff[n] = 0; if(fputs(recvBuff, stdout) == EOF) { printf("\n Error : Fputs error"); } printf("\n"); } if( n < 0) { printf("\n Read Error \n"); } return 0; }
After debugging both source files, run Socket-server.out
, then run Socket-client
. Attention here, never mess up with the order of executing Socket-server.out
and Socket-client
. Socket-server
must be executed first, then execute Socket-client.out
and never try to break Socket-server
forever loop. It means, you need to open two terminals to run each of the outputs.
When you execute Socket-cli
, I guess you will get the following result:
If you see the message above, congratulations, you have success with your first step to networking programming. Otherwise, do some checking on your development environment or try to run some simple code for instance hello world.
Why Both Server and Client on the Same Computer?
The answer is the server and client both are software but not hardware. It means what is happening on the top is there are two different software executed. To be more precise, the server and client are two different processes with different jobs. If you are experienced with constructing a server, you might find out that a server can be built on a home computer by installing a server OS. It is because server is a kind of software.
Understand Sockets
Imagine a socket as a seaport that allows a ship to unload and gather shipping, whereas socket is the place where a computer gathers and puts data into the internet.
Configure Socket
Things that need to be initialized are listed as follows:
- Using TCP or UDP
- Additional protocol
- Permit the incoming IP address
- Assign the port used
At the beginning, a socket function needs to be declared to get the socket descriptor.
int socket(int domain, int type, int protocol)
Domain | AF_UNIX — connect inside same machine AF_INET – connect with different machine |
Type | SOCK_STREAM – TCP connection SOCK_DGRAM – UDP connection |
Protocol | Define here when there is any additional protocol. Otherwise, define it as 0 |
Next, decide which struct
needs to be used based on what domain is used above.
AF_UNIX | AF_INET |
C# struct sockaddr_un { sa_family_t sun_family ; char sun_path[]; }; |
C# struct sockaddr_in { short int sin_family ; int sin_port; struct in_addr sin_addr; }; |
Use struct sockaddr_un if you are using AF_UNIX on your domain. It is required to include <sys/un.h> |
Use struct sockaddr_in if you are using AF_INT on your domain. |
In this article, I will explain sockadd_in
that showed in the code above.
serv_addr.sin_family = AF_INET; |
Define the domain used |
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); |
Permit any incoming IP address by declaring INADDR_ANY |
serv_addr.sin_port = htons(5000);
|
Declare port 5000 to be used. |
Based on the example above, server is using port 5000. You can check it by the following command:
sudo netstat -ntlp
Then, you will see the following list:
Inside red bracket, you will find 0.0.0.0:5000 and Socket-server
, it means port 5000 is used and listen to any valid incoming address.
On client side, serv_addr.sin_port = htons(127.0.0.1)
is declared in order to listen to the internal network.
The flow chart below shows the interaction between client and server. The flow chart might look complicated but make sure you don’t lose your patience due to the following flow chart. Because every process on the flow chart is needed and it acts as a very important role on network connection.
After all setup on struct sockaddr_in
is done, declare bind
function. As flow chart, bind
function must be declared on both server and client.
bind function
server_socket & client_socket |
Put socket description retrieved on the top |
address |
Put struct sockaddr_in into it as domain is AF_INET . If your domain is AF_UNIX , try and put struct sockaddr_un here. |
address_len |
Put the length of the address |
Server and client will start interacting with each other after the bind
function and it is the most important session. From what flow chart shows, listen
, accept
, connect
, three functions play very important roles.
Imagine that server looks like an ATM, and only one person can be used the ATM. So, what happens if there are 2 or more people that come at one time? The answer is simple, lining up and wait for the front people to finish using with ATM. It is exactly the same as what is happening in the server.
Listen
function acts as a waiting room, asking the traffic wait on the waiting room. Accept
function acts as the person who is asking the traffic waiting inside the waiting room to be ready for the meeting between server. Last, connect
function acts as the person who wants to carry out some work with the server.
listen function
server_socket |
Put socket description retrieved on the top |
backlog |
Define the maximum of awaiting request |
accept function
server_socket |
Put socket description retrieved on the top |
client_address |
Put null here if there is no special request to specify address. |
address_len |
Put null here if second parameter is null |
return |
Return information of client socket description. Use it for interaction between client and server. |
connect function
client_socket |
Put socket description retrieved on the top |
address |
Put the struct sockaddr defined on the top |
address_len |
Put the length of the address |
Finally, after the request is accepted, what should server and client do is send and read data. It is the most simple part in this entire article. read
function is used to read the buffer data and write
function is used to send the data. That’s all.
read function
socket_description |
Put server or client socket description depending on reading data from server or client |
read buffer | Content of the data retrieved |
read buffer length | Length of the output string |
write function
socket_description |
Put server or client socket description depending on sending data to server or client |
write buffer | Data to be sent |
write buffer length | Length of the output string |
Personal Comment
This article was published on 2013/5/1 and I was still new to networking programming at that time. Maybe there is some point that I am not making clear enough. I have tried my best to present all my knowledge to this article. Hope you can get the good basic beginning over here. Thank you!
Hi! Thank you everyone who reading my article. My major is electronic and programming. Right now I am doing foreign study at Japan. I will like sharing to everyone with my works and if you do interesting with my works, please leave you comment on my blog. Any comments is welcoming.
What is socket programming?
Socket programming is a way of connecting two nodes on a network to communicate with each other. One socket(node) listens on a particular port at an IP, while the other socket reaches out to the other to form a connection. The server forms the listener socket while the client reaches out to the server.
State diagram for server and client model
State diagram for server and client model of Socket
Stages for server
1. Socket creation:
int sockfd = socket(domain, type, protocol)
- sockfd: socket descriptor, an integer (like a file-handle)
- domain: integer, specifies communication domain. We use AF_ LOCAL as defined in the POSIX standard for communication between processes on the same host. For communicating between processes on different hosts connected by IPV4, we use AF_INET and AF_I NET 6 for processes connected by IPV6.
- type: communication type
SOCK_STREAM: TCP(reliable, connection oriented)
SOCK_DGRAM: UDP(unreliable, connectionless) - protocol: Protocol value for Internet Protocol(IP), which is 0. This is the same number which appears on protocol field in the IP header of a packet.(man protocols for more details)
2. Setsockopt:
This helps in manipulating options for the socket referred by the file descriptor sockfd. This is completely optional, but it helps in reuse of address and port. Prevents error such as: “address already in use”.
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
3. Bind:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
After the creation of the socket, the bind function binds the socket to the address and port number specified in addr(custom data structure). In the example code, we bind the server to the localhost, hence we use INADDR_ANY to specify the IP address.
4. Listen:
int listen(int sockfd, int backlog);
It puts the server socket in a passive mode, where it waits for the client to approach the server to make a connection. The backlog, defines the maximum length to which the queue of pending connections for sockfd may grow. If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED.
5. Accept:
int new_socket= accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket. At this point, the connection is established between client and server, and they are ready to transfer data.
Stages for Client
- Socket connection: Exactly same as that of server’s socket creation
- Connect: The connect() system call connects the socket referred to by the file descriptor sockfd to the address specified by addr. Server’s address and port is specified in addr.
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Implementation
Here we are exchanging one hello message between server and client to demonstrate the client/server model.
Server.c
C
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080
int
main(
int
argc,
char
const
* argv[])
{
int
server_fd, new_socket, valread;
struct
sockaddr_in address;
int
opt = 1;
int
addrlen =
sizeof
(address);
char
buffer[1024] = { 0 };
char
* hello =
"Hello from server"
;
if
((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror
(
"socket failed"
);
exit
(EXIT_FAILURE);
}
if
(setsockopt(server_fd, SOL_SOCKET,
SO_REUSEADDR | SO_REUSEPORT, &opt,
sizeof
(opt))) {
perror
(
"setsockopt"
);
exit
(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if
(bind(server_fd, (
struct
sockaddr*)&address,
sizeof
(address))
< 0) {
perror
(
"bind failed"
);
exit
(EXIT_FAILURE);
}
if
(listen(server_fd, 3) < 0) {
perror
(
"listen"
);
exit
(EXIT_FAILURE);
}
if
((new_socket
= accept(server_fd, (
struct
sockaddr*)&address,
(socklen_t*)&addrlen))
< 0) {
perror
(
"accept"
);
exit
(EXIT_FAILURE);
}
valread = read(new_socket, buffer, 1024);
printf
(
"%s\n"
, buffer);
send(new_socket, hello,
strlen
(hello), 0);
printf
(
"Hello message sent\n"
);
close(new_socket);
shutdown(server_fd, SHUT_RDWR);
return
0;
}
client.c
C
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8080
int
main(
int
argc,
char
const
* argv[])
{
int
status, valread, client_fd;
struct
sockaddr_in serv_addr;
char
* hello =
"Hello from client"
;
char
buffer[1024] = { 0 };
if
((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf
(
"\n Socket creation error \n"
);
return
-1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if
(inet_pton(AF_INET,
"127.0.0.1"
, &serv_addr.sin_addr)
<= 0) {
printf
(
"\nInvalid address/ Address not supported \n"
);
return
-1;
}
if
((status
= connect(client_fd, (
struct
sockaddr*)&serv_addr,
sizeof
(serv_addr)))
< 0) {
printf
(
"\nConnection Failed \n"
);
return
-1;
}
send(client_fd, hello,
strlen
(hello), 0);
printf
(
"Hello message sent\n"
);
valread = read(client_fd, buffer, 1024);
printf
(
"%s\n"
, buffer);
close(client_fd);
return
0;
}
Compiling:
gcc client.c -o client gcc server.c -o server
Output:
Client:Hello message sent Hello from server Server:Hello from client Hello message sent
Next: Socket Programming in C/C++: Handling multiple clients on server without multi threading
This article is contributed by Akshat Sinha. If you like GeeksforGeeks and would like to contribute, you can also write an article using write.geeksforgeeks.org or mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
Last Updated :
13 Jul, 2023
Like Article
Save Article
1.0 Client-server model
Client server model is a software architecture paradigm prevalent in distributed applications. A server has information resources and processes that provide answers to queries and other services to remote clients over the network. Some of the examples of these services are booking an airline ticket and/or a room in a hotel, sending a file to a client, sending a mail message to another person (client), etc. As host computers have a loopback network interface and processes on a host can also use network protocols for communication among themselves, a client can also reside on the same host as the server. In this post we will look at the Transmission Control Protocol (TCP) for full-duplex communication between a server and its clients.
2.0 Network addresses and ports
How does a client identify the server with which it wants to communicate? A client needs to know two things about the server – the Internet Protocol (IP) address of the host computer on which the server resides and the port number on that host for the server process. The IP address can be a traditional IPv4 32 bit address written, for example, as 203.0.113.10, where each number represents the contents of a byte. Or, the IP address can, alternately, be a 128-bit newer IPv6 address, expressed, for example, as 2001:db8::1:2:3:4, where colons are used as punctuation to improve readability and each character is a hexadecimal digit representing four bits of address. Leading zeros are omitted which means that, as an example, db8 is actually 0db8 and 1 is 0001. Missing digits between two adjacent colons are zeroes. In order to have unambiguous representation, there can be only one occurrence of adjacent colons in an IPv6 address.
An IP address identifies a host on the network. Next, the client needs to identify the server process on that host. More specifically, the client needs to know the port number for the server process. A process, wishing to communicate over the network is linked to a unique port number on the host on which it is running. A port number is a 16 bit integer. Port numbers less than 1024 are well known port numbers and identify popular services. For example, port numbers 25 and 587 are for SMTP, 80 is for HTTP, 443 is for HTTPS, port 993 is for IMAPS, etc. A list of reserved port numbers is available in the file, /etc/services.
3.0 Sockets
The client and server processes communicate over sockets. A socket is a network communication end point at a host. A socket is initialized with the socket system call. After initialization, it is built up as a network communication end point using calls like bind, listen, connect, accept, etc. Once a socket is fully built up as a network communication endpoint, we can use the socket id like a file descriptor in read and write system calls. Then, there are specialized calls like send, recv, sendto and recvfrom, that only work for sockets.
4.0 System calls and library functions
4.1 getaddrinfo, freeaddrinfo, gai_strerror
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> int getaddrinfo (const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); void freeaddrinfo (struct addrinfo *res); const char *gai_strerror (int errcode);
Where, the struct addrinfo is,
struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct addrinfo *ai_next; };
The getaddrinfo function translates a host name and a service into a struct addrinfo which contains an Internet address that can be passed as a parameter in bind and connect calls. The host name, node could be a domain name or an IP address. If node is NULL and AI_PASSIVE flag in ai_flags member of hints is set, the returned socket addresses would be suitable for use in the bind call for a socket that will accept connections from clients. The service is the name of a service from the /etc/services file or simply a port number passed as a string. The data element address family specifies the network address family. The values are AF_INET for IPv4, AF_INET6 for IPv6 and AF_UNSPEC for unspecified address family, which is the case when system may use either AF_INET or AF_INET6. Then, there are two types of sockets, stream sockets (SOCK_STREAM) and datagram sockets (SOCK_DGRAM). Stream sockets provide reliable, error-free, full duplex connected communication streams, guaranteeing delivery of packets in sequential chronological order. Datagram sockets provide connection-less communication where packets may arrive at the destination out of order. That is, a later packet might arrive before an earlier packet. Or, a packet might get lost in transit. However, if a packet arrives, it is guaranteed to be error-free.
The argument, hints in the getaddrinfo function is used for specifying the desired address family and the socket type. These are specified in the ai_family and ai_socktype members of hints. Also, if getaddrinfo is being called from a server, the AI_PASSIVE flag in ai_flags member of hints must be set so that the returned address can be used in an accept call. However, if the call is from a client, AI_PASSIVE flag should be clear which will result in socket addresses that will be suitable for use in connect, sendto and sendmsg calls.
getaddrinfo makes a linked list of struct addrinfo based on hints and returns a pointer to the start of the list in location pointed by res. The calling program can scan through this linked list and select an address.
getaddrinfo returns 0 on success. If the return value is non-zero, the error can be printed by using the gai_strerror function. The return value of getaddrinfo needs to be passed to the gai_strerror function and the latter prints the error string. The freeaddrinfo function frees the memory occupied by the linked list pointed by res.
4.2 socket
#include <sys/types.h> #include <sys/socket.h> int socket (int domain, int type, int protocol);
The socket system call creates a communication endpoint and returns a descriptor which can be used to refer to the socket in further calls. The first parameter, domain can be either AF_INET for IPv4 or AF_INET6 for IPv6 protocol family. There are other protocol families, but they are outside the scope of this tutorial. The second parameter, type indicates the socket type which could be SOCK_STREAM, for stream sockets which provide sequenced, reliable, full duplex, connection-based byte streams, or SOCK_DGRAM, for datagram sockets which provide connection-less unreliable delivery of datagrams. Stream sockets use the underlying Transmission Control Protocol (TCP), whereas the datagram sockets use the User Datagram Protocol (UDP). There are more socket types but outside the scope of this post. The last parameter, protocol specifies a particular protocol to be used with the socket. In most cases, there is one protocol for a given socket type and hence protocol may be specified as 0. However, in some cases, there might be multiple protocols and in that case a protocol has to be specified. However, as shown in the example at the end of this post, the values returned by the getaddrinfo function are passed to the socket system call.
The socket call returns the file descriptor for the socket on success. If the call fails, -1 is returned and errno is set accordingly.
4.3 getsockopt, setsockopt
#include <sys/types.h> #include <sys/socket.h> int getsockopt (int sockfd, int level, int optname, void *optval, socklen_t *optlen); int setsockopt (int sockfd, int level, int optname, const void *optval, socklen_t optlen);
getsockopt and setsockopt are for query and setting of options associated with a socket respectively. The socket is identified by the descriptor, sockfd. The level needs to be SOL_SOCKET for manipulating options at the socket API level. The next three parameters are for the option name, option value and the length of option value. The socket options can be listed with the man 7 socket
command.
4.4 bind
#include <sys/types.h> #include <sys/socket.h> int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
The bind system call assigns a network address to a socket. sockfd is the socket file descriptor, whereas the pointer addr and the length of the address are the values obtained from the getaddrinfo function. bind returns 0 on success and -1 on error.
4.5 listen
#include <sys/types.h> #include <sys/socket.h> int listen (int sockfd, int backlog);
listen marks a socket identified by the descriptor, sockfd as a passive socket. This means that it will accept connections using the accept system call. backlog is the maximum number of connection request that can be there in the queue. listen returns 0 on success. If there is an error, -1 is returned and errno is set accordingly.
4.6 accept
#include <sys/types.h> #include <sys/socket.h> int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen);
The accept system call takes the first connect request from the queue on a listening socket and creates a connection with the client that sent the request. accept takes in three parameters. The first, sockfd is the listening socket file descriptor. The second parameter, addr is pointer to a buffer for the client socket address. The client address is filled in by the accept call. The third parameter, addrlen is the length of the addr buffer, which is modified by accept to contain the length of data written at addr. accept returns a new socket file descriptor for further communication (send and recv calls) with the calling client. The original listening socket file descriptor is not affected and is used for accepting further connections from other clients.
If there are no connection requests and the socket has not been marked non-blocking, accept blocks till a connection request comes. If the socket is marked non-blocking and there are no connection requests, accept returns with error EAGAIN or EWOULDBLOCK.
Normally, we wish to write code that works for both IPv4 and IPv6. However, the socket address lengths are different for the two cases. So, we need to pass a buffer of adequate size in the accept call. It happens that there is struct sockaddr_storage which would do the job just fine.
// sockaddr.h ... typedef unsigned short int sa_family_t; /* This macro is used to declare the initial common members of the data types used for socket addresses, `struct sockaddr', `struct sockaddr_in', `struct sockaddr_un', etc. */ #define __SOCKADDR_COMMON(sa_prefix) \ sa_family_t sa_prefix##family #define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int)) /* Size of struct sockaddr_storage. */ #define _SS_SIZE 128 // socket.h ... /* Structure describing a generic socket address. */ struct sockaddr { __SOCKADDR_COMMON (sa_); /* Common data: address family and length. */ char sa_data[14]; /* Address data. */ }; /* Structure large enough to hold any socket address (with the historical exception of AF_UNIX). */ #define __ss_aligntype unsigned long int #define _SS_PADSIZE \ (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype)) struct sockaddr_storage { __SOCKADDR_COMMON (ss_); /* Address family, etc. */ char __ss_padding[_SS_PADSIZE]; __ss_aligntype __ss_align; /* Force desired alignment. */ }; // netinet/in.h ... /* Internet address. */ typedef uint32_t in_addr_t; struct in_addr { in_addr_t s_addr; }; ... /* Type to represent a port. */ typedef uint16_t in_port_t; ... /* IPv6 address */ struct in6_addr { union { uint8_t __u6_addr8[16]; #ifdef __USE_MISC uint16_t __u6_addr16[8]; uint32_t __u6_addr32[4]; #endif } __in6_u; #define s6_addr __in6_u.__u6_addr8 #ifdef __USE_MISC # define s6_addr16 __in6_u.__u6_addr16 # define s6_addr32 __in6_u.__u6_addr32 #endif }; ... /* Structure describing an Internet socket address. */ struct sockaddr_in { __SOCKADDR_COMMON (sin_); in_port_t sin_port; /* Port number. */ struct in_addr sin_addr; /* Internet address. */ /* Pad to size of `struct sockaddr'. */ unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof (in_port_t) - sizeof (struct in_addr)]; }; ... struct sockaddr_in6 { __SOCKADDR_COMMON (sin6_); in_port_t sin6_port; /* Transport layer port # */ uint32_t sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr; /* IPv6 address */ uint32_t sin6_scope_id; /* IPv6 scope-id */ }; #endif /* !__USE_KERNEL_IPV6_DEFS */
Instead of pointer to struct sockaddr, a pointer to struct sockaddr_storage is passed (after type casting to the former) in the accept call. The first member of struct sockaddr_storage, ss_family, is checked. If ss_family is AF_INET, the structure pointer is type cast to struct sockaddr_in pointer. Otherwise, if ss_family is AF_INET6, the structure pointer is type cast to struct sockaddr_in6 pointer. Then, further processing is done as per IPv4 or IPv6.
4.7 connect
#include <sys/types.h> #include <sys/socket.h> int connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
The connect system call is called by a client to connect the socket identified by sockfd to the server address specified by addr. The third parameter, addrlen specifies the length of addr. If connect is successful, it returns 0. In case of failure -1 is returned and errno is set accordingly.
4.8 send
#include <sys/types.h> #include <sys/socket.h> ssize_t send (int sockfd, const void *buf, size_t len, int flags);
The send call is used to send a message on a connected socket, identified by the file descriptor sockfd. The message is pointed by buf and len specifies the length of the message. The value of flags is bit-wise OR of zero or more flags, which are, MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_MORE, MSG_NOSIGNAL and MSG_OOB. MSG_CONFIRM asks the data link layer to report progress in sending the message. MSG_DONTROUTE flag indicates that message should only be sent to directly connected networks and should not be sent to a gateway. MSG_DONTWAIT is for the non-blocking sending of message. If the operation would involve blocking, an error is returned. MSG_EOR is for indicating end of record and is relevant for certain special cases. MSG_MORE indicates that there is more data to be sent. MSG_NOSIGNAL tells to not to generate SIGPIPE signal if the peer has closed the connection. However, the EPIPE error is still returned. Finally, MSG_OOB is for sending out of band data.
4.9 recv
#include <sys/types.h> #include <sys/socket.h> ssize_t recv (int sockfd, void *buf, size_t len, int flags);
The recv call is for receiving messages on a connected socket identified by the file descriptor sockfd. buf points to the buffer for storing the message and len is the length of the buffer. The value of flags is bit-wise OR of zero or more flags. The recv call, by default, blocks for a message. recv returns the number of bytes actually received on success. In case of error, -1 is returned and errno is set appropriately. The flags may well be 0 and not that rarely used flags are, MSG_DONTWAIT, MSG_OOB, MSG_PEEK and MSG_WAITALL. The flag, MSG_DONTWAIT, causes a non-blocking operation and if no message is available, recv returns -1 with errno set to EAGAIN or EWOULDBLOCK. The MSG_OOB flag is for receiving out of band data. With the MSG_PEEK flag, the data received is not removed from the queue and, hence, the same message would again be received in the next call. The MSG_WAITALL flag causes the call to block until the entire data requested is received. However, less data might still get returned because an error occurred or a signal got caught.
4.10 Byte order
#include <arpa/inet.h> uint32_t htonl (uint32_t hostlong); uint16_t htons (uint16_t hostshort); uint32_t ntohl (uint32_t netlong); uint16_t ntohs (uint16_t netshort);
Little-endian computers store the lower byte of numbers first, followed by the higher byte, whereas big-endian computers do just the opposite. So, if we just send integers from a little-endian to big-endian system, or vice-versa, there would be chaos. To prevent that, there is a network byte order, which is big-endian. All numbers should be transmitted on the network in the network byte order. The above functions convert long and short integers between host to network and network to host byte orders.
4.11 inet_ntop, inet_pton
#include <arpa/inet.h> const char *inet_ntop (int af, const void *src, char *dst, socklen_t size); int inet_pton (int af, const char *src, void *dst);
The function inet_ntop converts an IPv4 or IPv6 binary host address to a printable one like 203.0.113.10 or 2001:db8::1:2:3:4. The parameter, af is either AF_INET or AF_INET6. src is a pointer to either struct in_addr for IPv4 or struct in_addr6 for IPv6. dst points to a buffer, where the printable string is stored by the function and size is the size of that buffer. The size of buffer should be INET6_ADDRSTRLEN, so that both IPv4 and IPv6 address strings can be accommodated. inet_ntop returns dst parameter on success, or NULL on failure, with errno indicating the error.
Similarly, the function, inet_pton converts a printable IPv4 or Ipv6 address string to the corresponding binary format. af is either AF_INET or AF_INET6. src points to a printable IPv4 or IPv6 address string and dst is a pointer to struct in_addr or struct in6_addr, where the result is stored. inet_pton returns 1 on success, 0 if input is not a valid IP address and -1 on error, with errno indicating the error.
5.0 An Example: Quote server and client programs
We have an example client-server system programs. Our server is a quotation server. It has a collection of quotations. Clients connect to the server to get quotations, which they can print for end-user perusal.
5.1 The server program
/* * quote_server.c: gives a quotation to clients * after receiving a message * */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <errno.h> #include <pthread.h> #include <syslog.h> #include <unistd.h> #define QUOTE_SERVER_PORT "4356" #define BACKLOG 10 #define MAX_MESSAGE_SIZE 256 #define QUOTATION_PLEASE 1 #define YOUR_QUOTATION_PLEASE 2 #define QUIT 0 #define MAX_CLIENTS 200 typedef enum {false, true} bool; void error (char *msg); void *handle_client (void *arg); // struct for passing data to client handling thread struct client_info { bool is_a_client; pthread_t tid; int accepted_sock_id; struct sockaddr_storage client; }; struct client_info clients [MAX_CLIENTS]; struct message { long message_id; char buffer [MAX_MESSAGE_SIZE]; }; static char *quote [] = { "A meeting is an event at which minutes are kept and hours are lost.", "The best time to plant a tree was twenty years ago. The second best time is now.", "When all efforts fail, read the instructions.", "An expert is the one who predicts that the job will take the longest and cost the most.", "An expert is one who knows more and more about less and less until he knows absolutely everything about nothing.", "They said I was gullible and I believed them.", "Confidence is the feeling you have before you fully understand the situation.", "With word processing, any idea can be right justified, even those which can not be justified on any other ground.", "Ever since we had the stress management program, the production has been slipping and no one is bothered.", "Self deprecating humor can be disastrous if people miss the humor in it.", "To a quick question, give a slow answer." }; const int num_quotes = sizeof (quote) / sizeof (char *); pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER; int main (int argc, char **argv) { const char * const ident = "quote_server"; pthread_attr_t thread_attr; for (int i = 0; i < MAX_CLIENTS; i++) clients [i].is_a_client = false; openlog (ident, LOG_CONS | LOG_PID | LOG_PERROR, LOG_USER); syslog (LOG_USER | LOG_INFO, "%s", "Hello world!"); if (pthread_attr_init (&thread_attr) != 0) error ("pthread_attr_init"); if (pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED) != 0) error ("pthread_attr_setdetachstate"); time_t now; if ((now = time (NULL)) == ((time_t) -1)) error ("time"); unsigned int seed = now % 47; srand (seed); struct addrinfo hints; memset(&hints, 0, sizeof (struct addrinfo)); hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_STREAM; /* Stream socket */ hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ struct addrinfo *result; int s; if ((s = getaddrinfo (NULL, QUOTE_SERVER_PORT, &hints, &result)) != 0) { fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s)); exit (EXIT_FAILURE); } /* Scan through the list of address structures returned by getaddrinfo. Stop when the the socket and bind calls are successful. */ int sock_fd, optval = 1; socklen_t length; struct addrinfo *rptr; for (rptr = result; rptr != NULL; rptr = rptr -> ai_next) { sock_fd = socket (rptr -> ai_family, rptr -> ai_socktype, rptr -> ai_protocol); if (sock_fd == -1) continue; if (setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (int)) == -1) error("setsockopt"); if (bind (sock_fd, rptr -> ai_addr, rptr -> ai_addrlen) == 0) // Success break; if (close (sock_fd) == -1) error ("close"); } if (rptr == NULL) { // Not successful with any address fprintf(stderr, "Not able to bind\n"); exit (EXIT_FAILURE); } freeaddrinfo (result); // Mark socket for accepting incoming connections using accept if (listen (sock_fd, BACKLOG) == -1) error ("listen"); socklen_t addrlen; int i, r; while (1) { addrlen = sizeof (struct sockaddr_storage); // find an empty slot for client // lock mutex if ((r = pthread_mutex_lock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } for (i = 0; i < MAX_CLIENTS; i++) { if (!clients [i].is_a_client) clients [i].is_a_client = true; break; } // Unlock mutex if ((r = pthread_mutex_unlock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } if (i == MAX_CLIENTS) { fprintf (stderr, "Can't handle more than %d clients. Quitting\n", MAX_CLIENTS); exit (EXIT_FAILURE); } if ((clients [i].accepted_sock_id = accept (sock_fd, (struct sockaddr *) &clients [i].client, &addrlen)) == -1) { if (errno == EINTR) { // lock mutex if ((r = pthread_mutex_lock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } clients [i].is_a_client = false; // Unlock mutex if ((r = pthread_mutex_unlock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } continue; } else { error ("accept"); } } if ((r = pthread_create (&clients [i].tid, NULL, handle_client, (void *) &i)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } } syslog (LOG_USER | LOG_INFO, "%s", "Bye."); closelog (); exit (EXIT_SUCCESS); } void *handle_client (void *arg) { struct client_info this_client; char str [INET6_ADDRSTRLEN]; struct message send_message, recv_message; bool over = false; struct sockaddr_in *ptr; struct sockaddr_in6 *ptr1; int r, i; i = *((int *) arg); // lock mutex if ((r = pthread_mutex_lock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } this_client = clients [i]; // Unlock mutex if ((r = pthread_mutex_unlock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } // print IP address of the client if ((this_client.client) . ss_family == AF_INET) { ptr = (struct sockaddr_in *) &(this_client.client); inet_ntop (AF_INET, &(ptr -> sin_addr), str, sizeof (str)); } else if ((this_client.client) . ss_family == AF_INET6) { ptr1 = (struct sockaddr_in6 *) &(this_client.client); inet_ntop (AF_INET6, &(ptr1 -> sin6_addr), str, sizeof (str)); } else { ptr = NULL; fprintf (stderr, "Address family is neither AF_INET nor AF_INET6\n"); } if (ptr) syslog (LOG_USER | LOG_INFO, "%s %s", "Connection from client", str); // Handle client messages while (!over) { // receive message if (recv (this_client.accepted_sock_id, &recv_message, sizeof (struct message), 0) <= 0) { over = true; // lock mutex if ((r = pthread_mutex_lock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } clients [i].is_a_client = false; // Unlock mutex if ((r = pthread_mutex_unlock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } break; } // process switch (ntohl (recv_message.message_id)) { case QUOTATION_PLEASE: // choose a quote send_message.message_id = htonl (YOUR_QUOTATION_PLEASE); int i = rand () % num_quotes; strcpy (send_message.buffer, quote [i]); size_t msg_len = sizeof (long) + strlen (quote [i]) + 1; if (send (this_client.accepted_sock_id, &send_message, msg_len, 0) == -1) error ("send"); break; case QUIT: over = true; // lock mutex if ((r = pthread_mutex_lock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } clients [i].is_a_client = false; // Unlock mutex if ((r = pthread_mutex_unlock (&client_mutex)) != 0) { fprintf (stderr, "Error = %d (%s)\n", r, strerror (r)); exit (1); } continue; } } return (NULL); } void error (char *msg) { perror (msg); exit (1); }
5.2 The client program
/* * client.c : get a quotation from server * */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #define QUOTE_SERVER_PORT "4356" #define MAX_MESSAGE_SIZE 256 #define QUOTATION_PLEASE 1 #define YOUR_QUOTATION_PLEASE 2 #define QUIT 0 typedef enum {false, true} bool; int get_option (); void error (char *msg); struct message { long message_id; char buffer [MAX_MESSAGE_SIZE]; }; int main (int argc, char **argv) { if (argc != 2) { fprintf (stderr, "Usage: client hostname\n"); exit (EXIT_FAILURE); } struct addrinfo hints; memset(&hints, 0, sizeof (struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; struct addrinfo *result; int s; if ((s = getaddrinfo (argv [1], QUOTE_SERVER_PORT, &hints, &result)) != 0) { fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s)); exit (EXIT_FAILURE); } /* Scan through the list of address structures returned by getaddrinfo. Stop when the the socket and connect calls are successful. */ int sock_fd; socklen_t length; struct addrinfo *rptr; for (rptr = result; rptr != NULL; rptr = rptr -> ai_next) { sock_fd = socket (rptr -> ai_family, rptr -> ai_socktype, rptr -> ai_protocol); if (sock_fd == -1) continue; if (connect (sock_fd, rptr -> ai_addr, rptr -> ai_addrlen) == -1) { if (close (sock_fd) == -1) error ("close"); continue; } break; } if (rptr == NULL) { // Not successful with any address fprintf(stderr, "Not able to connect\n"); exit (EXIT_FAILURE); } freeaddrinfo (result); int option; struct message message; while (1) { option = get_option (); // send request to server message.message_id = htonl (option); if (send (sock_fd, &message, sizeof (long), MSG_NOSIGNAL) == -1) error ("send"); if (option == QUIT) break; // receive response from server if (recv (sock_fd, &message, sizeof (struct message), 0) == -1) error ("recv"); // display if (ntohl (message.message_id) == YOUR_QUOTATION_PLEASE) printf ("\n%s\n\n", message.buffer); } exit (EXIT_SUCCESS); } int get_option () { int option; while (1) { printf ("Get a Quote\n\n"); printf ("\tNew Quote\t1\n"); printf ("\tQuit\t\t0\n\n"); printf ("Your option: "); scanf ("%d", &option); if (option == 0 || option == 1) return option; else printf ("Illegal option, try again\n\n"); } } void error (char *msg) { perror (msg); exit (1); }
We can compile the server and client and run the server.
$ gcc quote_server.c -o quote_server -lpthread $ gcc client.c -o client $ ./quote_server & [1] 23244 $ quote_server[23244]: Hello world!
Once the server is running, we can run client from different terminals on the same system or from different systems connected on the network and get quotations.
6.0 See also
Before you start learning socket programming in C, you should basic knowledge of IP addresses, TCP, and UDP. In this article, I shall describe TCP/IP and write a socket program using the TCP/IP API.
TCP (Transmission control protocol)
A TCP (transmission control protocol) is a connection-oriented communication. It is an intermediate layer of the application layer and internet protocol layer in the OSI model. TCP is designed to send the data packets over the network. It ensures that data is delivered to the correct destination.
TCP creates a connection between the source and destination node before transmitting the data and keeps the connection alive until the communication is active.
In TCP before sending the data it breaks the large data into smaller packets and cares the integrity of the data at the time of reassembling at the destination node. Major Internet applications such as the World Wide Web, email, remote administration, and file transfer rely on TCP.
TCP also offers the facility of retransmission, when a TCP client sends data to the server, it requires an acknowledgment in return. If an acknowledgment is not received, after a certain amount of time transmitted data will be lost and TCP automatically retransmits the data.
The communication over the network in TCP/IP model takes place in form of a client-server architecture. ie, the client begins the communication and establishes a connection with a server.
For more understanding let’s create a server that continuously runs and establish the connection after getting a request from the client.
Note: here I am creating the server and client for Linux.
In this example, After the connection with a client, the server will wait for a message from the client. After getting the message server will check the received message and send a proper response as per the received message.
Sequence of socket API calls and data flow:
To better understand check out the sequence of socket API calls and data flow for TCP client-server communication. The left-hand column represents the client and the right-hand side is the server.
Steps to create a client using TCP/IP API
- Create a socket with the socket() system call.
- Initialize the socket address structure as per the server and connect the socket to the address of the server using the connect() system call.
- Receive and send the data using the recv() and send().
- Close the connection by calling the close() function.
Steps to create a server using TCP/IP API
- Create a socket with the socket() system call.
- Initialize the socket address structure and bind the socket to an address using the bind() system call.
- Listen for connections with the listen() system call.
- Accept a connection with the accept() system call. This call typically blocks until a client connects to the server.
- Receive and send data by using the recv() and send().
- Close the connection by using the close().
If you are a beginner and want to learn TCP/IP, then you can check this course “TCP/IP Networking for Developers” that created by Steve Evans, and the rating of this course is around 4.7. The good thing is that FREE TRIAL is available and you can also access thousands of courses that are created by industry experts.
Today Grab your free Trial
As we know in socket programming network nodes (sockets) are communicating with each other over the network. One socket(node) listens on a particular port at an IP, while the other socket reaches out to the other to form a connection. In this example code, we will create two-node, one node for the server and the other for the client. So let’s see the example code for client-server socket programming in C.
Example Source code for TCP/IP client in C Linux:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/socket.h> #include<arpa/inet.h> #include<unistd.h> //Create a Socket for server communication short SocketCreate(void) { short hSocket; printf("Create the socket\n"); hSocket = socket(AF_INET, SOCK_STREAM, 0); return hSocket; } //try to connect with server int SocketConnect(int hSocket) { int iRetval=-1; int ServerPort = 90190; struct sockaddr_in remote= {0}; remote.sin_addr.s_addr = inet_addr("127.0.0.1"); //Local Host remote.sin_family = AF_INET; remote.sin_port = htons(ServerPort); iRetval = connect(hSocket,(struct sockaddr *)&remote,sizeof(struct sockaddr_in)); return iRetval; } // Send the data to the server and set the timeout of 20 seconds int SocketSend(int hSocket,char* Rqst,short lenRqst) { int shortRetval = -1; struct timeval tv; tv.tv_sec = 20; /* 20 Secs Timeout */ tv.tv_usec = 0; if(setsockopt(hSocket,SOL_SOCKET,SO_SNDTIMEO,(char *)&tv,sizeof(tv)) < 0) { printf("Time Out\n"); return -1; } shortRetval = send(hSocket, Rqst, lenRqst, 0); return shortRetval; } //receive the data from the server int SocketReceive(int hSocket,char* Rsp,short RvcSize) { int shortRetval = -1; struct timeval tv; tv.tv_sec = 20; /* 20 Secs Timeout */ tv.tv_usec = 0; if(setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO,(char *)&tv,sizeof(tv)) < 0) { printf("Time Out\n"); return -1; } shortRetval = recv(hSocket, Rsp, RvcSize, 0); printf("Response %s\n",Rsp); return shortRetval; } //main driver program int main(int argc, char *argv[]) { int hSocket, read_size; struct sockaddr_in server; char SendToServer[100] = {0}; char server_reply[200] = {0}; //Create socket hSocket = SocketCreate(); if(hSocket == -1) { printf("Could not create socket\n"); return 1; } printf("Socket is created\n"); //Connect to remote server if (SocketConnect(hSocket) < 0) { perror("connect failed.\n"); return 1; } printf("Sucessfully conected with server\n"); printf("Enter the Message: "); gets(SendToServer); //Send data to the server SocketSend(hSocket, SendToServer, strlen(SendToServer)); //Received the data from the server read_size = SocketReceive(hSocket, server_reply, 200); printf("Server Response : %s\n\n",server_reply); close(hSocket); shutdown(hSocket,0); shutdown(hSocket,1); shutdown(hSocket,2); return 0; }
Example Source code for TCP/IP server in C Linux:
#include<stdio.h> #include<string.h> #include<sys/socket.h> #include<arpa/inet.h> #include<unistd.h> short SocketCreate(void) { short hSocket; printf("Create the socket\n"); hSocket = socket(AF_INET, SOCK_STREAM, 0); return hSocket; } int BindCreatedSocket(int hSocket) { int iRetval=-1; int ClientPort = 90190; struct sockaddr_in remote= {0}; /* Internet address family */ remote.sin_family = AF_INET; /* Any incoming interface */ remote.sin_addr.s_addr = htonl(INADDR_ANY); remote.sin_port = htons(ClientPort); /* Local port */ iRetval = bind(hSocket,(struct sockaddr *)&remote,sizeof(remote)); return iRetval; } int main(int argc, char *argv[]) { int socket_desc, sock, clientLen, read_size; struct sockaddr_in server, client; char client_message[200]= {0}; char message[100] = {0}; const char *pMessage = "hello aticleworld.com"; //Create socket socket_desc = SocketCreate(); if (socket_desc == -1) { printf("Could not create socket"); return 1; } printf("Socket created\n"); //Bind if( BindCreatedSocket(socket_desc) < 0) { //print the error message perror("bind failed."); return 1; } printf("bind done\n"); //Listen listen(socket_desc, 3); //Accept and incoming connection while(1) { printf("Waiting for incoming connections...\n"); clientLen = sizeof(struct sockaddr_in); //accept connection from an incoming client sock = accept(socket_desc,(struct sockaddr *)&client,(socklen_t*)&clientLen); if (sock < 0) { perror("accept failed"); return 1; } printf("Connection accepted\n"); memset(client_message, '\0', sizeof client_message); memset(message, '\0', sizeof message); //Receive a reply from the client if( recv(sock, client_message, 200, 0) < 0) { printf("recv failed"); break; } printf("Client reply : %s\n",client_message); if(strcmp(pMessage,client_message)==0) { strcpy(message,"Hi there !"); } else { strcpy(message,"Invalid Message !"); } // Send some data if( send(sock, message, strlen(message), 0) < 0) { printf("Send failed"); return 1; } close(sock); sleep(1); } return 0; }
OutPut 1.
OutPut 2.
Recommended Post
- Best Mouse for programmers.
- HDLC Protocol in C.
- SSL programming in C.
- Socket programming in C.
- Parse XML response in C without using the library.
- Create Http Get and Post request in C.
- File handling in C.
- I2C Communication protocol.
- Embedded C Interview Questions.
- Pointers in C.
- CAN Protocol Interview Questions.
- Bit-wise interview Questions in C.
- Base64 encoding decoding online tool