How to Open a Socket in C

Jinku Hu Feb 02, 2024
How to Open a Socket in C

This article will explain several methods of how to open a socket in C.

Use listen and accept Functions to Open a Socket in C

listen and accept are part of the UNIX networking facilities, sometimes called Sockets API, which implements the core functionality to deal with inter-process communications. Note that a socket is an abstract representation of the endpoint for the communication path. There are different types of sockets e.g., Unix Domain and Internet Domain, but all of them follow similar procedures for establishing the communication between two processes.

Keep in mind that networking is just communication between two running programs. At first, the socket function is called to create the endpoint and corresponding handle. Sockets are handled with file descriptors similar to the regular files or pipes on UNIX systems. After the socket call, it has two scenarios depending on the kind of program you are developing. Generally, we have a client-server model in network communication, where one program (server) needs to wait for others (clients) to connect with it.

In this example, we implement a server-side program that needs to listen to the specific endpoint and accept client connections. In this scenario, the bind function is called after socket to bind to the specific address where the program will accept the incoming connections. After bind, listen and accept functions are called to form a so-called passive socket. Note that accept blocks the process until the different process does not establish a connection using the connect function call.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define handle_error(msg) \
  do {                    \
    perror(msg);          \
    exit(EXIT_FAILURE);   \
  } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum { LISTEN_QUEUE = 100 };

int main(int argc, char *argv[]) {
  int listenfd, connfd;
  socklen_t len;
  struct sockaddr_un servaddr, cliaddr;

  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (listenfd == -1) handle_error("socket");

  memset(&servaddr, 0, sizeof(servaddr));
  servaddr.sun_family = AF_UNIX;
  strncpy(servaddr.sun_path, SOCKET_PATH, sizeof(servaddr.sun_path) - 1);

  if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    handle_error("bind");

  if (listen(listenfd, LISTEN_QUEUE) == -1) handle_error("listen");

  len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
  if (connfd == -1) handle_error("accept");

  unlink(SOCKET_PATH);
  exit(EXIT_SUCCESS);
}

Usually, the accept call is followed by the code that implements incoming connections handling. An external program that wants to establish a connection with this server needs to know the address and port. Assuming both of them are known to the process, it invokes the connect function after the socket call and hopefully gets connected to the server process. Once the connection is established, both processes can write and read from the socket descriptor as having a bidirectional channel. In real-world scenarios, multiple client processes are supposed to connect to the server simultaneously; the connection handling code should be implemented concurrently. Otherwise, the server won’t be able to service more than one client at a time.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define handle_error(msg) \
  do {                    \
    perror(msg);          \
    exit(EXIT_FAILURE);   \
  } while (0)

#define SOCKET_PATH "/tmp/my.sock"

enum { MAXLINE = 4096, LISTEN_QUEUE = 100 };

int main(int argc, char *argv[]) {
  int listenfd, connfd;
  socklen_t len;
  struct sockaddr_un servaddr, cliaddr;
  char buf[MAXLINE];

  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
  if (listenfd == -1) handle_error("socket");

  memset(&servaddr, 0, sizeof(servaddr));
  servaddr.sun_family = AF_UNIX;
  strncpy(servaddr.sun_path, SOCKET_PATH, sizeof(servaddr.sun_path) - 1);

  if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    handle_error("bind");

  if (listen(listenfd, LISTEN_QUEUE) == -1) handle_error("listen");

  len = sizeof(cliaddr);
  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
  if (connfd == -1) handle_error("accept");

  size_t ret = read(connfd, buf, MAXLINE);
  if (ret == -1) handle_error("read");

  printf("read %d bytes\nmessage: %s\n", connfd, buf);

  unlink(SOCKET_PATH);
  exit(EXIT_SUCCESS);
}
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Related Article - C Networking