Sockets for interprocess communication

Posted by amclean on Wed, 22 Dec 2021 06:32:19 +0100


The methods of interprocess communication described in the previous articles, such as pipeline, shared memory, message queue, etc., all depend on the shared resources of a computer system. These resources include file system space, shared physical memory, or message queues, but they can only be used on the same host. So how can we communicate between processes on different hosts?
The answer is to use sockets. Sockets are a form of communication that is easy to understand and can be thought of as Address+Port. Client/Server systems can be developed either locally on a single machine or across networks between different hosts, and sockets can fulfill both communication needs. Here is a simple example of socket usage.

1. Socket Connection

(1) Socket properties

There may be multiple services running at the same time on the server machine. Customers can specify a specific service on a networked machine through an IP port. Within the system, ports are identified by assigning a unique 16-bit integer, while outside the system, ports are identified by a combination of IP address and port number. Sockets are the focus of communication and must bind a port.
The server waits for the client to connect at a specific port. Well-known services assign the same port number on all Linux and UNIX machines. They are usually less than 1024. The last one is the standard interface of the Web server. In general, ports less than 1024 are reserved for system services, and the processes being served must have superuser privileges.

(2) Socket type

A socket domain may have many different modes of communication, each of which has its own characteristics. But AF_ Sockets in UNIX domains do not have this problem; they provide a reliable two-way communication path.
Internet protocols provide two communication mechanisms: stream s and datagrams.
* Stream socket
Stream sockets provide an ordered, reliable, two-way connection to byte streams. It is implemented over TCP/IP.
* Datagram socket
By type SOCK_ The packet socket specified by DGRAM does not establish and maintain a connection. It provides an out-of-order, unreliable service. It is implemented through UDP/IP. The advantage is low cost and fast speed.

Example

The following provides a simple example where a server program can only serve one client at a time, reading a character from the client, increasing its value, and then writing it back.
(1) client1.c

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

int main()
{
    int sockfd;
    int len;
    struct sockaddr_un address;
    int result;
    char ch = 'A';

/*  Create a socket for the client.  */

    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

/*  Name the socket, as agreed with the server.  */

    address.sun_family = AF_UNIX;
    strcpy(address.sun_path, "server_socket");
    len = sizeof(address);

/*  Now connect our socket to the server's socket.  */

    result = connect(sockfd, (struct sockaddr *)&address, len);

    if(result == -1) {
        perror("oops: client1");
        exit(1);
    }

/*  We can now read/write via sockfd.  */

    write(sockfd, &ch, 1);
    read(sockfd, &ch, 1);
    printf("char from server = %c\n", ch);
    close(sockfd);
    exit(0);
}

(2) Server program server1.c

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

int main()
{
    int server_sockfd, client_sockfd;
    int server_len, client_len;
    struct sockaddr_un server_address;
    struct sockaddr_un client_address;

/*  Remove any old socket and create an unnamed socket for the server.  */

    unlink("server_socket");
    server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

/*  Name the socket.  */

    server_address.sun_family = AF_UNIX;
    strcpy(server_address.sun_path, "server_socket");
    server_len = sizeof(server_address);
    bind(server_sockfd, (struct sockaddr *)&server_address, server_len);

/*  Create a connection queue and wait for clients.  */

    listen(server_sockfd, 5);
    while(1) {
        char ch;

        printf("server waiting\n");

/*  Accept a connection.  */

        client_len = sizeof(client_address);
        client_sockfd = accept(server_sockfd, 
            (struct sockaddr *)&client_address, &client_len);

/*  We can now read/write to client on client_sockfd.  */

        read(client_sockfd, &ch, 1);
        ch++;
        write(client_sockfd, &ch, 1);
        close(client_sockfd);
    }
}

(2) Create sockets

The socket system call creates a socket and returns a descriptor that can be used to access the socket.
#include <sys/type.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
The socket created is an endpoint of a communication line. The domain parameter specifies the protocol family, the type parameter specifies the communication type of the socket, and the protocol parameter specifies the protocol used.
SOCK_STREAM is an ordered, reliable, and connection-oriented two-way byte stream. For AF_ For INET domain sockets, this feature is provided by default through a TCP connection established between two stream socket endpoints. Data can be passed in both directions through a socket. The TCP protocol provides a mechanism for fragmenting and reorganizing long messages and for retransmitting data that may be lost on the network.
The protocol parameter generally does not require selection, is set to 0, and uses the default protocol.
A socket system call returns a descriptor, which in many ways resembles the underlying file descriptor. When this socket is connected to a socket on the other end, we can use read and write system calls to send and receive data on the socket through this descriptor. close system i steals to end a socket connection. (looks like a pipeline, like a message queue)

(3) Socket address

Each socket field has its own address format. For AF_ For UNIX domain sockets, its address is determined by the structure sockaddr_un, which is defined in the header file sys/un.h species
struct sockaddr_un{
sa_family_t sun_family;
char sun_path[];
};

AF_ In the INET domain, the socket address is sockaddr_in
struct sockaddr_in{
short int sin_family;
unsigned short int sin_port;
struct in_addr;
};

(4) Named sockets

In order for a socket created by a socket call to be used by other processes, the server program must name the socket. Thus, AF_UNIX sockets are associated with the path name of a file system, AF_ The INET socket is associated with an IP port number.
#include <sys/socket.h>

int bind(int socket, const struct sockaddr *address, size_t address_len);
The bind system call assigns the address in the parameter address to the unnamed socket associated with the file descriptor socket.

(5) Create socket queues

#include <sys/socket.h>

int listen(int socket, int backlog);
The linux system may limit the maximum number of unprocessed connections that can be held in the queue. The queue length is the value of the backlog parameter. If the number of incoming connections waiting to be processed exceeds this length, subsequent connections will be rejected. Causes the client's connection request to fail.

(6) Accept connections

#include <sys/socket.h>

int accept(int socket, struct sockaddr *address, size_t *address_len);
This interface sets the server to receive request status

(7) Request connection

#include <sys/socket.h>

int connect(int socket, const struct sockaddr *address, size_t address_len);
Clients can connect to the server through this interface.

(8) Close the socket

The close function terminates socket connections on the server and the client.

2. Socket communication

After describing the basic system calls related to sockets, we should try to use network sockets instead of file system sockets. The disadvantage of file system sockets is that unless an absolute path is used, sockets will be created in the current directory of the server program.
To be clear, a loop network consists of only one computer. Traditionally, it is called a localhost, and it has a standard IP address of 127.0. 0.1. This is the local host
Also provide an example of network socket communication:

Network Client:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int sockfd;
    int len;
    struct sockaddr_in address;
    int result;
    char ch = 'A';

/*  Create a socket for the client.  */

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

/*  Name the socket, as agreed with the server.  */

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("127.0.0.1");
    address.sin_port = 9734;
    len = sizeof(address);

/*  Now connect our socket to the server's socket.  */

    result = connect(sockfd, (struct sockaddr *)&address, len);

    if(result == -1) {
        perror("oops: client2");
        exit(1);
    }

/*  We can now read/write via sockfd.  */

    write(sockfd, &ch, 1);
    read(sockfd, &ch, 1);
    printf("char from server = %c\n", ch);
    close(sockfd);
    exit(0);
}

Client is used in header file netinet/in. Sockaddr_defined in H The in structure specifies an AF_INET address. It tried to connect to IP address 127.0. Server on 0.1 host. It uses inet_ The addr function converts the text representation of an IP address to a format that meets the socket address requirements.

Network Server:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int server_sockfd, client_sockfd;
    int server_len, client_len;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;

/*  Create an unnamed socket for the server.  */

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

/*  Name the socket.  */

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
    server_address.sin_port = 9734;
    server_len = sizeof(server_address);
    bind(server_sockfd, (struct sockaddr *)&server_address, server_len);

/*  Create a connection queue and wait for clients.  */

    listen(server_sockfd, 5);
    while(1) {
        char ch;

        printf("server waiting\n");

/*  Accept a connection.  */

        client_len = sizeof(client_address);
        client_sockfd = accept(server_sockfd, 
            (struct sockaddr *)&client_address, &client_len);

/*  We can now read/write to client on client_sockfd.  */

        read(client_sockfd, &ch, 1);
        ch++;
        write(client_sockfd, &ch, 1);
        close(client_sockfd);
    }
}

Summary: The way clients and servers use socket communication is just like the process of service provided by customer service. The general customer service adjusts to the state of accepting calls from customers. After receiving the calls from customers, the service customer service assigns a suitable service customer service to them. The service customer service then processes the requests of customers accordingly, and notifies the customers of the results of the processing.

Topics: Unix network server