Socket usage example of Linux inter-process communication method

linux inter-process communication video analysis:


How to implement the Linux kernel and inter-process communication components to implement your own server communication protocol to
implement the network protocol stack by hand, please prepare the environment and write the code together

Socket is a communication mechanism, by virtue of this mechanism, the development of the client/server system can be carried out either on a local stand-alone computer or across the network.
The characteristics of the socket are determined by three attributes, they are: domain (domain), type (type), and protocol (protocol). The socket also uses the address as its name. The format of the address varies with the domain (also known as the protocol family). Each protocol family can use one or more address families to define the address format.

1. Socket domain

The domain specifies the network medium used in socket communication. The most common socket domain is AF_INET, which refers to the Internet network. Many Linux LANs use this network. Of course, the Internet itself uses it. The underlying protocol-Internet Protocol (IP) has only one address family, which uses a specific way to specify computers in the network, that is, IP addresses.

Inside the computer system, the port is represented by assigning a unique 16-bit integer. Outside the system, it needs to be determined by the combination of the IP address and the port number.

2. Socket type

Stream sockets (in some ways similar to domain standard input/output streams) provide an orderly, reliable, bidirectional byte stream connection.

Stream sockets are specified by the type SOCK_STREAM, and they are implemented through TCP/IP connections in the AF_INET domain. They are also common socket types in the AF_UNIX domain.

Packet socket

In contrast to stream sockets, the packet socket specified by the type SOCK_DGRAM does not establish and maintain a connection. It has a limit on the length of data packets that can be sent. The datagram is transmitted as a single network message, and it may be lost, duplicated, or arrive out of order.

The datagram socket is implemented in the AF_INET domain through a UDP/IP connection, and it provides an unreliable service that is unnecessary.

3. Socket protocol

As long as the underlying transport mechanism allows more than one protocol to provide the required socket type, we can choose a specific protocol for the socket.

Previous code

Server:

//s_unix.c 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/un.h>  
#define UNIX_DOMAIN "/tmp/UNIX.domain" 
int main(void) 
{
    
     
  socklen_t clt_addr_len; 
  int listen_fd; 
  int com_fd; 
  int ret; 
  int i; 
  static char recv_buf[1024];  
  int len; 
  struct sockaddr_un clt_addr; 
  struct sockaddr_un srv_addr; 
  listen_fd=socket(PF_UNIX,SOCK_STREAM,0); 
  if(listen_fd<0) 
  {
    
     
    perror("cannot create communication socket"); 
    return 1; 
  }  
  //set server addr_param 
  srv_addr.sun_family=AF_UNIX; 
  strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1); 
  unlink(UNIX_DOMAIN); 
  //bind sockfd & addr 
  ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr)); 
  if(ret==-1) 
  {
    
     
    perror("cannot bind server socket"); 
    close(listen_fd); 
    unlink(UNIX_DOMAIN); 
    return 1; 
  } 
  //listen sockfd  
  ret=listen(listen_fd,1); 
  if(ret==-1) 
  {
    
     
    perror("cannot listen the client connect request"); 
    close(listen_fd); 
    unlink(UNIX_DOMAIN); 
    return 1; 
  } 
  //have connect request use accept 
  len=sizeof(clt_addr); 
  com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len); 
  if(com_fd<0) 
  {
    
     
    perror("cannot accept client connect request"); 
    close(listen_fd); 
    unlink(UNIX_DOMAIN); 
    return 1; 
  } 
  //read and printf sent client info 
  printf("/n=====info=====/n"); 
  for(i=0;i<4;i++) 
  {
    
     
    memset(recv_buf,0,1024); 
    int num=read(com_fd,recv_buf,sizeof(recv_buf)); 
    printf("Message from client (%d)) :%s/n",num,recv_buf);  
  } 
  close(com_fd); 
  close(listen_fd); 
  unlink(UNIX_DOMAIN); 
  return 0; 
} 

Client:

//c_unix.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIX_DOMAIN "/tmp/UNIX.domain"
int main(void)
{
    
    
  int connect_fd;
  int ret;
  char snd_buf[1024];
  int i;
  static struct sockaddr_un srv_addr;
//creat unix socket
  connect_fd=socket(PF_UNIX,SOCK_STREAM,0);
  if(connect_fd<0)
  {
    
    
    perror("cannot create communication socket");
    return 1;
  }  
  srv_addr.sun_family=AF_UNIX;
  strcpy(srv_addr.sun_path,UNIX_DOMAIN);
//connect server
  ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
  if(ret==-1)
  {
    
    
    perror("cannot connect to the server");
    close(connect_fd);
    return 1;
  }
  memset(snd_buf,0,1024);
  strcpy(snd_buf,"message from client");
//send info server
  for(i=0;i<4;i++)
    write(connect_fd,snd_buf,sizeof(snd_buf));
  close(connect_fd);
  return 0;
}

The use of sockets can not only realize the communication between different hosts in the network, but also realize the communication between different processes of the same host, and the established communication is a two-way communication. Socket process communication and network communication use a unified socket, but the address structure is different from some parameters.

[Article benefits] C/C++ Linux server architect learning materials plus group 812855908 (data including C/C++, Linux, golang technology, Nginx, ZeroMQ, MySQL, Redis, fastdfs, MongoDB, ZK, streaming media, CDN, P2P, K8S, Docker, TCP/IP, coroutine, DPDK, ffmpeg, etc.)
Insert picture description here

One, create socket process

(1) Create a socket, the type is AF_LOCAL or AF_UNIX, which means it is used for process communication:

To create a socket, you need to use the socket system call, and its prototype is as follows:

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

Among them, the domain parameter specifies the protocol family. For local sockets, its value must be set to the AF_UNIX enumeration value; the type parameter specifies the socket type, and the protocol parameter specifies the specific protocol; the type parameter can be set to SOCK_STREAM (stream Socket) or SOCK_DGRAM (datagram socket), the protocol field should be set to 0; its return value is the generated socket descriptor.

For local sockets, the streaming socket (SOCK_STREAM) is a sequential and reliable two-way byte stream, which is equivalent to establishing a data channel between local processes; the datagram socket (SOCK_DGRAM) ) Is equivalent to simply sending a message. In the process of process communication, theoretically there may be situations in which information is lost, copied, or arrives out of order, but because it communicates locally and does not pass through the external network, the probability of these situations is very high. small.

2. Name the socket.

Both parties of the SOCK_STREAM type local socket communication need to have a local address, and the local address of the server side needs to be clearly specified. The method of specifying is to use a variable of struct sockaddr_un type.

struct sockaddr_un {
    
    
  sa_family_t   sun_family;   /* AF_UNIX */
  char  sun_path[UNIX_PATH_MAX];    /* 路径名 */
};

There is a very important thing, there are two ways of naming socket process communication. One is ordinary naming. The socket will create a socket file with the same name based on this naming. When the client connects, it connects to the socket server by reading the socket file. The disadvantage of this method is that the server must have write permission to the path of the socket file, the client must know the path of the socket file, and must have read permission to the path.

Another way of naming is the abstract namespace. In this way, there is no need to create a socket file. You only need to name a global name to allow clients to connect based on this name. The difference between the latter's implementation process and the former is that when the latter assigns the address structure member sun_path array, the first byte must be set to 0, that is, sun_path[0] = 0. The following code illustrates:

The first way:

//name the server socket 
	server_addr.sun_family = AF_UNIX;
	strcpy(server_addr.sun_path,"/tmp/UNIX.domain");
	server_len = sizeof(struct sockaddr_un);
	client_len = server_len;

The second way:


#define SERVER_NAME @socket_server 
  server_addr.sun_family = AF_UNIX; 
  strcpy(server_addr.sun_path, SERVER_NAME); 
  server_addr.sun_path[0]=0; 
  //server_len = sizeof(server_addr); 
  server_len = strlen(SERVER_NAME) + offsetof(struct sockaddr_un, sun_path);

Among them, the offsetof function is defined in the #include <stddef.h> header file. Because the first byte of the second method is set to 0, we can add a placeholder string before the named string SERVER_NAME, for example:

#define SERVER_NAME @socket_server  

The @ symbol in front of it represents a placeholder and is not counted as an actual name.

Tip: When the client connects to the server, the naming method must be the same as that of the server, that is, if the server is a normal naming method, the address of the client must also be a normal naming method; if the server is an abstract naming method, the address of the client must also be Abstract naming method.

Three, binding

Both parties of the SOCK_STREAM type local socket communication need to have a local address, and the local address of the server side needs to be clearly specified. The method of specifying is to use a variable of struct sockaddr_un type, assign the corresponding field, and then bind it to the created server socket Literally, binding uses the bind system call, and its prototype is as follows:

int bind(int socket, const struct sockaddr *address, size_t address_len);

Where socket represents the socket descriptor on the server side, address represents the local address to be bound, is a variable of type struct sockaddr_un, and address_len represents the byte length of the local address. The code to implement the server-side address specification function is as follows (assuming that the server has created a socket through the socket system call described above, and server_sockfd is its socket descriptor):

struct sockaddr_un server_address;
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "Server Socket");
bind(server_sockfd, (struct sockaddr*)&server_address, sizeof(server_address));

The local address of the client does not need to be explicitly specified, it only needs to be able to connect to the server. Therefore, the struct sockaddr_un variable of the client needs to be set according to the setting of the server. The code is as follows (assuming the client has passed the socket mentioned above The system call creates a socket, and client_sockfd is its socket descriptor):

struct sockaddr_un client_address;
client_address.sun_family = AF_UNIX;
strcpy(client_address.sun_path, "Server Socket");

Four, monitor

After the server-side socket is created and assigned the local address value (name, in this case, Server Socket), it needs to be monitored, wait for the client to connect and process the request, monitor using the listen system call, and accept the client connection using the accept system call , Their prototype is as follows:

int listen(int socket, int backlog);
int accept(int socket, struct sockaddr *address, size_t *address_len);

Where socket represents the socket descriptor on the server side; backlog represents the length of the queued connection queue (if multiple clients are connected at the same time, it needs to be queued); address represents the local address of the currently connected client, this parameter is an output parameter, yes The information about itself passed by the client; address_len represents the byte length of the local address of the currently connected client. This parameter is both an input parameter and an output parameter. The code for monitoring, receiving and processing is as follows:

#define MAX_CONNECTION_NUMBER 10
int server_client_length, server_client_sockfd;
struct sockaddr_un server_client_address;
listen(server_sockfd, MAX_CONNECTION_NUMBER);
while(1)
{
    
    
  // ...... (some process code)
  server_client_length = sizeof(server_client_address);
  server_client_sockfd = accept(server_sockfd, (struct sockaddr*)&server_client_address, &server_client_length);
  // ...... (some process code)
}

The reason for using the infinite loop here is that the server is an entity that continuously provides services. It needs to monitor, accept, and process connections continuously. In this example, each connection can only be processed serially, that is, after one connection is processed, In order to proceed with subsequent connection processing. If you want multiple connections to be processed concurrently, you need to create threads and hand each connection to the corresponding thread for concurrent processing.

After the client socket is created and assigned a local address value, it needs to connect to the server for communication, and let the server provide processing services for it. For streaming sockets of type SOCK_STREAM, a connection between the client and the server is required. To connect to use the connect system call, its prototype is

int connect(int socket, const struct sockaddr *address, size_t address_len);

Where socket is the socket descriptor of the client, address represents the local address of the current client, which is a variable of type struct sockaddr_un, and address_len represents the byte length of the local address. The code to realize the connection is as follows:

connect(client_sockfd, (struct sockaddr*)&client_address, sizeof(client_address));

Both the client and the server must interact with each other on data. This interaction is also the subject of our process communication. One process plays the role of the client, and the other process plays the role of the server. The two processes send and receive data to each other. This is the process communication based on the local socket. Send and receive data to use write and read system calls, their prototype is:

int read(int socket, char *buffer, size_t len);
int write(int socket, char *buffer, size_t len);

Where socket is the socket descriptor; len is the length of the data to be sent or received; for the read system call, buffer is the buffer used to store the received data, that is, the received data is stored in it and is an output parameter; For the write system call, the buffer is used to store the data that needs to be sent, that is, the data in the buffer is sent, which is an input parameter; the return value is the length of the data that has been sent or received. For example, if the client wants to send a "Hello" string to the server, the code is as follows:

char buffer[10] = "Hello";
write(client_sockfd, buffer, strlen(buffer));

After the interaction is completed, the connection needs to be disconnected to save resources. The close system call is used. The prototype is:

int close(int socket);

Not much to say, just use it directly, everyone will know it, ha ha!

Each system call described above has a return value of -1. When the call is unsuccessful, they will all return -1. This feature allows us to use if-else or exception handling statements to handle errors. Great convenience.

SOCK_DGRAM datagram-style local sockets are rarely used. Because the local connection time of streaming sockets can be ignored, so the efficiency is not improved, and sending and receiving all need to carry the local address of the other party, so rarely or almost Do not use.

Corresponding to the local socket is the network socket, which can be used to transmit data on the network, in other words, it can realize the process of communication between different machines. In the TCP/IP protocol, the first byte of the IP address is 127, which means local, so local socket communication can be realized by using a network socket with an IP address of 127.xxx.

The above is the whole content of this article about the socket usage example of Linux inter-process communication, I hope it will be helpful to everyone.

Guess you like

Origin blog.csdn.net/qq_40989769/article/details/112392736