Simple TCP Socket Client and Server Communication in C Under Linux

This post doesn’t provide details about how Linux socket works, its design etc. It mainly for providing source code of simple TCP socket client and server in C. I’m writing this because I found myself need simple TCP client and server for testing from time to time. 🙂
1. The TCP Server Code

You can refer to TCP Server code below (save it as tcpserver.c),

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/types.h> 

#include <sys/socket.h>

#include <netinet/in.h>

 

void nonblock(int sockfd)

{

    int opts;

    opts = fcntl(sockfd, F_GETFL);

    if(opts < 0)

    {

        fprintf(stderr, "fcntl(F_GETFL) failedn");

    }

    opts = (opts | O_NONBLOCK);

    if(fcntl(sockfd, F_SETFL, opts) < 0) 

    {

        fprintf(stderr, "fcntl(F_SETFL) failedn");

    }

}

 

int main(int argc, char *argv[])

{

     int BUFLEN = 2000;

     int sockfd, newsockfd, portno;

     socklen_t clilen;

     char buffer[BUFLEN];

     struct sockaddr_in serv_addr, cli_addr;

     int n, i;

     int one = 1;

 

     if (argc < 2) {

         fprintf(stderr,"please specify a port numbern");

         exit(1);

     }

     sockfd = socket(AF_INET, SOCK_STREAM, 0);

     if (sockfd < 0) {

        perror("ERROR create socket");

        exit(1);

     }

     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);    //allow reuse of port

     //bind to a local address

     bzero((char *) &serv_addr, sizeof(serv_addr));

     portno = atoi(argv[1]);

     serv_addr.sin_family = AF_INET;

     serv_addr.sin_addr.s_addr = INADDR_ANY;

     serv_addr.sin_port = htons(portno);

     if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {

        perror("ERROR on bind");

        exit(1);

     }

     //listen marks the socket as passive socket listening to incoming connections, 

     //it allows max 5 backlog connections: backlog connections are pending in queue

     //if pending connections are more than 5, later request may be ignored

     listen(sockfd,5);

     //accept incoming connections

     clilen = sizeof(cli_addr);

     newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);

     //nonblock(newsockfd);        //if we want to set the socket as nonblock, we can uncomment this

     if (newsockfd < 0) {

        perror("ERROR on accept");

        exit(1);

     }

     printf("connection acceptedn");

     for (i = 0; i < 5; ++i) {

         bzero(buffer,BUFLEN);

         n = read(newsockfd,buffer,BUFLEN);

         if (n < 0) {

            perror("ERROR read from socket");

         }

         printf("received: %s",buffer); 

         n = write(newsockfd, buffer, n);

         printf("sent: %s", buffer);

         if (n < 0) {

            perror("ERROR write to socket");

         }

     }

     close(newsockfd);

     close(sockfd);

     return 0; 

}

The code first create a socket of SOCK_STREAM type in AF_INET domain. SOCK_STREAM corresponds to TCP, and AF_INET refers to IPv4.

It calls setsockopt to make the socket resuable. For example, your server open up a socket at a port number, and then it exits. Without this line of code, sometimes you may not bind the socket to same port.

The program then binds the socket to a local IP address with a specific port number. listen(sockfd, 5) will set the socket as passive socket listening to incoming connections. 5 means the maximum number of backlog is 5. Backlog connections are pending connect request in queue. If pending request are more than 5, later request may be ignored.

The code then calls accept method to accept incoming connections. This is a block call, and it returns when a new connect request is received, or an error occurred.

In the for loop, the tcp server listens to incoming packets and echo the message back. Note that the nonblock(newsockfd) has been commented out. You can uncomment it to enable nonblock read and write, but you may want to modify the read and write part to make it work properly.

2. The TCP Client Code
The TCP client code is as below (save it as tcpclient.c),

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <fcntl.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h> 

 

void nonblock(int sockfd)

{

    int opts;

    opts = fcntl(sockfd, F_GETFL);

    if(opts < 0)

    {

        fprintf(stderr, "fcntl(F_GETFL) failedn");

    }

    opts = (opts | O_NONBLOCK);

    if(fcntl(sockfd, F_SETFL, opts) < 0) 

    {

        fprintf(stderr, "fcntl(F_SETFL) failedn");

    }

}

 

int main(int argc, char *argv[])

{

    int BUFLEN = 2000;

    int sockfd, portno, n;

    struct sockaddr_in serv_addr;

    struct hostent *server;

    int i;

 

    char buffer[BUFLEN];

    if (argc < 3) {

       fprintf(stderr,"usage: %s hostname_or_ip portn", argv[0]);

       exit(0);

    }

    portno = atoi(argv[2]);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd < 0) {

        perror("ERROR creating socket");

        exit(1);

    }

    //get the address info by either host name or IP address

    server = gethostbyname(argv[1]);

    if (server == NULL) {

        fprintf(stderr,"ERROR, no such hostn");

        exit(1);

    }

    bzero((char *) &serv_addr, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;

    bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);

    serv_addr.sin_port = htons(portno);

    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)  {

        perror("ERROR connecting");

        exit(1);

    }

    printf("connection establishedn");

    //nonblock(sockfd);    //uncomment this line if we want to make the socket non-block

    for (i = 0; i < 5; ++i) {

        printf("Please enter the message: ");

        bzero(buffer,BUFLEN);

        fgets(buffer,BUFLEN,stdin);

        n = write(sockfd,buffer,strlen(buffer));

        printf("sent: %s", buffer);

        if (n < 0) {

             perror("ERROR writing to socket");

        }

        bzero(buffer,BUFLEN);

        n = read(sockfd,buffer,BUFLEN);

        if (n < 0) {

             perror("ERROR reading from socket");

        }

        printf("received: %s",buffer);

    }

    close(sockfd);

    return 0;

}

The code creates a TCP socket. It then tries to connect to a server address with specified IP and port number. The connect is a block call, it returns when the connection is established, which means it receives the ACK from tcp server, which is third packet of TCP 3-way handshake (SYN, ACK+SYN, ACK).

In the loop the tcp client sends and receives messages. Also the nonblock code has been disabled.

3. Compile and Run
To compile the code, use the commands below,

gcc -o client tcpclient.c
gcc -o server tcpserver.c

A sample run could be,

./server 12345 (at one terminal)
./client localhost 12345 (at another terminal)

4 thoughts on “Simple TCP Socket Client and Server Communication in C Under Linux”

Leave a Reply

Your email address will not be published. Required fields are marked *