Fundamentals 26 min read

Understanding POSIX: Concepts, Network APIs, and Semaphore Programming in C

This article explains the POSIX standards, their role in providing portable Unix-like system interfaces, demonstrates network client‑server programming with POSIX sockets, analyzes key socket functions and the TCP three‑way handshake, and introduces POSIX named and unnamed semaphores with example code for synchronization and producer‑consumer patterns.

Deepin Linux
Deepin Linux
Deepin Linux
Understanding POSIX: Concepts, Network APIs, and Semaphore Programming in C

POSIX (Portable Operating System Interface) is a family of IEEE/ISO standards that define a portable API for Unix‑like operating systems, allowing source‑level code to run on many different systems.

POSIX Concept

POSIX specifies the application programming interface (API) and related utilities such as command‑line shells. Programs written to the POSIX API can be easily ported to various Unix derivatives, including Linux.

The standard defines the system‑call interface that operating systems must expose to applications. Most Unix‑like OSes provide these interfaces through the C standard library.

POSIX : Portable Operating System Interface of UNIX.

It defines the OS interfaces that applications should use.

Applications using POSIX‑conformant APIs are portable across systems.

POSIX specifies the system‑call interface for applications.

The most common OS API in the Unix world is based on the POSIX standard and is usually delivered as a C library (glibc on Linux), which implements the POSIX system calls.

POSIX Network API

Common client‑server network programming APIs are shown below.

Server (server.cpp)

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf("Using:./server port\nExample:./server 5005\n\n");
        return -1;
    }
    int listenfd;
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        return -1;
    }
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(atoi(argv[1]));
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {
        perror("bind");
        close(listenfd);
        return -1;
    }
    if (listen(listenfd, 5) != 0) {
        perror("listen");
        close(listenfd);
        return -1;
    }
    int clientfd;
    int socklen = sizeof(struct sockaddr_in);
    struct sockaddr_in clientaddr;
    clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, (socklen_t *)&socklen);
    printf("client (%s) connect server success...\n", inet_ntoa(clientaddr.sin_addr));
    char buffer[1024];
    while (1) {
        int ret;
        memset(buffer, 0, sizeof(buffer));
        if ((ret = recv(clientfd, buffer, sizeof(buffer), 0)) <= 0) {
            printf("ret = %d , client disconnected!!!\n", ret);
            break;
        }
        printf("recv msg: %s\n", buffer);
        if ((ret = send(clientfd, buffer, strlen(buffer), 0)) <= 0) {
            perror("send");
            break;
        }
        printf("response client: %s success...\n", buffer);
    }
    close(listenfd);
    close(clientfd);
    return 0;
}

Client (client.cpp)

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("Using:./client ip port\nExample:./client 127.0.0.1 5005\n\n");
        return -1;
    }
    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        return -1;
    }
    struct hostent *h;
    if ((h = gethostbyname(argv[1])) == 0) {
        printf("gethostbyname failed.\n");
        close(sockfd);
        return -1;
    }
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(atoi(argv[2]));
    memcpy(&servaddr.sin_addr, h->h_addr, h->h_length);
    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) {
        perror("connect");
        close(sockfd);
        return -1;
    }
    char buffer[1024];
    for (int i = 0; i < 3; i++) {
        int ret;
        memset(buffer, 0, sizeof(buffer));
        sprintf(buffer, "This is message [%d]", i+1);
        if ((ret = send(sockfd, buffer, strlen(buffer), 0)) <= 0) {
            perror("send");
            break;
        }
        printf("Sent: %s\n", buffer);
        memset(buffer, 0, sizeof(buffer));
        if ((ret = recv(sockfd, buffer, sizeof(buffer), 0)) <= 0) {
            printf("ret = %d error\n", ret);
            break;
        }
        printf("Received from server: %s\n", buffer);
        sleep(1);
    }
    close(sockfd);
}

The program output demonstrates a successful three‑way handshake, data exchange, and graceful termination.

Key Socket Functions

socket()

int socket(int domain, int type, int protocol);

Creates a socket descriptor and a TCP control block (TCB) that holds the five‑tuple (source IP, source port, destination IP, destination port, protocol).

listen()

int listen(int listenfd, int backlog);

Marks the socket as passive and sets the length of the pending‑connection queue (backlog).

connect()

Initiates a connection from the client to the server, triggering the TCP three‑way handshake.

accept()

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

Retrieves a pending connection from the queue, allocates a new descriptor for the established connection, and returns it.

TCP Three‑Way Handshake

The client sends a SYN packet, the server replies with SYN‑ACK (after moving the request to the half‑open queue), and the client sends an ACK, completing the connection. The server then moves the connection to the fully‑established queue, where accept() can retrieve it.

Data Transfer Functions

send() copies data from user space to the kernel’s send buffer; the call returning does not guarantee that the data has been transmitted on the wire.

recv() copies data from the kernel’s receive buffer to user space.

Connection Termination (close)

Calling close() on a socket initiates the TCP four‑way termination handshake, moving the socket through FIN_WAIT1, FIN_WAIT2, TIME_WAIT, and finally CLOSED states.

POSIX Semaphores (Inter‑process Communication)

POSIX provides named and unnamed semaphores. Both are counters with an associated PCB wait queue.

Named Semaphore

Created with sem_open() , closed with sem_close() , and removed with sem_unlink() . Named semaphores can be shared between unrelated processes via a common name.

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

Unnamed Semaphore

Created with sem_init() and destroyed with sem_destroy() . Useful for threads sharing memory or for processes that share a memory segment.

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

Semaphore Operations

sem_wait() decrements the counter; if the result is negative, the calling thread blocks.

#include <semaphore.h>
int sem_wait(sem_t *sem);

sem_trywait() attempts a non‑blocking decrement, returning -1 with EAGAIN if the semaphore is zero.

sem_timedwait() waits with a timeout, returning -1 with ETIMEOUT on expiry.

sem_post() increments the counter and wakes a waiting thread if any.

#include <semaphore.h>
int sem_post(sem_t *sem);

Producer‑Consumer Example Using Unnamed Semaphores

The following C++ program implements a bounded buffer with a mutex semaphore, a producer semaphore, and a consumer semaphore.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <vector>
#include <pthread.h>
#include <semaphore.h>
#include <sys/syscall.h>
#include <iostream>

#define RESOURCECOUNT 4
#define PTHREADCOUNT 2

class ModelOfProdConsByPosix {
public:
    ModelOfProdConsByPosix() : _queue(RESOURCECOUNT) {
        _capacity = RESOURCECOUNT;
        sem_init(&_lock, 0, 1);
        sem_init(&_prod, 0, RESOURCECOUNT);
        sem_init(&_cons, 0, 0);
        _write_pos = _read_pos = 0;
    }
    ~ModelOfProdConsByPosix() {
        sem_destroy(&_lock);
        sem_destroy(&_prod);
        sem_destroy(&_cons);
    }
    void push(int &data) {
        sem_wait(&_prod);
        sem_wait(&_lock);
        _queue[_write_pos] = data;
        _write_pos = (_write_pos + 1) % _capacity;
        sem_post(&_lock);
        sem_post(&_cons);
    }
    void pop(int &data) {
        sem_wait(&_cons);
        sem_wait(&_lock);
        data = _queue[_read_pos];
        _read_pos = (_read_pos + 1) % _capacity;
        sem_post(&_lock);
        sem_post(&_prod);
    }
private:
    std::vector<int> _queue;
    int _capacity;
    sem_t _lock, _prod, _cons;
    int _write_pos, _read_pos;
};

void *ConsumerStart(void *arg) {
    ModelOfProdConsByPosix *cp = (ModelOfProdConsByPosix*)arg;
    int data = 0;
    while (1) {
        cp->pop(data);
        printf("Consumer thread %d got %d\n", (int)syscall(SYS_gettid), data);
    }
}

void *ProducerStart(void *arg) {
    ModelOfProdConsByPosix *cp = (ModelOfProdConsByPosix*)arg;
    int data = 0;
    while (1) {
        cp->push(data);
        printf("Producer thread %d produced %d\n", (int)syscall(SYS_gettid), data);
        ++data;
    }
}

int main() {
    ModelOfProdConsByPosix *cp = new ModelOfProdConsByPosix;
    pthread_t cons[PTHREADCOUNT], prod[PTHREADCOUNT];
    for (int i = 0; i < PTHREADCOUNT; ++i) {
        pthread_create(&cons[i], NULL, ConsumerStart, (void*)cp);
        pthread_create(&prod[i], NULL, ProducerStart, (void*)cp);
    }
    for (int i = 0; i < PTHREADCOUNT; ++i) {
        pthread_join(cons[i], NULL);
        pthread_join(prod[i], NULL);
    }
    return 0;
}

The program prints interleaved producer and consumer messages, demonstrating correct synchronization using POSIX semaphores.

ConcurrencyC++UnixNetwork ProgrammingPOSIXSemaphores
Deepin Linux
Written by

Deepin Linux

Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.