[CNet-01] Basic C socket programming
Welcome!
This article unfortunately requires you to have at least basic C programming knowledge. If you wanna see the result without touching any of this technical, then I recommend you to directly jump into CNet-03 article
Disclaimer: This article refers to https://www.geeksforgeeks.org/tcp-server-client-implementation-in-c/ with slightly modification and understanding
Introduction to socket programming
The word “socket” means by the tool or way to enable 2 or more process to be connected each other. The word “connected” here means by communication over TCP stack. We’ll cover about socket programming in UDP later on (not in this series).
It means, this allows you to build distributed app that can be accessed and used from internet.
And now, let’s talk about TCP behaviour

Refers to image [1], you need to implement at least those functions in respective client and server in order to be functional.
As addition, from “Send/Recv” it’s possible to exit the socket and re-listen again to receive another connections from client. We’ll see that on the source code. At this time, we only code the server side only. For client, we will be using netcat or telnet. Let’s inspect this code!
Refer to: https://github.com/habibiefaried/sysprousingc/blob/master/1.srv.c
1. Socket creation
// socket create and verification
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
printf("socket creation failed...\n");
exit(0);
}
else
printf("Socket successfully created..\n");Refers to the manual of function socket: http://man7.org/linux/man-pages/man2/socket.2.html. Parameters explanation:
- domain, we chose AF_INET for IPv4 Internet protocols
- type, we chose SOCK_STREAM that means TCP, for UDP is SOCK_DGRAM
- protocol, just set it to 0
2. Binding the port
// assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);// Binding newly created socket to given IP and verification
if ((bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) != 0) {
printf("socket bind failed...\n");
exit(0);
}
else
printf("Socket successfully binded..\n");
Refers to the manual of function socket: http://man7.org/linux/man-pages/man2/bind.2.html. Parameters explanation:
- sockfd, refers to socket file descriptor that created above
- struct sockaddr, refers to the type struct. you can see this above the bind function. It contains 3 sub parameters, pay attention on INADDR_ANY and PORT. It means that, your port will be listened on port number specified in PORT variable to all interface (0.0.0.0/0).
- addrlen, we put the length of the struct
3. Listening to incoming tcp connections
printf("Listen failed...\n");
exit(0);
}
else
printf("Server listening..\n");
len = sizeof(cli);Refers to the manual of function socket: http://man7.org/linux/man-pages/man2/listen.2.html. Parameters explanation:
- sockfd, refers to socket file descriptor that created above
- backlog, how many pending connections that able to be queued up in the process. Some process just get overwhelmed by so many connections that happens in same time. This queue also takes resources. So, put the number wisely.
4. Accepting the incoming packet
connfd = accept(sockfd, (struct sockaddr *)&cli, &len);
if (connfd < 0) {
printf("server acccept failed...\n");
exit(0);
}
else
printf("server acccept the client...\n");// Function for chatting between client and server
func(connfd);
Refers to the manual of function socket: http://man7.org/linux/man-pages/man2/accept.2.html. Parameters explanation:
- sockfd, refers to socket file descriptor that created above
- struct sockaddr, refers to the type struct, but it’s different with the one on “binding port”. Should be empty variable
- *addrlen, put the address of the length of the struct (type int variable)
It returns the connfd, this variable will hold the file descriptor that we can use to communicate with them.
It’s really important and we will be using for communicating with client, until the destruction of the socket
5. Receive the data from client
read(sockfd, buff, sizeof(buff));
printf("From client: %s\t To client : ", buff);Refers to the manual of function socket: http://man7.org/linux/man-pages/man2/read.2.html. Parameters explanation:
- fd, the file descriptor that taken from function accept before.
- buff, reads up and store the N bytes from file descriptor fd. “N” is taken from the third parameter
- count, integer type variable
After that, I tried to print whatever the client sent me.
5. Send data to client
n = 0;
// copy server message in the buffer
while ((buff[n++] = getchar()) != '\n');// and send that buffer to client
write(sockfd, buff, sizeof(buff));
Refers to the manual of function socket: http://man7.org/linux/man-pages/man2/write.2.html. Parameters explanation:
- fd, the file descriptor that taken from function accept before.
- buff, reads up and write the N bytes from file descriptor fd. “N” is taken from the third parameter
- count, integer type variable
6. Close the connection
close(sockfd)Refers to the manual of function socket: http://man7.org/linux/man-pages/man2/close.2.html. Parameters explanation:
- sockfd, the file descriptor that created.
This closes the file descriptor that ready to be used again in the future.
Testing
Let’s compile it with command
gcc 1.srv.c && ./a.out
It’s working!
Concurrency Problem
As you can see on source code, there’s no way to receive the second connection until it finishes with the first one.

After I exited from client1, then the data from client2 arrives. I forgot to tell you that I put while(1) on block where accept function resides. This makes the app immortal (run as forever loop) and keep receiving connections, unless it’s killed intentionally by the user.
while (1) {
// Accept the data packet from client and verification
// Always listening to the port, but blocking mechanism
connfd = accept(sockfd, (struct sockaddr *)&cli, sizeof(cli));
if (connfd < 0) {
printf("server acccept failed...\n");
exit(0);
}
else
printf("server acccept the client...\n");
// Function for chatting between client and server
func(connfd);
}
Conclusion
So now, we are thinking about scaling the app, in order to make it usable by thousands or even millions of concurrent connection.
In order to do so, you need to know about another important thing called “forking”. Let’s jump to this article -> https://medium.com/@habibiefaried/cnet-02-forking-for-concurrent-access-db5962f14e2b