Skip to main content

Simple TCP/IP Client Server Example

Socket Programming in C

Socket programming is a method used in network communication that allows data to be sent and received between devices. It is a critical concept in computer networking and is widely used to develop client-server applications.

In this blog post, we will explore the fundamentals of socket programming in C, focusing on TCP (Transmission Control Protocol) for reliable, connection-oriented communication.


What is Socket Programming?

Socket programming enables communication between two nodes on a network. A server listens for incoming client requests, and the client connects to the server to facilitate data exchange.

Sockets provide a communication channel between two processes, either on the same machine or different machines connected via a network.


Types of Sockets

  1. Stream Sockets (SOCK_STREAM):

    • Uses TCP for communication.

    • Provides reliable, connection-oriented communication.

    • Data is transmitted in order and without loss.

  2. Datagram Sockets (SOCK_DGRAM):

    • Uses UDP (User Datagram Protocol).

    • Connectionless and unreliable, but faster.

    • Data may arrive out of order or get lost.

  3. Raw Sockets:

    • Allows direct access to lower-level protocols.

    • Used for packet-level manipulation.


How Socket Communication Works

  1. Server Setup:

    • Create a socket.

    • Bind the socket to an IP address and port.

    • Listen for incoming connections.

    • Accept client connections.

  2. Client Setup:

    • Create a socket.

    • Connect to the server.

  3. Data Exchange:

    • Send and receive data between the server and client.


Server Code

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    int sockfd, newsock;
    struct sockaddr_in server, client;
    socklen_t len = sizeof(client);
    char buffer[1024];

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(8080);

    bind(sockfd, (struct sockaddr *)&server, sizeof(server));
    listen(sockfd, 5);
    printf("Waiting for connection...\n");

    newsock = accept(sockfd, (struct sockaddr *)&client, &len);
    read(newsock, buffer, sizeof(buffer));
    printf("Message: %s\n", buffer);

    send(newsock, "Hello from server", 17, 0);
    close(newsock);
    close(sockfd);

    return 0;
}

Client Code

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

int main() {
    int sockfd;
    struct sockaddr_in server;
    char buffer[1024] = "Hello Server";

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    server.sin_family = AF_INET;
    server.sin_port = htons(8080);
    server.sin_addr.s_addr = inet_addr("127.0.0.1");

    connect(sockfd, (struct sockaddr *)&server, sizeof(server));
    send(sockfd, buffer, strlen(buffer), 0);
    read(sockfd, buffer, sizeof(buffer));
    printf("Response: %s\n", buffer);
    close(sockfd);

    return 0;
}

Output:
$ ./server
Waiting for connection...
Message: Hello Server

$ ./client
Response: Hello from server


How It Works

  1. Server:
  • Creates a socket with socket().
  • Binds to an IP/port with bind().
  • Waits for connections using listen().
  • Accepts incoming connections with accept().
  • Receives and sends data using recv() and send().
  1. Client:
  • Creates a socket with socket().
  • Connects to the server with connect().
  • Sends a message using send().
  • Receives a response from the server.

Testing the Program

  1. Compile the server and client:

    gcc server.c -o server     gcc client.c -o client
  1. Run the server:

    ./server
  1. In another terminal, run the client:

    ./client
  1. The server will receive the message and respond back to the client.

This is a simple TCP server program in C that accepts a client connection, receives a message, and sends a response. Let me break down the key parts of the code for you:

Breakdown of the Code: Server

  1. Headers:


    #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h>
    • These headers include functions for input/output, string manipulation, memory allocation, system calls (like read and write), and network communication.
  2. Socket Creation:


    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    • Creates a TCP socket.
    • AF_INET specifies IPv4, SOCK_STREAM indicates TCP (connection-oriented).
  3. Server Address Structure:


    server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(8080);
    • sin_family specifies the address family (IPv4).
    • INADDR_ANY allows the server to accept connections on any network interface.
    • htons(8080) converts the port number to network byte order (big-endian).
  4. Binding the Socket:


    bind(sockfd, (struct sockaddr *)&server, sizeof(server));
    • Associates the socket with the IP address and port.
  5. Listening for Connections:


    listen(sockfd, 5);
    • Puts the socket in passive mode to listen for incoming connection requests.
    • 5 specifies the backlog, the number of pending connections allowed.
  6. Accepting a Connection:


    newsock = accept(sockfd, (struct sockaddr *)&client, &len);
    • Accepts the first incoming client connection and creates a new socket (newsock) for communication.
  7. Receiving Data:


    read(newsock, buffer, sizeof(buffer)); printf("Message: %s\n", buffer);
    • Reads data from the client and prints the received message.
  8. Sending a Response:


    send(newsock, "Hello from server", 17, 0);
    • Sends a response back to the client.
  9. Closing Sockets:


    close(newsock); close(sockfd);
    • Closes both the connection socket (newsock) and the listening socket (sockfd).



Breakdown of the Client Code:

  1. Socket Creation:

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    • Creates a TCP socket for the client.
  2. Server Address Structure:


    server.sin_family = AF_INET; server.sin_port = htons(8080); server.sin_addr.s_addr = inet_addr("127.0.0.1");
    • The inet_addr("127.0.0.1") specifies the localhost (server running on the same machine).
    • Change the IP to the server's IP if running on different machines.
  3. Connecting to Server:


    connect(sockfd, (struct sockaddr *)&server, sizeof(server));
    • Connects to the server. The server must be running before this.
  4. Sending Data:


    send(sockfd, buffer, strlen(buffer), 0);
    • Sends a message to the server.
  5. Receiving Response:

    read(sockfd, buffer, sizeof(buffer));
    printf("Message from server: %s\n", buffer);
    • Reads the server’s response and displays it.
  6. Closing Socket:

    close(sockfd);

Notes:

  • Error Handling: This code lacks error handling. Consider adding checks after socket creation, binding, listening, and accepting to ensure smooth operation.
  • Port Selection: Ensure port 8080 is free or change to an available port if needed.
  • Concurrency: This implementation handles one client at a time. For multiple clients, consider using fork() or threads.

Comments

Popular posts from this blog

Server/Client Communication-python

The basic mechanisms of client-server setup are: A client app send a request to a server app.  The server app returns a reply.  Some of the basic data communications between client and server are: File transfer - sends name and gets a file.  Web page - sends url and gets a page.  Echo - sends a message and gets it back.  Client server communication uses socket.              To connect to another machine, we need a socket connection. What's a connection?  A relationship between two machines, where two pieces of software know about each other. Those two pieces of software know how to communicate with each other. In other words, they know how to send bits to each other. A socket connection means the two machines have information about each other, including network location (IP address) and TCP port. (If we can use anology, IP address is the phone number and the TCP port is the extension).  A so...

Banker's Algorithm

Banker's algorithm is a deadlock avoidance algorithm. It is named so because this algorithm is used in banking systems to determine whether a loan can be granted or not. Consider there are n account holders in a bank and the sum of the money in all of their accounts is S. Everytime a loan has to be granted by the bank, it subtracts the loan amount from the total money the bank has. Then it checks if that difference is greater than S. It is done because, only then, the bank would have enough money even if all the n account holders draw all their money at once. Banker's algorithm works in a similar way in computers. Whenever a new process is created, it must exactly specify the maximum instances of each resource type that it needs. Let us assume that there are n processes and m resource types. Some data structures are used to implement the banker's algorithm. They are: Available: It is an array of length m . It represents the number of available resourc...

Inter Process Communication-Message Queue

Interprocess communication (IPC) is a set of programming interfaces that allow a programmer to coordinate activities among different program processes that can run concurrently in an operating system. This allows a program to handle many user requests at the same time. Since even a single user request may result in multiple processes running in the operating system on the user's behalf, the processes need to communicate with each other. The IPC interfaces make this possible. Each IPC method has its own advantages and limitations so it is not unusual for a single program to use all of the IPC methods . Message Based Communication Messages are a very general form of communication. Messages can be used to send and receive formatted data streams between arbitrary processes. Messages may have types. This helps in message interpretation. The type may specify appropriate permissions for processes. Usually at the receiver end, messages are put in a queue. Messages may also be fo...