Learning C + + project - Fundamentals of computer network programming and learning multi threading and multi process foundation

Posted by tmann on Tue, 12 Oct 2021 05:57:58 +0200

Learn computer network programming

1, Ideas and learning methods

   this article learned from: C language technology network (www.freecplus.net), and gave some understanding and reproduction. If there is infringement, it will be deleted.
   now I have learned the basic knowledge of C + + and algorithms, and completed the basic computer network knowledge. At the same time, I have also briefly learned the operating system and data structure.
   learn network programming, follow the video station B UP main C language technology network, C/C + + network programming, from socket to epoll, and learn according to this website. The website is as follows: http://www.freecplus.net/44e059cca66042f0a1286eb188c51480.html , I just want to learn the record. If there is infringement, delete it immediately, There are no changes to the program inside. The part you understand will be marked below. It is said that network programming C/C + + is the most difficult technology to master. It requires to master the knowledge of signals, multi processes and multi threads. Come to learn today and understand it. This series of videos starts with the most basic socket, then multi process / multi-threaded network service program development, and I/O reuse (select, poll and epoll).
   in the process of learning the project, I carefully studied the source code inside. I didn't understand the basic knowledge, didn't system, and consulted bit by bit. The efficiency was quite slow, and I couldn't understand the whole architecture. Therefore, calm down and go through this course and study in practice.

2, Understanding the basic structure of network programming

2.1 basic concepts of network programming

Socket is a socket. Two programs running in a computer establish a channel through socket, and data is transmitted in the channel. Socket hides the complex TCP/IP protocol family. As long as the socket related functions are used well, the network communication can be completed.
Socket provides two communication mechanisms: stream and datagram. stream socket is based on TCP protocol. It is an orderly, reliable and bidirectional byte stream channel. The transmitted data will not be lost, repeated or disordered. datagram socket is based on UDP protocol and does not need to establish and maintain a connection. It may be lost or disordered.
  simple socket communication process,

client:
socket() -> connect() -> send()/recv() -> close()
1. Create streaming socket
2. Initiate a connection request to the server
3. send out/receive data 
4. close socket Connect and free resources

Server
socket() -> bind() -> listen() -> accept() -> recv()/send() -> close()
1. Create streaming socket
2. Specifies the of the communication ip Address and port
3. hold socket Set to listening mode
4. Accept client connections
5. accept/send data
6.close socket Connect and free resources 

   next, learn a program for the communication between the server and the customer. However, before the program is executed, it is necessary to ensure that the firewall of the server has opened the network access policy (the ECS also needs to log in to the cloud control platform to open the access policy)---- Therefore, you need to install a firewall, open the firewall and open the port.
  steps for installing and setting firewall:

/*Install firewall section*/
// 1. Enable administrator mode
su 
// 2. Install firewall
yum install firewall
// 3.
systemctl start firewalld
// 4. Turn on the firewall
systemctl enable firewalld
// 5. Set the firewall status. If you enter the document editing page, press and hold:, and then enter q to exit
systemctl status firewalld

/*Set the open port and view the status section*/
// 1. Check the working status of firewall
firewall-cmd --state
// 2. Open the 5000 tcp port
firewall-cmd --zone=public --add-port=5000/tcp --permanent
// 3. Reload, otherwise the port number will not be displayed
firewall-cmd --reload
// 4. Check the open port number
firewall-cmd --list-port

  the operation is as follows:


  after configuring the firewall, start running the program source code of the client and server. First, notice that creating a cpp file in linux is as follows

touch server.cpp
touch client.cpp

// Create makefile
touch makefile

  then view the ip address of your virtual machine:

ifconfig -a


Here 192.168.201.129 is my ip address.
  then open the server.cpp file. The code is as follows,

/*
 * Program name: server.cpp. This program is used to demonstrate the server of socket communication
 * Author: C language technology network (www.freecplus.net) date: 20190525
*/
#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;
  }
 
  // Step 1: create the socket of the server.
  int listenfd;
  if ( (listenfd = socket(AF_INET,SOCK_STREAM,0))==-1) { perror("socket"); return -1; }
 
  // Step 2: bind the address and port used by the server for communication to the socket.
  struct sockaddr_in servaddr;    // Data structure of server address information.
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;  // The protocol family can only be AF in socket programming_ INET. 
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);          // Any ip address.
  //servaddr.sin_addr.s_addr = inet_addr("192.168.190.134"); //  Specify the ip address.
  servaddr.sin_port = htons(atoi(argv[1]));  // Specify the communication port.
  if (bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
  { perror("bind"); close(listenfd); return -1; }
 
  // Step 3: set the socket to listening mode.
  if (listen(listenfd,5) != 0 ) { perror("listen"); close(listenfd); return -1; }
 
  // Step 4: accept the connection from the client.
  int  clientfd;                  // The socket of the client.
  int  socklen=sizeof(struct sockaddr_in); // struct sockaddr_ Size of in
  struct sockaddr_in clientaddr;  // Address information of the client.
  clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);
  printf("Client(%s)Connected.\n",inet_ntoa(clientaddr.sin_addr));
 
  // Step 5: communicate with the client and reply ok after receiving the message sent by the client.
  char buffer[1024];
  while (1)
  {
    int iret;
    memset(buffer,0,sizeof(buffer));
    if ( (iret=recv(clientfd,buffer,sizeof(buffer),0))<=0) // Receive the request message from the client.
    {
       printf("iret=%d\n",iret); break;  
    }
    printf("receive:%s\n",buffer);
 
    strcpy(buffer,"ok");
    if ( (iret=send(clientfd,buffer,strlen(buffer),0))<=0) // Send response results to the client.
    { perror("send"); break; }
    printf("send out:%s\n",buffer);
  }
 
  // Step 6: close the socket and release resources.
  close(listenfd); close(clientfd);
}

  then open the client.cpp file

/*
 * Program name: client.cpp. This program is used to demonstrate the client of socket
 * Author: C language technology network (www.freecplus.net) date: 20190525
*/
#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;
  }
 
  // Step 1: create the socket of the client.
  int sockfd;
  if ( (sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) { perror("socket"); return -1; }
 
  // Step 2: initiate a connection request to the server.
  struct hostent* h;
  if ( (h = gethostbyname(argv[1])) == 0 )   // Specify the ip address of the server.
  { 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])); // Specify the communication port of the server.
  memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);
  if (connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0)  // Initiate a connection request to the server.
  { perror("connect"); close(sockfd); return -1; }
 
  char buffer[1024];
 
  // Step 3: communicate with the server, send a message, wait for a reply, and then send the next message.
  for (int ii=0;ii<3;ii++)
  {
    int iret;
    memset(buffer,0,sizeof(buffer));
    sprintf(buffer,"This is the second%d Super girl, number%03d. ",ii+1,ii+1);
    if ( (iret=send(sockfd,buffer,strlen(buffer),0))<=0) // Send a request message to the server.
    { perror("send"); break; }
    printf("send out:%s\n",buffer);
 
    memset(buffer,0,sizeof(buffer));
    if ( (iret=recv(sockfd,buffer,sizeof(buffer),0))<=0) // Receive the response message from the server.
    {
       printf("iret=%d\n",iret); break;
    }
    printf("receive:%s\n",buffer);
  }
 
  // Step 4: close the socket and release resources.
  close(sockfd);
}

  then open the makefile file and enter

all:client server

client:client.cpp
      g++ -g -o client client.cpp

server:server.cpp
      g++ -g -o server server.cpp

  finally, it's time to run them. First, start two terminals on linux,

// 1. Select a terminal, compile it first, and use the make instruction
make

/*Terminal 1*/
// Detect and turn on the firewall
systemctl start firewalld
systemctl enable firewalld
firewall-cmd --state
firewall-cmd --zone=public --add-port=5000/tcp --permanent
firewall-cmd --reload
firewall-cmd --list-port

// Run the client program with tcp 5000 port number
./client 127.0.0.1 5000

/*Terminal 2*/
// Run the server program with tcp 5000 port number
./server 5000

  the results are as follows:

   when learning the program, there are basically fixed formats, and few need to be modified, so I read and understand it myself. Because many of them are library files, macro definitions or library functions, I remember the fixed format first, modify it slowly after getting familiar with it, and try to apply it. I learned what I mentioned in it according to the above, and its website is as follows:
http://www.freecplus.net/0047ac4059b14d52bcc1d4df6ae8bb83.html
   now I started my own analysis. In fact, at the beginning, I had questions about why any ip can communicate. Later, I read the source code,

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);          // Any ip address.
//servaddr.sin_addr.s_addr = inet_addr("192.168.190.134"); / / specify the ip address.

  then record the important points,

1. socket()The return value of the function is essentially a file descriptor and an integer.

2. Two important transmit and receive functions
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd Established for socket,buf Cache for sending and receiving, flags = 0
 The function returns the number of characters that have been sent. Returns when an error occurs-1,error message errno Marked.

3. For the server, there are two socket,One is for listening socket,Another is that the client connection is successful
 After, by accept Function to send and receive messages with the client socket. 

4. apply socket resources
int socket(int domain, int type, int protocol);
domain Protocol family, macro definition; type  Specify type, macro definition;  protocol Transmission protocol mode
 Return value: a value is returned if successful socket,Failure Return-1,Error reason exists in errno Yes.
The first parameter can only be filled in AF_INET,The second parameter can only be filled in SOCK_STREAM,The third parameter can only be filled with 0.

5. hold ip Address or domain name conversion to hostent The address expressed by the structure.
struct hostent *gethostbyname(const char *name);
name: Domain name or host name;    
Return value: returns a value if successful hostent Structure pointer, failure return NULL. 
gethostbyname For client only.

6. Initiate a connection request to the server.
int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);
Function Description: connect The function is used to convert parameters sockfd of socket Connect to parameter serv_addr Specified service
 End, parameter addrlen by sockaddr The length of the structure.
Return value: 0 for success and 0 for failure-1,Error reason exists in errno Yes.
connect Function is only used on the client side.

7. The server binds the address and port used for communication to socket Come on.
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
parameter sockfd,Need to bind socket. 
parameter addr,It stores the address and port used by the server for communication.
parameter addrlen express addr The size of the structure.
Return value: 0 for success and 0 for failure-1,Error reason exists in errno Yes.

8. listen Function to connect the active socket Become passive connected socket,Make this socket Others are acceptable
socket To become a server-side connection request socket. 
int listen(int sockfd, int backlog);
parameter sockfd Yes, it has been bind Yes socket. 
parameter backlog,This parameter involves some network details, which is troublesome. Fill in 5 or 10, usually no more than 30.
When called listen After that, the server socket You can call accept To accept the connection request from the client.
Return value: 0 for success and 0 for failure-1,Error reason exists in errno Yes.

9. The server accepts the connection of the client.
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
parameter sockfd Yes, it has been listen Yes socket. 
parameter addr It is used to store the address information of the client sockaddr Structure expression. If you do not need the address of the client, you can
 Fill in 0 with.
parameter addrlen For storage addr The length of the parameter, if addr Is 0, addrlen Also fill in 0.
accept Function waits for the connection of the client. If no client is connected, it will wait all the time. This method is called blocking
 Plug.

  for two structure definitions

struct sockaddr_in{
	short sin_family;/*Address family Generally speaking, AF_INET (address family) PF_INET (protocol family)*/
	unsigned short sin_port;/*Port number(Network data format must be adopted. Ordinary numbers can be converted into numbers in network data format with htons() function*/
	struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/
	unsigned char sin_zero[8];/*Same size as struct sockaddr It has no practical significance, just to align with the SOCKADDR structure in memory*/
};

/*This structure records host information, including host name, alias, address type, address length and address list.*/
struct hostent{
	char * h_name;/*Official name of address*/
	char ** h_aliases;/* Null byte - pointer to the prepared name of the address*/
	short h_addrtype;/*Address type; Usually AF_INET*/
	short h_length;
	char ** h_addr_list;
	#define h_addr h_addr_list[0];
};
char *h_name Represents the canonical name of the host. for example www.google.com The canonical name of is actually www.l.google.com 
char **h_aliases Represents the alias of the host. www.google.com namely google His own alias. Sometimes, some hosts may have several aliases, which are actually names for their own websites for easy user memory.
int h_addrtype Indicates the host ip What is the type of address ipv4(AF_INET),still ipv6(AF_INET6)
int h_length Indicates the host ip Length of address
int **h_addr_lisst Indicates the of the host ip Address. Note that this is stored in network byte order. Never use it directly printf belt%s There will be a problem if you use parameters to type this thing. So I really need to print this out IP You need to call inet_ntop(). 
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) : 
This function will be of type af Network address structure src,Converted to the string form of host sequence and stored in cnt In a string.
This function actually returns a pointer dst A pointer to the. If the function call is wrong, the return value is NULL. 

   note that for the meaning of the two parameters in the main function, you can refer to the following website: https://blog.csdn.net/sun1314_/article/details/71271641.
   after learning and understanding the source code, the structure can check it on Baidu. The code can be understood as the configuration content, and the user can modify this part,

server.cpp

// Step 5: communicate with the client and reply ok after receiving the message sent by the client.
char buffer[1024];
// Note that it is while, and it is received all the time
while (1)
{
  int iret;
  memset(buffer,0,sizeof(buffer));
  if ( (iret=recv(clientfd,buffer,sizeof(buffer),0))<=0) // Receive the request message from the client.
  {
  	 // This sentence will be executed after sending
     printf("iret=%d\n",iret); break;   
  }
  printf("receive:%s\n",buffer);

  strcpy(buffer,"ok"); // Change the transmitted character or data
  if ( (iret=send(clientfd,buffer,strlen(buffer),0))<=0) // Send response results to the client.
  { perror("send"); break; }
  printf("send out:%s\n",buffer);
}

// Step 6: close the socket and release resources.
close(listenfd); close(clientfd); 

----------------------------------------------------------------------
                              Dividing line
----------------------------------------------------------------------

client.cpp

char buffer[1024];

// Step 3: communicate with the server, send a message, wait for a reply, and then send the next message.
// Send complete
for (int ii=0;ii<3;ii++)
{
  int iret;
  memset(buffer,0,sizeof(buffer));
  // Write data to the buffer and send it. It can be modified
  sprintf(buffer,"This is the second%d Super girl, number%03d. ",ii+1,ii+1);
  if ( (iret=send(sockfd,buffer,strlen(buffer),0))<=0) // Send a request message to the server.
  { perror("send"); break; }
  printf("send out:%s\n",buffer);

  memset(buffer,0,sizeof(buffer));
  // Receive the data and store it in the buffer
  if ( (iret=recv(sockfd,buffer,sizeof(buffer),0))<=0) // Receive the response message from the server.
  {
     // Reception complete, end
     printf("iret=%d\n",iret); break;
  }
  printf("receive:%s\n",buffer);
}

// Step 4: close the socket and release resources.
close(sockfd);

  lecture records

use gdb You can debug programs:
yum install gdb // Installing gdb
gdb server // Debug server
(gdb) set args 5005 // Set parameters
(gdb) run // Run program
(gdb) n // Skip a line
(gdb) p sockfd // View a variable value
(gdb) q // sign out

Others are trying to practice

   through the above learning, I have a certain understanding of network programming. The above are basically fixed formats. Remember their communication rules and methods, modify them according to the template, and continue to learn later.

2.2 introduce network programming from the perspective of professional programmers

2.2.1 detailed explanation of socket() function

  once again, I only use it as a learning record and share my learning process. If it infringes, I will withdraw it immediately. The function is declared as follows

int socket(int domain, int type, int protocol);
Parameter Description:
1. domain: Protocol domain, also known as protocol family( family). Common protocol families are AF_INET,AF_INET6,
AF_LOCAL(Or AF_UNIX,Unix field Socket),AF_ROUTE Wait. The protocol family decides socket Address type, in
 The corresponding address must be used in communication, such as AF_INET Decided to use ipv4 Group of address (32-bit) and port number (16 bit)
Close AF_UNIX Decided to use an absolute pathname as the address.

2. type: appoint socket Type. Common socket Types have SOCK_STREAM,SOCK_DGRAM,SOCK_RAW,
SOCK_PACKET,SOCK_SEQPACKET Equal flow socket(SOCK_STREAM)Is a connection oriented socket,
For connection oriented TCP Service application. Datagram socket(SOCK_DGRAM)Is a connectionless socket,Corresponding to
 Connectionless UDP Service application.

3. protocol: Specify the protocol. Common protocols are IPPROTO_TCP,IPPROTO_UDP,IPPROTO_STCP,
IPPROTO_TIPC And so on TCP Transmission protocol UDP Transmission protocol STCP Transmission protocol TIPC Transport protocol.

4. Return value: a value is returned if successful socket,Failure Return-1,Error reason exists in errno Yes.

Said a lot of nonsense, the first parameter can only be filled in AF_INET,The second parameter can only be filled in SOCK_STREAM,The third parameter can only be filled in
0. Unless the system runs out of data, socket Functions generally do not return failures.

   the default open socket is 1024, which is determined by the system setting. Attention should be paid to thread stress testing. Use this statement to check

ulimit -a

  as follows

2.2.2 host byte order and network byte order

   byte order refers to the storage order of data with more than one byte in memory. A 32-bit integer consists of 4 bytes. There are two ways to store these four bytes in memory: one is to store the low order bytes at the starting address, which is called small end byte order; In addition, the high byte is stored in the starting address, which is called large end byte order. such as

Will 0 x12345678

Big endian byte order
 High address      Low address
 78  56  34  12

Small end byte order
 High address      Low address
 12  34  56  78

  note:

Network byte order:
The network byte order is TCP/IP A data representation format specified in, which is related to the specific CPU Independent of type, operating system, etc
 Ensure that data can be correctly interpreted when transmitted between different hosts. Network bytes are sorted in big end.

Host byte order:
The byte order of different machine hosts is different, and CPU Design related, the order of data is determined by CPU Determined, independent of the operating system.

For this reason, machines with different architectures cannot communicate with each other, so they need to be converted into a conventional byte order, that is, network bytes
 Preface. Even two processes on the same machine(For example, because C Language, another by JAVA to write)Communication should also consider words
 Section order problem(JAVA Adopt large byte order)

Conversion function between network byte order and host byte order:
// Complete the mutual conversion of 16 bit unsigned numbers
htons() // host to network short
ntohs() // network to host short

// Complete the conversion of 32-bit unsigned numbers to each other
htonl() // host to network long
ntohl() // network to host long

TCP The host address and port in the protocol are expressed as integers:
192.168.190.134
// Small end mode
11000000 10101000 10111110 10000110
3232284294 ----> Decimal number storage

// Big end mode --- network byte order mode
10000110 10111110 10101000 11000000
2260641984 ----> Decimal number storage
2.2.3 structure of network communication

   in network programming, the network protocol, IP address and port are stored in a structure as follows,

// The two struct bytes are the same, so they can cast types to each other
// The problem of such storage: it is troublesome to store with 14 bytes
struct sockaddr{
	unsigned short sa_family; // Address type, AF_xxx 2 bytes
	char sa_data[14];         // 14 byte port and address
};

struct sockaddr_in{
	short int sin_family;       //Address type - 2 bytes
	unsigned short int sin_port;//Port number - 2 bytes
	struct in_addr sin_addr;    // Address - 4 bytes
	unsigned char sin_zero[8];  // To keep the same length as struct sockaddr - 8 bytes
};
struct in_addr{
	unsigned long s_addr;       // address
};

  structure for processing and storing IP addresses

struct hostent{
	char * h_name;       // host name
	char ** h_aliases;   // A string array composed of all aliases of the host. Multiple domain names can be bound to the same IP
	int h_addrtype;      // The type of IP address of the host, such as IPv4(AD_INET) or IPv6
 	int h_length;        // The IP address length of the host, IPv4 address is 4, IPv6 address is 16
	char ** h_addr_list;  // IP address of the host, stored in network byte order
	#define h_addr h_addr_list[0];
};
// The gethostbyname function can obtain the IP network byte order address by using the domain name in string format
struct hostent *gethostbyname(const char * name);

   here, write like this, no matter to the domain name or IP address, it can be resolved

struct hostent* h;
if ( (h = gethostbyname(argv[1])) == 0 )   // Specify the ip address of the server.
{ printf("gethostbyname failed.\n"); close(sockfd); return -1; }
struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));

  there are also some functions corresponding to structure transformation

1. int inet_aton(const *cp, struct in_addr *inp);
Put a string IP The address is converted to a 32-bit network byte order IP Address. If the function succeeds, the return value of the function
 Non zero, if the input address is incorrect, zero will be returned. No error code exists when using this function errno So its value will flicker
 Slightly.
2. char *inet_ntoa(struct in_addr in);
Method for converting network byte order into string IP Address.
3. in_addr_t inet_addr(const char *cp);
Put string IP The address is converted to network byte order.
2.2.4 bind() function

The   bind function assigns a local protocol address to a socket. For internet protocol, the protocol address is a combination of 32-bit IPv4 address or 128 bit IPv6 address and 16 bit TCP or UDP port number. The server binds the address and port used for communication to the socket. The function is declared as follows:

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

1. parameter sockfd,Need to bind socket. 
2. parameter addr,It stores the address and port used by the server for communication. Note that the structure must be forcibly converted to sockaddr type
3. parameter addrlen express addr The size of the structure.
4. Return value: 0 for success and 0 for failure-1,Error reason exists in errno Yes.

If the bound address is wrong or the port is occupied, bind The function must report an error, otherwise it will not return an error.
2.2.5 listen(), connect() and accept() functions

  summary of several functions

1). The server is calling listen() Before, the client cannot initiate a connection request to the server.
2). Server call listen() Function, the socket Start listening for client connections.
3). Client call connect() Function initiates a connection request to the server.
4). stay TCP At the bottom layer, a communication channel is established after the client and the server shake hands. If there are multiple client requests, a communication channel will be formed at the server
 A queue of ready connections.
5). Server call accept() The function gets a prepared connection from the queue, and the function returns a new connection socket , 
new socket For communicating with clients, listen of socket It is only responsible for listening to the connection request of the client.

   the listen function changes an active connection socket into a passive connection socket, so that this socket can accept connection requests from other sockets, thus becoming a server socket. If the socket does not listen, there will be a connection error.

int listen(int sockfd, int backlog);
Return: 0-success, -1-fail

1. parameter sockfd Yes, it has been bind Yes socket. socket Function returns socket Is an active connection socket,
In server-side programming, programmers want this socket It can accept external connection requests, that is, passively waiting for the client to connect.
Because the system considers a socket It is actively connected, so it needs to tell the system in some way that the programmer calls
listen Function to do this.

2. parameter backlog,This parameter involves some network details, which is troublesome. Fill in 5 or 10, usually no more than 30.

3. When called listen After that, the server socket You can call accept To accept the connection request from the client.

4. Return value: 0 for success and 0 for failure-1,Error reason exists in errno Yes.

The   connect function initiates a connection request to the server, which is declared as follows

int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);

1. Function Description: connect The function is used to convert parameters sockfd of socket Connect to parameter serv_addr Specified server, refer to
 number addrlen by sockaddr The length of the structure.

2. Return value: 0 for success and 0 for failure-1,Error reason exists in errno Yes.

3. connect Function is only used on the client side.
If the address of the server is wrong, or the port is wrong, or the server is not started, connect It will fail.

  the accept function accepts the connection of the client for the server. For multiple clients, when establishing communication, accept is equivalent to receiving messages sent by clients from the queue. If the queue is empty, it will block waiting. Its statement is as follows

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

1. parameter sockfd Yes, it has been listen Yes socket. 

2. parameter addr It is used to store the address information of the client sockaddr Structure expression. If you do not need the address of the client, you can
 Fill in 0.

3. parameter addrlen For storage addr The length of the parameter, if addr Is 0, addrlen Also fill in 0.

4. accept Function waits for the connection of the client. If no client is connected, it will wait all the time. This method is called blocking.

5. accept After waiting for a connection to the client, create a new one socket,The return value of the function is this new socket,The server uses this new socket Send and receive messages with the client.

6. Return value: 0 for success and 0 for failure-1,Error reason exists in errno Yes.

7. accept In the process of waiting, if it is interrupted or for other reasons, the function returns-1,Indicates a failure. If it fails, you can restart accept. 
2.2.6 TCP triple handshake and connection queue

  the three handshakes are illustrated as follows,

Among them, ESTABLISHED stands for successful handshake. After listen ing, wait for connection and become syn_ After recv, data can be sent.

You can view the corresponding port status in this way
netstat -na|grep 5005

hold server.cpp Replace part with
// Step 3: set the socket to listening mode.
if (listen(listenfd,5) != 0 ) { perror("listen"); close(listenfd); return -1; }
sleep(1000);

// Step 4: accept the connection from the client.
while(1){
	clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,(socklen_t*)&socklen);
	printf("Client(%s)Connected.\n",inet_ntoa(clientaddr.sin_addr));
	sleep(10);
}

Then start the server first ./server 5005
 By observing that it has been listen state

Then start multiple clients, all using port 5005
./client 127.0.0.1 5005

give the result as follows

It can be seen that both the server and the client shake hands successfully. After listen ing and connect establish a handshake, wait for the accept data to be sent and received.

2.2.7 send() and recv() functions

   now introduce two functions, send data and receive data. The function declaration is as follows.
   recv function is used to receive the data sent from the opposite socket. Recv function is used to receive the data sent from the opposite socket. Both the client and the server use recv function to receive the data sent from the other end of the TCP connection. The declaration is as follows

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

1. sockfd For a connected socket. 

2. buf Is the memory address used to receive data, which can be C The address of a language basic data type variable can also be an array, structure, or string, as long as it is a piece of memory.

3. len The length of data to be received cannot exceed buf Otherwise, the memory overflows.

4. flags Fill 0, Other values are of little significance.

5. The function returns the number of characters that have been received. Returns when an error occurs-1,Not set on failure errno Value of.

If socket The opposite end of does not send data, recv The function will wait. If the opposite end sends data, the function returns the received characters
 Number. Returns when an error occurs-1. If socket If it is closed by the opposite end, the return value is 0 recv Error returned by function(<=0),Express pass
 The channel is no longer available.

   the send function is used to send data to the opposite end through the socket. Both the client and the server use the send function to send data to the other end of the TCP connection. The function declaration is as follows

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

1. sockfd For a connected socket. 

2. buf The memory address of the data to be sent, which can be C The address of the language basic data type variable can also be array, structure
 String, send whatever is in memory.

3. len The length of data to be sent is buf Length of valid data in.

4. flags Fill 0, Other values are of little significance.

5. The function returns the number of characters that have been sent. Returns when an error occurs-1,error message errno Marked.

Note that even if the network is disconnected, or socket Has been closed by the opposite end, send The function does not report an error immediately. It will take several seconds to report an error. If
send Error returned by function(<=0),Indicates that the communication link is no longer available.

   the send function is also blocked. If the buffer is filled and the recv at the receiving end is not satisfied, it will be blocked. Continue to send until the recv is sufficient.

3, TCP related knowledge

3.1 TCP packet subcontracting and sticking

  basic concepts

Subcontracting: the sender sends a string“ helloworld",The receiver received two strings“ hello"And“ world". 
Sticky packet: the sender sends two strings“ hello"+"world",The receiver received it at one time“ helloworld". 

however TCP Transmission can guarantee several points:
1. The order remains unchanged;
2. No other data will be inserted into the split package. To solve the subcontracting and sticking problems, define a protocol. The common methods are
 packet length  + Message content 0010 helloworld
 packet length  ascii Binary integer

   there is a demonstration about TCP packet subcontracting and packet sticking in the video. I run on my virtual machine according to its method, and there is no clutter. However, the UP Master said that I will continue to explain later and continue to understand later.

3.2 programming methods of professional programmers

3.2.1 basic unsealed socket program

   socket programming has many functions and many details. If each project starts programming from the socket function, the code will be very cumbersome. The solution is encapsulation (making wheels).

3.2.2 write() function and read() function

   recv() function may read incomplete messages and send() may write incomplete data. Therefore, two functions are written to understand these problems. The two functions are as follows:,

3.2.3 TcpWrite() and tcprad() functions

  in order to solve the problem of TCP packet subcontracting and sticking, TcpWrite and tcprad functions are used to solve the problem

3.2.4 package the socket server into CTcpServer class

  the source code is as follows,

/*
 * Program name: book248.cpp. This program is used to demonstrate encapsulating socket server with C + +
 * Author: C language technology network (www.freecplus.net) date: 20190525
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
class CTcpServer
{
public:
  int m_listenfd;   // socket used by the server to listen
  int m_clientfd;   // socket connected to the client
 
  CTcpServer();
 
  bool InitServer(int port);  // Initialize server
 
  bool Accept();  // Waiting for client connection
 
  // Send message to opposite end
  int  Send(const void *buf,const int buflen);
  // Receive message from opposite end
  int  Recv(void *buf,const int buflen);
 
 ~CTcpServer();
};
 
int main()
{
  CTcpServer TcpServer;
 
  if (TcpServer.InitServer(5005)==false)
  { printf("TcpServer.InitServer(5005) failed,exit...\n"); return -1; }
 
  if (TcpServer.Accept() == false) { printf("TcpServer.Accept() failed,exit...\n"); return -1; }
 
  printf("The client is connected.\n");
 
  char strbuffer[1024];
 
  while (1)
  {
    memset(strbuffer,0,sizeof(strbuffer));
    if (TcpServer.Recv(strbuffer,sizeof(strbuffer))<=0) break;
    printf("receive:%s\n",strbuffer);
 
    strcpy(strbuffer,"ok");
    if (TcpServer.Send(strbuffer,strlen(strbuffer))<=0) break;
    printf("send out:%s\n",strbuffer);
  }
 
  printf("Client disconnected.\n");
}
 
CTcpServer::CTcpServer()
{
  // Constructor initializes socket
  m_listenfd=m_clientfd=0;
}

CTcpServer::~CTcpServer()
{
  if (m_listenfd!=0) close(m_listenfd);  // The destructor closes the socket
  if (m_clientfd!=0) close(m_clientfd);  // The destructor closes the socket
}
 
// Initialize the socket of the server, and the port is the communication port
bool CTcpServer::InitServer(int port)
{
  m_listenfd = socket(AF_INET,SOCK_STREAM,0);  // Create a socket on the server
 
  // Bind the address and port used by the server for communication to the socket
  struct sockaddr_in servaddr;    // Data structure of server address information
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;  // The protocol family can only be AF_INET in socket programming
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  // Any ip address of this host
  servaddr.sin_port = htons(port);  // Binding communication port
  if (bind(m_listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
  { close(m_listenfd); m_listenfd=0; return false; }
 
  // Set socket to listening mode
  if (listen(m_listenfd,5) != 0 ) { close(m_listenfd); m_listenfd=0; return false; }
 
  return true;
}
 
bool CTcpServer::Accept()
{
  if ( (m_clientfd=accept(m_listenfd,0,0)) <= 0) return false;
 
  return true;
}
 
int CTcpServer::Send(const void *buf,const int buflen)
{
  return send(m_clientfd,buf,buflen,0);
}
 
int CTcpServer::Recv(void *buf,const int buflen)
{
  return recv(m_clientfd,buf,buflen,0);
}

3.2.5 encapsulate socket client into CTcpClient class

  the source code is as follows,

/*
 * Program name: book247.cpp. This program is used to demonstrate encapsulating socket client with C + +
 * Author: C language technology network (www.freecplus.net) date: 20190525
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
// TCP client class
class CTcpClient
{
public:
  int m_sockfd;
 
  CTcpClient();
 
  // Initiate a connection to the server, serverip - server ip, port communication port
  bool ConnectToServer(const char *serverip,const int port);
  // Send message to opposite end
  int  Send(const void *buf,const int buflen);
  // Receive message from opposite end
  int  Recv(void *buf,const int buflen);
 
 ~CTcpClient();
};
 
int main()
{
  CTcpClient TcpClient;
 
  // Initiate a connection request to the server
  if (TcpClient.ConnectToServer("127.0.0.1,5005) == false)
  { printf("TcpClient.ConnectToServer(\"127.0.0.1\",5005) failed,exit...\n"); return -1; }
 
  char strbuffer[1024];
 
  for (int ii=0;ii<5;ii++)
  {
    memset(strbuffer,0,sizeof(strbuffer));
    sprintf(strbuffer,"This is the second%d Super girl, number%03d. ",ii+1,ii+1);
    if (TcpClient.Send(strbuffer,strlen(strbuffer))<=0) break;
    printf("send out:%s\n",strbuffer);
   
    memset(strbuffer,0,sizeof(strbuffer));
    if (TcpClient.Recv(strbuffer,sizeof(strbuffer))<=0) break;
    printf("receive:%s\n",strbuffer);
  }
}
 
CTcpClient::CTcpClient()
{
  m_sockfd=0;  // Constructor initialization m_sockfd
}
 
CTcpClient::~CTcpClient()
{
  if (m_sockfd!=0) close(m_sockfd);  // Destructor close m_sockfd
}
 
// Initiate a connection to the server, serverip - server ip, port communication port
bool CTcpClient::ConnectToServer(const char *serverip,const int port)
{
  m_sockfd = socket(AF_INET,SOCK_STREAM,0); // Create a socket for the client
 
  struct hostent* h; // Data structure of ip address information
  if ( (h=gethostbyname(serverip)) == 0 )
  { close(m_sockfd); m_sockfd=0; return false; }
 
  // Convert the address and port of the server into a data structure
  struct sockaddr_in servaddr;
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons(port);
  memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);
 
  // Initiate a connection request to the server
  if (connect(m_sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))!=0)
  { close(m_sockfd); m_sockfd=0; return false; }
 
  return true;
}
 
int CTcpClient::Send(const void *buf,const int buflen)
{
  return send(m_sockfd,buf,buflen,0);
}
 
int CTcpClient::Recv(void *buf,const int buflen)
{
  return recv(m_sockfd,buf,buflen,0);
}

  they establish a connection, and the operation results are as follows:,

  for network programs, the meaning of encapsulation is as follows,

use C++The significance of encapsulation mainly includes the following aspects:
1) Put the data initialization code in the constructor;
2) Turn off socket The code that releases resources is put in the destructor;
3) hold socket Defined as a member variable of a class, the code outside the class can't see it at all socket;
4) The code is simpler and safer (the destructor automatically calls off) socket,Free resources).

3, Multi process and multi thread knowledge

3.1 basic functions of process

  several important functions of the process,

// 1. The function of getpid library function is to obtain the process number when the program is running.
pid_t getpid();

// 2. The fork function is used to generate a new process, and the return value of the function is pid_t is an integer. In the parent process, the return value is the child process number, and in the child process, the return value is 0.
pid_t fork();

linux Related operations on the process:
ps                     View the process of the current terminal.
ps -ef |grep book      View all processes of the system

  the child process and the parent process use the same code segment; The child process copies the stack and data segments of the parent process. Once the child process starts running, it copies all the data of the parent process, and then runs separately without affecting each other. A copy of the variable defined in the parent process will be copied in the child process. After fork, the child process's operation on the variable will not affect the parent process, and the parent process's operation on the variable will not affect the child process. Also note that,

1)The process number is dynamically allocated by the system. The same program is executed at different times, and the process number is different.
2)The process number will be recycled, but at the same time, the process number is unique, that is, no matter at any time, the system
 There cannot be two processes with the same number.

3.2 interprocess communication

  the data space of processes is independent and private, and cannot be accessed to each other. However, in some cases, processes need to communicate to realize a function or exchange data, including:
   1) data transmission: one process needs to send its data to another process.
   2) shared data: multiple processes want to operate shared data. If one process modifies the shared data, other processes should see it immediately.
   3) notification event: a process needs to send a message to another process or group of processes to notify it (them) of an event (such as notifying the process to exit).
  4) process control: one process wants to control the operation of another process.
  process communication can be divided into the following ways:,

1)Pipes: including nameless pipes( pipe)And named pipes( named pipe),Anonymous pipes can be used with parent and child processes
 The named pipeline overcomes the limitation that the pipeline has no name. Therefore, in addition to the functions of the pipeline, it also allows no relatives
 Communication between processes.
2)Message queue( message): Processes can add messages to the queue, and other processes can read messages in the queue.
3)Signal( signal): Signals are used to inform other processes that an event has occurred.
4)Shared memory( shared memory): Multiple processes can access the same memory space.
5)Semaphore( semaphore): Also known as a semaphore, it is used to lock shared resources between processes.
6)Socket( socket): It can be used for interprocess communication between different computers.

Application experience:
1)The pipeline is too outdated. It has no application value. Just understand the concept.
2)socket It can be used for process communication between different systems, and can completely replace the pipes and channels that can only communicate between processes in the same system
 Message queuing.
3)There are many application scenarios of signals, which are mainly used for process control, such as notifying the running background service program to exit.
4)In the same system, the efficiency of data exchange between processes using shared memory is the highest. However, shared memory has no locking mechanism
 It is often used in combination with signal lights. In high-performance network server programs, shared memory can be used as data cache
(cache). 
5)In Enterprise IT Within the system, message queue has gradually become the core means of communication. It has the advantages of low coupling, reliable delivery, broadcasting and traffic
 Control, consistency and other functions. There are many mainstream message middleware in the market today Redis,RabbitMQ,Kafka,
ActiveMQ,ZeroMQ,Independently developed by Alibaba RocketMQ Wait.

3.3 Linux signal

   if you want the program to run in the background, when executing the program, add the "&" symbol at the end of the command. The number of programs

For example, before running the client:
./client 127.0.0.1 5005 & 
This allows multiple clients to run

View process
ps -ef|grep client

killall client 
Kill the process
 perhaps
 First use“ ps -ef|grep client"Find the process number of the program and use“ kill Process number.

  you can also use fork. The main program executes fork to generate a child process, and then the parent process exits, leaving the child process to continue running, and the child process will be managed by the system. This also adds a program in the background

if (fork()>0) return 0;

   signal signal is a very important part of Linux programming. Next, we will introduce in detail the basic concept, implementation and use of signal, and several system calls (library functions) with signal. Signal signal is a method for transmitting messages between processes. Signals are called soft interrupt signals, or soft interrupt signals.
   soft interrupt signal (signal, also referred to as signal) is used to notify the process of an event. Soft interrupt signals can be sent between processes by calling kill library functions. The Linux kernel may also send signals to the process to notify the process of an event (such as memory out of bounds).
   note that signals are only used to inform a process of what events have occurred, and no data can be transmitted to the process. The process has three methods for processing signals:
   1) the first method is to ignore a signal and do no processing on the signal as if it had not happened.
   2) the second is to set the interrupt processing function, which will process the signal after receiving it.
   3) the third method is that the default operation of the system is adopted for the processing of the signal, and the default operation of most signals is to terminate the process.
   there are many reasons for sending signals. Here, simply classify them according to the reasons for sending signals to understand various signals. Refer to the link: http://www.freecplus.net/eec5c39aa63b45ad946f1cc08134d9f9.html
   the signal library function can set the signal processing method of the program, as follows:,

sighandler_t signal(int signum, sighandler_t handler);

parameter signum Indicates the number of the signal.
parameter handler Indicates the processing mode of the signal. There are three cases:
1)SIG_IGN: Ignore parameters signum The signal referred to.
2)A user-defined function for processing signals. The signal number is the parameter of this user-defined function.
3)SIG_DFL: Recovery parameters signum The processing method of the indicated signal is the default value. 
Programmers don't care signal Return value of

Linux The operating system provides kill The command sends a signal to the program, C The language also provides kill Library function, which is used to enter other functions in the program
 Send a signal to a process or thread.
int kill(pid_t pid, int sig);
parameter pid There are several situations:
1)pid>0 Send signal to process number pid Process.
2)pid=0 Send a signal to all processes in the same process group as the current process. It is often used for the parent process to send a signal to the child process. Note: Send a signal
 The signaler process also receives its own signal.
3)pid=-1 Transmit the signal broadcast to all processes in the system. For example, when the system is shut down, the shutdown message will be broadcast to all login windows
 Rest.
sig: For the signal code to be sent, if its value is zero, no signal is sent, but the system will perform error checking, which is usually used sig A value of zero verifies that a process is still running.

Return value Description: 0 is returned when the execution is successful; Failure Return-1,errno Is set to one of the following values.
EINVAL: The specified signal code is invalid (parameter sig Illegal).
EPERM: Insufficient permissions to signal the specified process.
ESRCH: parameter pid The specified process or process group does not exist.

   the service program runs in the background. If you want to abort it, forcibly killing it is not a good way, because when the program is killed, the program suddenly dies and does not release resources, which will affect the stability of the system. Using Ctrl + C to abort is the same as killing the program. Signal function: if you can send a signal to the background program, after the background program receives the signal, call a function, write the code to release the resources in the function, the program can exit in a planned way, and it is safe and decent.

3.4 shared memory

  Shared Memory allows multiple processes to access the same memory space. It is the most efficient way to share and transfer data among multiple processes. The operating system arranges the Shared Memory between different processes into the same physical memory. Processes can connect the Shared Memory to their own address space. If a process modifies the data in the Shared Memory, the data read by other processes will also change. The Shared Memory does not provide a locking mechanism. If you want to lock the read / write of Shared Memory, you can use the semaphore. Linux provides a set of functions for operating Shared Memory. The program needs to include the following header files:

#include <sys/ipc.h>
#include <sys/shm.h>

  some important functions,

shmget The function is used to obtain or create shared memory. Its declaration is:
int shmget(key_t key, size_t size, int shmflg);
parameter key Is the key value of shared memory. It is an integer, typedef unsigned int key_t,Is the number of shared memory in the system. Different shared memory numbers cannot be the same, which is guaranteed by the programmer. key Hexadecimal representation is better.
parameter size Is the size of the shared memory to be created, in bytes.
parameter shmflg Is the access permission of shared memory, which is the same as that of files. 0666|IPC_CREAT Indicates that all users can read and write to it. If the shared memory does not exist, a shared memory will be created.


Connect shared memory to the address space of the current process. Its statement is as follows:
void *shmat(int shm_id, const void *shm_addr, int shmflg);
parameter shm_id By shmget The shared memory ID returned by the function.
parameter shm_addr Specifies the address location where the shared memory is connected to the current process. It is usually empty, indicating that the system is allowed to select the address of the shared memory.
parameter shm_flg Is a set of flag bits, usually 0.
A pointer to the first byte of shared memory is returned when the call is successful. If the call fails, it is returned-1.


This function is used to separate the shared memory from the current process, equivalent to shmat Inverse operation of function. Its statement is as follows:
int shmdt(const void *shmaddr);
parameter shmaddr yes shmat The address returned by the function.
0 is returned when the call succeeds and 0 is returned when the call fails-1.


Delete the shared memory. Its declaration is as follows:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
parameter shm_id yes shmget The shared memory identifier returned by the function.
parameter command fill IPC_RMID. 
parameter buf Fill in 0.
Explain, shmctl It is a function to control shared memory. Its function is not only to delete shared content, but other functions are useless, so I won't introduce them.
Attention, use root The shared memory created cannot be deleted by ordinary users regardless of the permission to create it.

  an example program is as follows,

/*
 * Program name: book258.cpp. This program is used to demonstrate the usage of shared memory
 * Author: C language technology network (www.freecplus.net) date: 20190525
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h> 

int main()
{
  int shmid; // Shared memory identifier
 
  // Create shared memory. The key value is 0x5005, 1024 bytes in total.
  if ( (shmid = shmget((key_t)0x5005, 1024, 0640|IPC_CREAT)) == -1)
  { printf("shmat(0x5005) failed\n"); return -1; }
   
  char *ptext=0;   // Pointer to shared memory
 
  // Connect the shared memory to the address space of the current process, and point to it by the ptext pointer
  ptext = (char *)shmat(shmid, 0, 0);
 
  // Operating the ptext pointer of this program is to operate the shared memory
  printf("Before writing:%s\n",ptext);
  sprintf(ptext,"The process number of this procedure is:%d",getpid());
  printf("After writing:%s\n",ptext);
 
  // Separates shared memory from the current process
  shmdt(ptext);
   
  // Delete shared memory
  // if (shmctl(shmid, IPC_RMID, 0) == -1)
  // { printf("shmctl(0x5005) failed\n"); return -1; }
}

The operation results are as follows:,

Because the shared memory is not deleted in the program, the process number will be written into the shared memory every time it runs. At first, the shared memory is empty, and then it will be filled and overwritten. Can use

ipcs -m  // View shared memory

ipcrm -m number(shmid) // Manually delete shared memory

The operation is as follows:,

3.5 Linux semaphore

   semaphore (semaphore) is essentially a counter used to coordinate the reading / writing of shared data objects by multiple processes (including but not limited to parent-child processes). It is not for the purpose of transmitting data. It is mainly used to protect shared resources (shared memory, message queue, socket connection pool, database connection pool, etc.) and ensure that shared resources are exclusive to only one process at a time. Semaphore is a special variable, which only allows the process to wait for and send signals. The simplest semaphore is the binary semaphore with values of 0 and 1, which is the most common form of semaphore. The knowledge of general semaphore (multiple positive integer values can be taken) and semaphore set is complex, and there are few application scenarios.
   Linux provides a set of functions to operate semaphores. The program needs to include the following header files:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

   the correlation function is as follows,

semget Function is used to obtain or create semaphores. Its prototype is as follows:
int semget(key_t key, int nsems, int semflg);
1)parameter key Is the key value of the semaphore, typedef unsigned int key_t,Is the number of semaphores in the system. The numbers of different semaphores cannot be the same, which is guaranteed by the programmer. key Hexadecimal representation is better.
2)parameter nsems Is the number of semaphores in the created semaphore set. This parameter is only valid when creating a semaphore set. Here, it is fixed and filled with 1.
3)parameter sem_flags Is a set of flags. If you want to create a new semaphore when the semaphore does not exist, you can use the and value IPC_CREAT Do bit by bit or operation. If not set IPC_CREAT Flag and the semaphore does not exist, an error will be returned( errno The value of is 2, No such file or directory). 
4)If semget If the function succeeds, it returns the identifier of the semaphore set; if it fails, it returns-1,Error reason exists in error Yes.


This function is used to control semaphores (commonly used to set the initial value of semaphores and destroy semaphores). Its prototype is as follows:
int semctl(int semid, int sem_num, int command, ...);
1)parameter semid By semget The semaphore ID returned by the function.
2)parameter sem_num Is the subscript on the semaphore set array, indicating a semaphore, and fill in 0.
3)parameter cmd It is the type of command for semaphore operation. There are two commonly used commands:
IPC_RMID: Destroy the semaphore without the fourth parameter;
SETVAL: Initialize the value of the semaphore (after the semaphore is successfully created, the initial value needs to be set). This value is determined by the fourth parameter. The fourth parameter is a user-defined community, as follows:
  // Community for signal lamp operation.
  union semun
  {
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
  };
4)If semctl Function call failure return-1;If it is successful, the return value is complex and I don't care about it for the time being.


This function has two functions: 1) wait for the semaphore value to change to 1. If the waiting is successful, immediately set the semaphore value to 0. This process is also called waiting lock; 2) Setting the value of the semaphore to 1 is also called releasing the lock.
int semop(int semid, struct sembuf *sops, unsigned nsops);
1)parameter semid By semget The semaphore ID returned by the function.
2)parameter nsops Is the number of operation semaphores, i.e sops The number of structure variables. Set its to 1 (only for the operation of one semaphore).
3)parameter sops Is a structure, as follows:
struct sembuf
{
  short sem_num;   // The number of semaphore sets. A single semaphore is set to 0.
  short sem_op;    // Semaphore data to be changed in this operation: - 1 - wait for operation; 1 - send operation.
  short sem_flg;   // Set this flag to SEM_UNDO, the operating system will track this semaphore.
                   // If the semaphore is not released when the current process exits, the operating system will release the semaphore to avoid resource deadlock.
};
Example:
1)The value of the waiting semaphore changes to 1. If the waiting is successful, set the value of the semaphore to 0 immediately;
  struct sembuf sem_b;
  sem_b.sem_num = 0;
  sem_b.sem_op = -1;
  sem_b.sem_flg = SEM_UNDO;
  semop(sem_id, &sem_b, 1);
2)Set the semaphore value to 1.
  struct sembuf sem_b;
  sem_b.sem_num = 0;
  sem_b.sem_op = 1;
  sem_b.sem_flg = SEM_UNDO;
  semop(sem_id, &sem_b, 1);

  examples are as follows,

/*
 * Program name: book259.cpp. This program is used to demonstrate the use of semaphores.
 * Author: C language technology network (www.freecplus.net) date: 20190525
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>
 
class CSEM
{
private:
  union semun  // Community for signal lamp operation.
  {
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
  };
 
  int  sem_id;  // Semaphore descriptor.
public:
  bool init(key_t key); // If the signal lamp already exists, obtain the signal lamp; If the semaphore does not exist, the semaphore is created and initialized.
  bool wait();          // Wait for the signal light to hang out.
  bool post();          // Hang out the signal light.
  bool destroy();       // Destroy the signal light.
};
 
int main(int argc, char *argv[])
{
   CSEM sem;
 
   // Initial signal lamp.
   if (sem.init(0x5000)==false) { printf("sem.init failed.\n"); return -1; }
   printf("sem.init ok\n");
  
   // Wait for the letter signal to hang out. After the wait is successful, the lock will be held.
   if (sem.wait()==false) { printf("sem.wait failed.\n"); return -1; }
   printf("sem.wait ok\n");
 
   sleep(50);  // During sleep, running other book259 programs will wait for the lock.
  
   // Hang out the signal light and release the lock.
   if (sem.post()==false) { printf("sem.post failed.\n"); return -1; }
   printf("sem.post ok\n");
  
   // Destroy the signal light.
   // if (sem.destroy()==false) { printf("sem.destroy failed.\n"); return -1; }
   // printf("sem.destroy ok\n");
}
 
bool CSEM::init(key_t key)
{
  // Get the signal light.
  if ( (sem_id=semget(key,1,0640)) == -1)
  {
    // If the semaphore does not exist, create it.
    if (errno==2)
    {
      if ( (sem_id=semget(key,1,0640|IPC_CREAT)) == -1) { perror("init 1 semget()"); return false; }
 
      // After the semaphore is created successfully, it needs to be initialized to an available state.
      union semun sem_union;
      sem_union.val = 1;
      if (semctl(sem_id,0,SETVAL,sem_union) <  0) { perror("init semctl()"); return false; }
    }
    else
    { perror("init 2 semget()"); return false; }
  }
 
  return true;
}
 
bool CSEM::destroy()
{
  if (semctl(sem_id,0,IPC_RMID) == -1) { perror("destroy semctl()"); return false; }
 
  return true;
}
 
bool CSEM::wait()
{
  struct sembuf sem_b;
  sem_b.sem_num = 0;
  sem_b.sem_op = -1;
  sem_b.sem_flg = SEM_UNDO;
  if (semop(sem_id, &sem_b, 1) == -1) { perror("wait semop()"); return false; }
   
  return true;
}
 
bool CSEM::post()
{
  struct sembuf sem_b;
  sem_b.sem_num = 0;
  sem_b.sem_op = 1;  
  sem_b.sem_flg = SEM_UNDO;
  if (semop(sem_id, &sem_b, 1) == -1) { perror("post semop()"); return false; }
 
  return true;
}

  the results are as follows:,

   it can be seen that when two programs are running, one program occupies signal resources and is in the wait state after init; After the other program runs, it is in the suspended state after init. After the first program post, the second program is in the wait state. Here, init is after initialization, wait is waiting, but it occupies the current signal resources. Post is hanging out and releases the signal resources.
  available

ipcs -s        //  View the semaphore of the system
ipcrm sem 8    //  Delete semaphore manually

  the results are as follows:,

3.6 Linux threads

Multithreading is a more resource-saving multi task operation mode compared with multi process. To start a new process, it must be allocated an independent address space. Each process has its own stack segment and data segment. The system overhead is relatively high. Data transmission can only be carried out through inter communication. In the same process, multiple threads can be run. Multiple threads running in the same process use the same address space and share global variables and objects. The resources consumed by starting a thread are less than those consumed by starting a process.
  pthread is adopted under Linux_ Create function to create a new thread. The function declares:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
parameter thread Is the address that points to the thread identifier.
parameter attr It is used to set thread properties. Generally, it is empty, indicating that the default property is used.
parameter start_routine Is the address of the thread running function. Just fill in the function name.
parameter arg Is the parameter of the thread running function. Newly created thread from start_routine The address of the function starts running, and the function has only one typeless pointer parameter arg. If you want to start_routine By passing multiple parameters, you can put multiple parameters in a structure, and then take the address of the structure as arg Parameters are passed in, but be very careful. Programmers usually don't do this.
When compiling, pay attention to adding-lpthread Parameter to call the static link library. because pthread be not Linux The default library for the system.

   if any thread in the process calls exit, the whole process will terminate. Therefore, at the start of the thread_ Exit cannot be used in routine function. There are three ways to terminate a thread: 1) start of the thread_ The routine function code ends and dies naturally. 2) Start of thread_ The routine function calls pthread_exit ends. 3) Aborted by the main process or another thread. pthread_ The exit function is declared as follows:

void pthread_exit(void *retval);
parameter retval Fill in the blank, i.e. 0

   there is an example of a multithreaded socket server. Note that you need to add a socket client program to realize communication. The program is as follows,

/*
 * Program name: book261.cpp. This program is used to demonstrate multithreaded socket communication server
 * Author: C language technology network (www.freecplus.net) date: 20190525
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
 
class CTcpServer
{
public:
  int m_listenfd;   // socket used by the server to listen
  int m_clientfd;   // socket connected to the client
 
  CTcpServer();
 
  bool InitServer(int port);  // Initialize server
 
  bool Accept();  // Waiting for client connection
 
  // Send message to opposite end
  int  Send(const void *buf,const int buflen);
  // Receive message from opposite end
  int  Recv(void *buf,const int buflen);
 
  // void CloseClient();    //  Close the socket of the client. The multithreaded server does not need this function.
  // void CloseListen();    //  Close the socket for listening. The multithreaded server does not need this function.
 
 ~CTcpServer();
};
 
CTcpServer TcpServer;
 
// Processing functions for SIGINT and SIGTERM
void EXIT(int sig)
{
  printf("Program exit, signal value=%d\n",sig);
 
  close(TcpServer.m_listenfd);  // Manual closing m_listenfd, release resources
 
  exit(0);
}
 
// Main function of communication thread with client
void *pth_main(void *arg);
 
int main()
{
  // Ignore all signals
  for (int ii=0;ii<50;ii++) signal(ii,SIG_IGN);
 
  // Set the handling functions of SIGINT and SIGTERM
  signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
 
  if (TcpServer.InitServer(5005)==false)
  { printf("Server initialization failed. The program exits.\n"); return -1; }
 
  while (1)
  {
    if (TcpServer.Accept() == false) continue;
 
    pthread_t pthid;   // Create a thread to communicate with the newly connected client
    if (pthread_create(&pthid,NULL,pth_main,(void*)((long)TcpServer.m_clientfd))!=0)
    { printf("Failed to create thread. The program exits. n"); return -1; }
 
    printf("A thread communicating with the client has been created.\n");
  }
}
 
CTcpServer::CTcpServer()
{
  // Constructor initializes socket
  m_listenfd=m_clientfd=0;
}
 
CTcpServer::~CTcpServer()
{
  if (m_listenfd!=0) close(m_listenfd);  // The destructor closes the socket
  if (m_clientfd!=0) close(m_clientfd);  // The destructor closes the socket
}
 
// Initialize the socket of the server, and the port is the communication port
bool CTcpServer::InitServer(int port)
{
  if (m_listenfd!=0) { close(m_listenfd); m_listenfd=0; }
 
  m_listenfd = socket(AF_INET,SOCK_STREAM,0);  // Create a socket on the server
 
  // Bind the address and port used by the server for communication to the socket
  struct sockaddr_in servaddr;    // Data structure of server address information
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;  // The protocol family can only be AF in socket programming_ INET
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  // Any ip address of this host
  servaddr.sin_port = htons(port);  // Binding communication port
  if (bind(m_listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
  { close(m_listenfd); m_listenfd=0; return false; }
 
  // Set socket to listening mode
  if (listen(m_listenfd,5) != 0 ) { close(m_listenfd); m_listenfd=0; return false; }
 
  return true;
}
 
bool CTcpServer::Accept()
{
  if ( (m_clientfd=accept(m_listenfd,0,0)) <= 0) return false;
 
  return true;
}
 
int CTcpServer::Send(const void *buf,const int buflen)
{
  return send(m_clientfd,buf,buflen,0);
}
 
int CTcpServer::Recv(void *buf,const int buflen)
{
  return recv(m_clientfd,buf,buflen,0);
}
 
// Main function of communication thread with client
void *pth_main(void *arg)
{
  int clientfd=(long) arg; // The arg parameter is the socket of the new client.
 
  // Communicate with the client and reply ok after receiving the message sent by the client.
  char strbuffer[1024];
 
  while (1)
  {
    memset(strbuffer,0,sizeof(strbuffer));
    if (recv(clientfd,strbuffer,sizeof(strbuffer),0)<=0) break;
    printf("receive:%s\n",strbuffer);
 
    strcpy(strbuffer,"ok");
    if (send(clientfd,strbuffer,strlen(strbuffer),0)<=0) break;
    printf("send out:%s\n",strbuffer);
  }
 
  printf("Client disconnected.\n");
 
  close(clientfd);  // Close the client connection.
 
  pthread_exit(0);
}

  name the file Thread.cpp and copy client.cpp to the file. The makefile file is as follows,

all:client Thread
client:client.cpp
	g++ -g -o client client.cpp

Thread:Thread.cpp
	g++ -g -o Thread Thread.cpp -lpthread

  the operation results are as follows:,

  pay attention to several points in the above procedure,

1. The signal processing is set to Ctrl+c,kill When using semaphores EXIT Function:
// Set the handling functions of SIGINT and SIGTERM
signal(SIGINT,EXIT); signal(SIGTERM,EXIT);

2. Attention in pth_main As a function executed by a thread, the syntax is mentioned above, but pay attention to the type of parameters
 Change.

   threads have two states: joinable and unjoinable. If the thread is joinable, the memory resources and other resources occupied by the thread will not be released when the main function of the thread terminates (exit by itself or call pthread_exit). This thread is called "zombie thread". When creating a thread, it is non detachable by default, or called joinable. To avoid zombie threads is how to correctly recycle thread resources. There are four methods:

Method 1: after creating the thread, call it in the program creating the thread. pthread_join This method is generally not used when waiting for the thread to exit, because pthread_join Blocking will occur.
pthread_join(pthid,NULL);

2)Method two: before creating thread, call pthread_attr_setdetachstate Set thread to detached,In this way, the system automatically reclaims thread resources when the thread exits.
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);  // Set the properties of the thread.
pthread_create(&pthid,&attr,pth_main,(void*)((long)TcpServer.m_clientfd);

3)Method three: after creating the thread, call it in the program creating the thread. pthread_detach Set the newly created thread to detached Status.
pthread_detach(pthid);

4)Method four: calling in online main function pthread_detach Change your state.
pthread_detach(pthread_self());

3.7 Linux thread synchronization

   there are two kinds of locks: one is that access is not allowed; The other is that the resource is busy. Only one user is allowed to occupy it at the same time, and other users must wait. For multithreading, resources are shared, and access is basically not allowed. However, shared resources can only be occupied by one thread at a certain point in time, so resources need to be locked.
  the types of thread locks include mutex lock, read-write lock, condition variable, spin lock and semaphore. In learning, we only introduce mutex locks. Other lock application scenarios are complex and difficult to develop, which is not suitable for beginners.
   the mutex mechanism allows only one thread to occupy shared resources at the same time.
  1. Initialization lock

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);

Where parameters mutexattr Used to specify the attribute of the lock (see below). If it is NULL The default attribute is used.

The attribute of mutex is specified when creating a lock. When a resource is locked by a thread, other threads will behave differently when trying to lock. There are currently four values to choose from:
1)PTHREAD_MUTEX_TIMED_NP,This is the default, which is a normal lock. When a thread is locked, the other threads requesting the lock will form a waiting queue and obtain the lock according to priority after unlocking. This locking strategy ensures the fairness of resource allocation.
2)PTHREAD_MUTEX_RECURSIVE_NP,Nested locks allow the same thread to successfully obtain the same lock multiple times and pass it multiple times unlock Unlock.
3)PTHREAD_MUTEX_ERRORCHECK_NP,Error detection lock. If the same thread requests the same lock, it returns EDEADLK,Otherwise with PTHREAD_MUTEX_TIMED_NP The type action is the same.
4)PTHREAD_MUTEX_ADAPTIVE_NP,Adaptive lock, the simplest type of lock, will compete again after unlocking.

  2. Blocking and locking

int pthread_mutex_lock(pthread_mutex *mutex);

If the lock is idle, the thread will obtain the lock; If the lock is already occupied, the thread will queue until the lock is successfully obtained.

  3. Non blocking locking

int pthread_mutex_trylock( pthread_mutex_t *mutex);

This function has the same semantics as pthread_mutex_lock() Similarly, the difference is to return immediately when the lock has been occupied EBUSY,Not a pending wait.

  4. Unlocking

int pthread_mutex_unlock(pthread_mutex *mutex);

The thread releases the lock it holds

   5. Destroy the lock (at this time, the lock must be in the unlock state, otherwise it returns EBUSY)

int pthread_mutex_destroy(pthread_mutex *mutex);

The lock must be idle before it can be destroyed( unlock). 

Multithreading can share resources (variables and objects) , which is convenient for programming. However, although some objects can be shared, they can only be used by one thread at the same time. If multiple threads are used at the same time, there will be conflicts, such as socket connection and database connection pool. The source program is as follows, because the author did not add CTcpserver class, I later added it to run it,

/*
 * Program name: book263.cpp. This program is used to demonstrate the mutex of multithreading
 * Author: C language technology network (www.freecplus.net) date: 20190525
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>

class CTcpServer
{
public:
  int m_listenfd;   // socket used by the server to listen
  int m_clientfd;   // socket connected to the client
 
  CTcpServer();
 
  bool InitServer(int port);  // Initialize server
 
  bool Accept();  // Waiting for client connection
 
  // Send message to opposite end
  int  Send(const void *buf,const int buflen);
  // Receive message from opposite end
  int  Recv(void *buf,const int buflen);
 
  // void CloseClient(); / / close the socket of the client. The multithreaded server does not need this function.
  // void CloseListen(); / / close the socket for listening. The multithreaded server does not need this function.
 
 ~CTcpServer();
};
 
CTcpServer TcpServer;

//xx pthread_mutex_t mutex; / / declare a mutex
 
// Main function of communication thread with client
void *pth_main(void *arg)
{
  int pno=(long)arg;   // Thread number
 
  pthread_detach(pthread_self());
 
  char strbuffer[1024];
 
  for (int ii=0;ii<3;ii++)    // Interact with the server for 3 times.
  {
    //XX pthread_mutex_lock (& mutex); / / lock
    memset(strbuffer,0,sizeof(strbuffer));
    sprintf(strbuffer,"thread %d: This is the second%d Super girl, number%03d. ",pno,ii+1,ii+1);
    if (TcpClient.Send(strbuffer,strlen(strbuffer))<=0) break;
    printf("send out:%s\n",strbuffer);
 
    memset(strbuffer,0,sizeof(strbuffer));
    if (TcpClient.Recv(strbuffer,sizeof(strbuffer))<=0) break;
    printf("thread %d receive:%s\n",pno,strbuffer);
    //XX pthread_mutex_unlock (& mutex); / / release lock
    // Usleep (100); / / usleep (100), otherwise other threads cannot obtain locks.
  }
 
  pthread_exit(0);
}
 
int main()
{
  // Initiate a connection request to the server
  if (TcpClient.ConnectToServer("172.16.0.15",5051)==false)
  { printf("TcpClient.ConnectToServer(\"172.16.0.15\",5051) failed,exit...\n"); return -1; }
 
  //XX pthread_mutex_init (& mutex, 0); / / create lock
 
  pthread_t pthid1,pthid2;
  pthread_create(&pthid1,NULL,pth_main,(void*)1);   // Create first thread
  pthread_create(&pthid2,NULL,pth_main,(void*)2);   // Create a second thread
 
  pthread_join(pthid1,NULL);    // Wait for thread 1 to exit.
  pthread_join(pthid2,NULL);    // Wait for thread 2 to exit.
 
  //XX pthread_mutex_lock (& mutex); / / destroy lock
}

CTcpServer::CTcpServer()
{
  // Constructor initializes socket
  m_listenfd=m_clientfd=0;
}
 
CTcpServer::~CTcpServer()
{
  if (m_listenfd!=0) close(m_listenfd);  // The destructor closes the socket
  if (m_clientfd!=0) close(m_clientfd);  // The destructor closes the socket
}
 
// Initialize the socket of the server, and the port is the communication port
bool CTcpServer::InitServer(int port)
{
  if (m_listenfd!=0) { close(m_listenfd); m_listenfd=0; }
 
  m_listenfd = socket(AF_INET,SOCK_STREAM,0);  // Create a socket on the server
 
  // Bind the address and port used by the server for communication to the socket
  struct sockaddr_in servaddr;    // Data structure of server address information
  memset(&servaddr,0,sizeof(servaddr));
  servaddr.sin_family = AF_INET;  // The protocol family can only be AF_INET in socket programming
  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  // Any ip address of this host
  servaddr.sin_port = htons(port);  // Binding communication port
  if (bind(m_listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
  { close(m_listenfd); m_listenfd=0; return false; }
 
  // Set socket to listening mode
  if (listen(m_listenfd,5) != 0 ) { close(m_listenfd); m_listenfd=0; return false; }
 
  return true;
}
 
bool CTcpServer::Accept()
{
  if ( (m_clientfd=accept(m_listenfd,0,0)) <= 0) return false;
 
  return true;
}
 
int CTcpServer::Send(const void *buf,const int buflen)
{
  return send(m_clientfd,buf,buflen,0);
}
 
int CTcpServer::Recv(void *buf,const int buflen)
{
  return recv(m_clientfd,buf,buflen,0);
}

The operation results are as follows:,

It can be seen that if the data received by the client is out of order and irregular, lock them. Note that the above comments can be cancelled. These are the statements,

..... global variable
pthread_mutex_t mutex; // Declare a mutex

.....Thread function
pthread_mutex_lock(&mutex);  // Lock

.....Thread function
pthread_mutex_unlock(&mutex);  // Release lock
usleep(100);   // usleep(100), otherwise other threads cannot obtain locks.

.....Main function
pthread_mutex_init(&mutex,0); // Create lock

.....
pthread_mutex_lock(&mutex);   // Destroy lock

The operation results are as follows:,

It can be seen that the mutex works when the reception is in order.

4, Summary

   after that, continue to study deeply in the face of network programming knowledge, reproduce, modify and summarize.

Topics: C++ Linux Multithreading multiple processes