socket programming of OSI layer 7, TCP/IP layer 5, UDP and TCP (server and client), byte order conversion, implementation of multi process and multi-threaded server

Posted by VanHagar on Fri, 14 Jan 2022 05:03:31 +0100

1. The network is divided by coverage: LAN / man / WAN, Internet / Internet, Ethernet / token ring - networking mode

2. Each host must be represented by one in the network to realize point-to-point accurate communication

IP address: ipv4: uint32_t unsigned 4-byte integer} DHCP/NAT} ipv6: uint_t addr[16];

Each data in network communication must have: source IP address / destination IP address -- indicating which host the data comes from and goes to.

Destination IP address: the router in the network can select different paths for each data according to the destination address to reach the opposite host.

Source IP address: it can let the peer host know who sent the data, so as to reply to the data.

3.IP address enables the communication between hosts in the network, but there are many processes on the host: the communication must identify which process should process a piece of data

Port: uint16_t unsigned 2-byte integer 0 ~ 65535

A process can use multiple ports, but a port can only be occupied by one process.

Each data in the network must have: source port / destination port ¢ indicates which process the data comes from and goes to.

4. Why not use pid to identify the process, but use the new field port identification?

The pid of the process will change with the restart of the program, but the port will not.

5. Inter process communication (network communication) between different hosts can be realized through IP address and port

Agreement: agreement -- (agreed common rules)

Network communication protocol: agreement on data format in network communication

6. Protocol layering:

Encapsulate communication protocols in different communication environments, use different protocols at different levels and provide different services; By dividing the communication environment, the implementation of communication is simpler and easier to form specifications.

Protocol layering in network communication environment

ISO: OSI seven layer reference model: application layer / presentation layer / session layer / transport layer / network layer / link layer / physical layer

TCP/IP five layer (or four layer) model: application layer / transport layer / network layer / link layer / physical layer (less considered)

TCP/IP is a set of protocol stack / protocol cluster: it contains many protocols, and IP and TCP protocols are only two typical ones.

Application layer: responsible for data communication between applications; For example, the communication protocol between qq and qq negotiates the data format of qq; Typical protocol: HTTP/DNS/FTP

Transport layer: responsible for data transmission between processes on different hosts; Because the main information contained in the transport layer protocol is the port; Typical protocol: TCP/UDP

Network layer: responsible for address management and routing; Select an appropriate path for the data in the network; IP information; Typical protocol: IP; Typical equipment: router

Link layer: responsible for data frame identification and transmission between adjacent devices; MAC address information of network card equipment; Typical protocol: Ethernet; Typical equipment: switch

Physical layer: responsible for the transmission of physical photoelectric signals; Typical protocol: Ethernet protocol

Protocol layering: according to different services provided, different communication levels are divided, and different protocols are used at each level to realize data format conventions - simplifying and clarifying the complex network communication environment.

Data transmission process in network communication (transmission layer):

Byte order: the order in which the cpu accesses data in memory -- depending on the cpu architecture

cpu architecture: X86 architecture cpu (small end) / mips architecture cpu (large end)

0x01020304 01 is high and 04 is low

Large end byte order: low address memory high bit a[0]=01 ^ a[1]=02 ^ a[2]=03 ^ a[3]=04

Small end byte order: low address storage low bit a[0]=04 , a[1]=03 , a[2]=02 , a[3]=01

Network communication is the communication between different hosts, but the byte order on different hosts will have a great impact on the communication: Data ambiguity

Therefore, we must unify the byte order - network byte order in network communication in order to avoid this problem

No matter whether the host is a large end or a small end, the network communication is uniformly converted to the network byte order - large end byte order

If the communication host is a small end host, the data can be sent only after byte order conversion during network communication

Not all data needs to be converted: the key to the data that needs to be converted is the data with an access size of more than one byte in memory: short int long float double

But the string char buf[1024] does not need to be converted -- the string itself is stored in bytes

What needs to be converted is the small end host, but not the large end -- how to judge whether the byte order of a host is the large end or the small end?

union tmp(int a;char b(b is a[0]);)   tmp. A = 1 if (TMP. B = 1) (small end)

Socket programming:

Socket is a set of network programming interface, similar to middleware; Upper layer users can simply complete network communication transmission through these interfaces; Instead of paying too much attention to the internal implementation process. Socket programming is about using socket interface to realize network communication

Quintuple: source IP address / source port / destination IP address / destination port / communication protocol -- identify a communication -- the data in each network will contain information.

socket programming: TCP/UDP (duplex communication)

The transport layer has two protocols: TCP/UDP; The two protocols are different, so the implementation process is also slightly different;

UDP: user datagram protocol; No connection, unreliable, datagram oriented;

I don't care whether the data responds or is lost.

Application scenario: a scenario where real-time data is greater than security -- video transmission

TCP: transmission control protocol: connection oriented, reliable transmission, byte stream oriented;

If a connection is established first and the server does not respond, the connection cannot be established

Application scenario: data security is greater than real-time -- file transfer

Datagram oriented: connectionless, unreliable, unnecessary, data transmission service with maximum length limit

Byte stream oriented: connection based, reliable, orderly and bidirectional byte stream transmission service} takes bytes as a unit and does not limit the size of upper layer transmission data.

Communication between two host processes in network communication: client / server

Client: the party that actively sends the request; server: the party that passively accepts the request

Always send requests from the client - > server

(this means that the client must know the address information of the server before it can package the data i layer by layer when sending data)

The data transmitted through the network should include: source IP address / destination IP address / source port / destination port / protocol

UDP network programming process:

Server side: (address permanently unchanged)

1. Create socket -- create socket structure in the kernel; Return an operation handle to the process;

Connect with the network card through the socket structure in the kernel

2. Bind address information for socket -- add various address description information (IP address and port) to the socket structure created in the kernel

The binding address is to tell the operating system which address and port I used and you received the data. If the destination address information is the same as the address information I bound, the data will be handed over to me for processing. When sending data, the source address information is the bound address information--- The server and the client bind their own address information.
When the operating system receives the data, it will judge the information of the destination address and go to the socket container in the kernel to find it one by one. If it finds it, it will put the data into the buffer received by the socket. If it is not found, it will be discarded directly.

3. Receive data -- fetch data from the receive buffer in the socket structure

Each data contains the source address and destination address, so obtaining the data will know who the opposite end is

4. Send data -- copy the data to the send buffer of the socket structure in the kernel

At this time, the operating system will take out the data from the transmission buffer at an appropriate time, and then encapsulate the data layer by layer and send it through the network card

5. If there is no communication, close the socket and release resources

client:

               1. Create socket

               2. Bind address information for socket

               3. send data

               4. receive data

               5. Close socket

Introduction to socket interface:

1. Create socket

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

Domain: address domain -- different network address structures_ INET - ipv4 address domain

Type: socket type -- streaming socket / datagram socket

Streaming socket: an ordered, reliable, bidirectional, connection based byte stream socket_ STREAM

Datagram socket: connectionless, unreliable, transmission socket with maximum length limit_ DGRAM

Protocol: protocol used 0 -- default protocol under different socket types; The default of streaming socket is TCP / datagram socket, and the default is UDP

               IPPROTO_TCP--TCP protocol {ipproto_ UDP -- UDP protocol

Return value: returns the operation handle of the socket --- file descriptor

2. Bind the address information for the socket (convert it to a unified network byte order in order to achieve unified interface)

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

sockfd: creates the operation handle returned by the socket

addr: address information structure to bind

len: length of address information

Return value: 0 is returned successfully; Failure returned - 1

User defined SOCKADDR_ IPV4 geological structure of in is transferred into bind after strong conversion

                   bind(sockaddr*){

                                if(sockaddr->sin_family==AF_INET){

/ / this is the resolution of the ipv4 address structure

                              }else if(sockaddr->sin_family==AF_INT6)

                   }

                    bind((struct sockaddr*)&addr)

3. Send data

int sendto(int sockfd,char *data,int data_len,int flag,struct sockaddr *dest_addr,socklen_t len)

sockfd: socket operation handle. Sending data is to copy the data to the socket sending buffer of the kernel

Data: the first address of the data to be sent

data_len: length of data to send

flag: the option parameter , 0 by default -- indicates that the current operation is a blocking operation , MSG_ Donwait -- set to non blocking

If the socket send buffer is full when sending data, 0 will block and wait by default; MSG_ Donwait immediately returns an error

dest_addr: destination address information structure -- indicates who the data is to be sent to

Each piece of data must describe the source side information (bound address information) and the opposite side information (currently assigned information)

addr_len: address information structure length

Return value: the number of data bytes actually sent is returned successfully; failure returns - 1

4. Receive data

int recvfrom(int sockfd,char *buf ,int len, int flag, struct sockaddr *src_addr,socklen_t *addr_len);

sockfd: socket operation handle

BUF: the first address of the buffer, which is used to store the received data. Take the data from the kernel socket receiving buffer and put it into the buf user state buffer

len: the length of the data that the user wants to read, but it cannot be greater than the length of the buf buffer

flag: 0 - default blocking operation - if there is no data in the buffer, wait for MSG all the time_ Donwait - non blocking

src_addr: the address of the sender of the received data -- indicating who sent the data and where it came from -- the reply is to reply to this address

addr_len: input / output type parameter, used to specify how long address information you want to obtain; After obtaining the address, it is used to return the actual length of address information

Return value: the length of actually received data bytes is successfully returned; Failure returns - 1;

5. Close the socket

int close(int fd);

Special features of the process in the client program:

The client usually does not recommend the user to bind the address information himself, but let the operating system send the socket without binding the address when sending the data, and then automatically select an appropriate ip address and port for binding

       1. If you do not actively bind, the operating system will select an appropriate address information for binding (what address is the appropriate address? -- a port that is not currently used)

A port can only be occupied by one process. If the user specifies the port and address for binding, it is possible that the port has been used, and the binding will fail. Let the operating system select the appropriate port information to avoid the probability of port conflict as much as possible.

For the client, it doesn't care what source address to use to send data, as long as it can send and receive data

        2. Can the server bind the address actively or not--- may not

The address information of the server known by the client is what the server tells the client

Once the server does not actively bind the address, the operating system team doctor will choose the appropriate address for binding. The server is not sure what address information it uses and how to tell the client. Therefore, the server must usually actively bind the address and cannot change it at will

UDP server program: written in C language

 1 #include <stdio.h>                                                                                                                                                                     
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <netinet/in. h> / / SOCKADDR structure / IPPROTO_UDP
  5 #include <arpa/inet. h> / / contains some interfaces for byte order conversion
  6 #include <sys/socket. h> / / socket interface header file
  7 #include <stdlib.h>
  8 //Write a C language program for udp server
  9 //1. Create socket
 10 //2. Bind address information for socket
 11 //3. Receive data
 12 //4. Send data
 13 //5. Close the socket
 14 int main(int argc,char *argv[]){
 15 
 16   //argc indicates the number of parameters. Pass port parameters to the program through argv
 17   if(argc!=3){
 18     printf("./udp_srv ip port em:./udp_srv 192.168.122.132 9000\n");
 19     return -1;
 20   }
 21   const char *ip_addr=argv[1];
 22   uint16_t port_addr=atoi(argv[2]);
 23   //Socket (address domain, socket type, protocol type)
 24   //1. Create socket
 25   int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
 26   if(sockfd<0){
 27     perror("socket error");
 28     return -1;
 29   }
 30   //Bind (socket descriptor, address structure, address length);
 31   //struct sockaddr_in ipv4 address structure
 32   struct sockaddr_in addr;
 33   addr.sin_family=AF_INET;
 34   //htons -- converts a 2-byte host byte order integer to a network byte order integer
 35   addr.sin_port=htons(port_addr);
 36   //inet_addr converts a dotted decimal string IP address to an integer IP address in network byte order
 37   addr.sin_addr.s_addr=inet_addr(ip_addr);
 38   socklen_t len=sizeof(struct sockaddr_in);
 39   int ret=bind(sockfd,(struct sockaddr*)&addr,len);
 40   if(ret<0){
 41     perror("bind error");
 42     return -1;
 43   }
 44   //3. Receive data
 45   while(1){
 46     char buf[1024]={0};
 47     struct sockaddr_in cliaddr;
 48     socklen_t len=sizeof(struct sockaddr_in);
 49     //Recvfrom (descriptor, buffer, length, parameter, client address information, address information length)
 50     int ret=recvfrom(sockfd,buf,1023,0,(struct sockaddr*)&cliaddr,&len);
 51     if(ret<0){
 52       perror("recfrom error");
 53       return -1;
 54     }
 55     printf("client sya:%s\n",buf);
 56     printf("server say:");
 57     fflush(stdout);//Let the user input data and send it to the client
 58     memset(buf,0x00,1024);//Clear data in buf
 59     scanf("%s",buf);
 60     //Send the data in buf to cli addr client through sockfd
 61     ret=sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&cliaddr,len);
 62     if(ret<0){
 63       perror("sendto error");
 64       return -1;
 65     }
 66   }
 67   close(sockfd);//5. Close the socket
 68 }
 69 

 

UDP client program: use C + + to encapsulate a udpsocket class, and provide a simple interface to implement a client / server

  1 #include <cstdio>                                                                                                                                                   
  2 #include <unistd.h>
  3 #include <string.h>
  4 #include <netinet/in. h> / / SOCKADDR structure / IPPROTO_UDP
  5 #include <arpa/inet. h> / / contains some interfaces for byte order conversion
  6 #include <sys/socket. h> / / socket interface header file
  7 #include <iostream>
  8 #include <stdlib. h> / / ATOI interface
  9 //Encapsulate a UDP socket class to provide a simple interface to implement socket programming
 10 class UdpSocket{
 11 
 12   public:
 13     UdpSocket():_sockfd(-1){
 14 
 15     }
 16     //1. Create socket
 17     bool Socket(){
 18       //Socket (address domain, socket type, protocol type);
 19         _sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
 20       if(_sockfd<0){
 21         perror("socket error");
 22         return false;
 23       }
 24       return true;
 25     }
 26 
 27     //2. Bind address information for socket
 28     bool Bind(const std::string &ip,uint32_t port){
 29       //(1). Define IPv4 address structure
 30       struct sockaddr_in addr;
 31       addr.sin_family=AF_INET;//The address field is used to indicate to the bind interface that this is an ipv4 address structure
 32       addr.sin_port=htons(port);//htons converts a 2-byte host byte order integer to a network byte order integer
 33       addr.sin_addr.s_addr=inet_addr(ip.c_str());//IP address of network byte order
 34       /*
 35       //bind Assignment information of three parameters of the interface
 36       struct sockaddr_in{
 37       sa_family_t sin_family;
 38       in_port_t sin_port;
 39       struct in_addr{
 40       in_addr_t _addr;
 41       }sin_addr;
 42       }
 43       */
 44       //(2). Binding address
 45       socklen_t len=sizeof(struct sockaddr_in);
 46       //Bind (descriptor, uniform address structure, sockaddr *, address information length)
 47       int ret=bind(_sockfd,(struct sockaddr*)&addr,len);
 48       if(ret<0){
 49         perror("bind error");
 50         return false;
 51       }
 52       return true;
 53     }
 54 
 55     //3. Send data
 56     bool Send(const std::string &data,const std::string &ip,uint16_t port){
 57       //SendTo (descriptor, data, length, options, peer address, address length)
 58       //(1). Address structure of ipv4 that defines peer address information
 59       struct sockaddr_in addr;
 60       addr.sin_family=AF_INET;//The address field is used to indicate to the bind interface that this is an ipv4 address structure
 61       addr.sin_port=htons(port);//htons converts a 2-byte host byte order integer to a network byte order integer
 62       addr.sin_addr.s_addr=inet_addr(ip.c_str());//IP address of network byte order
 63       //(2). Send data to this address
 64       int ret;
 65       socklen_t len=sizeof(struct sockaddr_in);
 66       ret=sendto(_sockfd,data.c_str(),data.size(),0,(struct sockaddr*)&addr,len);
 67       if(ret<0){
 68         perror("sendto error");
 69         return false;                                                                                                                                               
 70       }
 71       return true;
 72     }
 73 
 74     //4. Send data
 75     //Input parameters use const references
 76     //Output parameters use pointers
 77     //Use reference for input and output lines
 78     bool Recv(std::string *buf,std::string *ip=NULL,uint16_t *port=NULL){
 79       //Recvfrom (descriptor, buffer, data length, options, peer address, address length)
 80       struct sockaddr_in addr;//Used to obtain sender address information
 81       socklen_t len=sizeof(struct sockaddr_in);//Specify the address length and get the actual address length
 82       int ret;
 83       char tmp[4096]={0};//A buffer used temporarily to hold data
 84       ret=recvfrom(_sockfd,tmp,4096,0,(struct sockaddr*)&addr,&len);
 85       if(ret<0){
 86         perror("recvfrom error");
 87         return -1;
 88       }
 89       buf->assign(tmp,ret);//Apply for ret size space for buf and copy ret length data from tmp
 90       //For the sake of flexible interface, if the user does not want to obtain the address information, it will not be converted
 91       Only when the user wants to get the address, the buffer is passed in, and we write the data in
 92       if(ip!=NULL){
 93         *ip=inet_ntoa(addr.sin_addr);//Converts the network byte order integer IP address to a string address and returns
 94       }
 95       if(port!=NULL){
 96         *port=ntohs(addr.sin_port);
 97       }
 98       return true;
 99     }
100 
101     //5. Close the socket
102     void Close(){
103       close(_sockfd);
104       _sockfd=-1;
105       return;
106     }                                                                                                                                                               
107   private:
108     //Socket descriptor throughout the full text
109     int _sockfd;
110 };
111 112 #define CHECK_RET(q) if((q)==false){return -1;}
113 //If the client wants to send data to the server, it needs to know the address information of the server
114 Therefore, the address information of the server is passed in through the program running parameters
115 int main(int argc,char *argv[]){
116   if(argc!=3){//Indicates the number of parameters. See whether the input parameter is 3
117     printf("em: ./udp_cli 192.168.122.132 9000\n");
118     return -1;
119 
120   }
121 
122   //argv[0]=./udp_cli
123   //argv[1]=192.168.122.132
124   //argv[2]=9000
125   std::string ip_addr=argv[1];//Server address information
126   uint16_t port_addr=atoi(argv[2]);
127   UdpSocket sock;
128   CHECK_RET(sock.Socket());//Create socket
129   //CHECK_RET(sock.Bind());// Binding address information
130   while(1){
131     //Get a standard input data and send it
132     std::cout<<"client say: ";
133     fflush(stdout);
134     std::string buf;
135     std::cin>>buf;//Get standard input data
136     sock.Send(buf,ip_addr,port_addr);//Sends buf data to the specified host process
137 
138     buf.clear();//Empty buf buffer
139     sock.Recv(&buf);//Because the client itself knows the address of the server, it does not need to obtain it in the
140     std::cout<<"server say: "<<buf<<std::endl;
141   }
142   sock.Close();
143   return 0;
144 }                     


TCP programming process: connection oriented, reliable transmission, byte stream oriented, UDP is the opposite, but both are (duplex communication)

Server:

1. Create socket -- create socket structure

2. Bind the address information for the socket (tell the operating system which data is put into my socket buffer and which source address information is used for sending data)

3. Start listening (a link needs to be established before communication at both ends of TCP - ensure that both sides of the communication have the ability to receive / send data; the process of establishing a connection is completed by the operating system, and the user does not need to care; starting listening is equivalent to telling the operating system that it can start receiving the connection request of the client)

(1) the socket created by the service can receive the connection request from the client

(2) when the client connection request arrives, the server will create a separate socket for the client

Description in socket structure (source IP / source port / peer IP / peer port / protocol...); These descriptions indicate that the socket is used specifically to communicate with the client

The server will create a socket for each client to communicate with the client (different from UDP)

The earliest socket creation: listening socket -- like door greeting -- always only receives connection requests from clients

Create a new socket: the communication socket -- subsequent data communication with the client -- is like a one-to-one waiter communicating with the client. How many clients means how many socket structures correspond to them

4. Get the operation handle descriptor of the new socket in the server program

Because subsequent communication with this client is completed through this operation handle

The earliest socket descriptor created -- the operation handle is only used to establish a connection and obtain a new connection

5. Sending and receiving data: because TCP socket describes both the source end and the opposite end, it is not necessary to obtain / specify the address information of the opposite end when sending and receiving data. (different from UDP)

6. Close socket: release resources

If the UDP server does not exist, it can still send data, and TCP directly rejects the connection (different from UDP)

 

client:

1. Create socket

2. Binding address information (not recommended)

3. Send a connection request to the server

4. Sending and receiving data

5. Close the socket

TCP client and server processes:

Client: create socket, describe address information, initiate connection request, connect successfully, send and receive data, close

Server: create a socket, describe the address information, start listening, receive connection requests, create a new socket, obtain the new socket descriptor, communicate with the client through this descriptor, and close.

Introduction to TCP programming socket interface:

1. Create socket: int socket(int domain, int type, int protocol); (AF_INET,SOCK_STREAM -- streaming socket, IPPROTO_TCP)

2. Binding address information: int bind (int sockfd,syruct sockaddr *addr,socklen_t len);    struct sockaddr_in;

3. The server starts listening: int listen(int sockfd,int backlog)--- Tell the operating system to start receiving connection requests

backlog: determines the number of client connection requests that the server can receive at the same time - the maximum number of nodes,

However, it does not determine how many client parameters the server can receive.

In case of a network attack: SYN flooding attack: malicious hosts constantly send a large number of connection requests to the server host

If the server establishes a socket for each connection request, the resources will be exhausted and the server will crash

Therefore, the server has a connection pending queue; Store the newly created socket node for connection request; The backlog parameter determines the maximum number of nodes in the queue; If queue slows down, and if new connection requests arrive, subsequent requests are discarded.

4. Get the operation handle of the new socket: take a socket from the pending queue of the socket specified by the kernel and return the operation handle

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

sockfd: listening socket -- specifies which socket in the pending queue to get

Addr: obtain a socket, which communicates with the specified client, and obtain the address information of the client through addr

len: input / output type parameters --- specify the desired length of address information and return the actual address length

Return value: if successful, the descriptor of the newly obtained socket --- operation handle is returned

5. Communicate with the specified client through the newly obtained socket operation handle (descriptor returned by accept)

Received data: ssize_t recv(int sockfd,char *buf,int len,int flag); Return value: successfully returns the actual read data length; When the connection is disconnected, it returns 0; Reading failed, return - 1--- The TCP connection is disconnected, just like the pipeline reading data, all write segments are closed, and the reader returns 0

Send data: ssize_t send(int sockfd,char *data,int len,int flag); Return value: the length of the actually sent data is successfully returned; Failure returns - 1; If the connection is disconnected, an exception is triggered

6. Close socket: release resource int close (int fd)

7. The client sends a connection request to the server

int connect(int sockfd,int sockaddr *addr,socklen_t len);

sockfd: client socket --- if the address is not bound, the operating system will select the appropriate source address for binding

addr: server address information -- struct sockaddr_in; The address information will also be described in the socket after connect ing

len: length of address information

Byte order conversion interface:

uint6_t htons(uint16_t port);//2 bytes of data

uint6_t ntohs(uint16_t port);// Host byte order and network byte order

uint32_t htonl(uint32_t port);//4 bytes of data

uint32_t ntohl(uint32_t port);// Host byte order and network byte order

in_addr_t inet_addr(char *ip);// Convert dotted decimal string IP address to network byte order integer IP address

char *inet_ntoa(struct in_addr addr);// Convert network byte order integer IP address to dotted decimal string

int inet_pton(int domain,char *ip,void *ip);// String address translation, network byte order, integer address, domain address field

int inet_ntop(int domain,void *ip,char *ip,int len);// Convert network byte order integer IP address to dotted decimal string

The implementation code is as follows: including tcpsocket hpp 

    1 #include <cstdio>                                                                                                      
    2 #include <unistd.h>
    3 #include <string.h>
    4 #include <netinet/in. h> / / SOCKADDR structure / IPPROTO_UDP
    5 #include <arpa/inet. h> / / contains some interfaces for byte order conversion
    6 #include <sys/socket. h> / / socket interface header file
    7 #include <iostream>
    8 #include <stdlib. h> / / ATOI interface
    9 //Encapsulate and implement a tcpsocket class to provide a simple interface to the outside world
   10 //The tcp communication program can be established by instantiating a tcpsocket object
   11 #define BACKLOG 10
   12 #define CHECK_RET(q)  if((q)==false){ return -1;}
   13 class TcpSocket
   14 {
   15   public:
   16     TcpSocket():_sockfd(-1){
   17 
   18     }
   19     //1. Create socket
   20     bool Socket(){
   21       //Socket (address domain, socket type, protocol type)
   22       _sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
   23       if(_sockfd<0){
   24         perror("socket create error");
   25         return false;
   26       }
   27       return true;
   28     }
   29     void Addr(struct sockaddr_in *addr,const std::string &ip,uint16_t port){
   30       addr->sin_family=AF_INET;
   31       addr->sin_port=htons(port);
   32       inet_pton(AF_INET,ip.c_str(),&(addr->sin_addr.s_addr));
   33     }
   34     //2. Binding address information
   35     bool Bind(const std::string &ip,const uint16_t port){
   36       //(1) Define IPv4 address structure
   37       struct sockaddr_in addr;
   38       Addr(&addr,ip,port);
   39       socklen_t len=sizeof(struct sockaddr_in);
   40       int ret=bind(_sockfd,(struct sockaddr*)&addr,len);
   41       if(ret<0){
   42         perror("bind error");
   43         return false;
   44       }
   45       return true;
   46     }
   47     //3. The server starts listening
   48     bool Listen(int backlog=BACKLOG){
   49       //Listen (descriptor, number of concurrent connections at the same time);
   50       int ret=listen(_sockfd,backlog);
   51       if(ret<0){
   52         perror("listen error");
   53         return false;
   54       }
   55       return true;
   56     }
   57     //4. The client initiates a connection request
   58     bool Connect(const std::string &ip, const uint16_t port){
   59       //(1) Define the IPv4 address structure and give the server address information
   60       struct sockaddr_in addr;
   61       Addr(&addr,ip,port);
   62       //(2) Send a request to the server
   63       //(3) Conect (client descriptor, server address information, address length)
   64       socklen_t len=sizeof(struct sockaddr_in);
   65       int ret=connect(_sockfd,(struct sockaddr*)&addr,len);
   66       if(ret<0){
   67         perror("connect error");
   68         return false;
   69       }
   70       return true;
   71     }
   72     //5. The server obtains the new connection
   73     bool Accept(TcpSocket *sock,std::string *ip=NULL,uint16_t *port=NULL){
   74       //Accept (listening socket, peer address information, address information length) returns a new descriptor
   75       struct sockaddr_in addr;
   76       socklen_t len=sizeof(struct sockaddr_in);
   77       //Get the new socket and the corresponding peer address information of the socket
   78       int clisockfd=accept(_sockfd,(struct sockaddr*)&addr,&len);
   79       if(clisockfd<0){
   80         perror("accept error");
   81         return false;
   82       }
   83 
   84       //Assign a value to the descriptor of this object -- assign a value to the descriptor of the new socket
   85       Subsequent communication with the client can be completed through this object
   86       sock->_sockfd=clisockfd;//The user passed in a pointer to the Tcpsocket object
   87       if(ip!=NULL){
   88         *ip=inet_ntoa(addr.sin_addr);//Converts the network byte order integer IP address to a string address and returns
   89       }
   90       if(port!=NULL){
   91         *port=ntohs(addr.sin_port);
   92       }
   93       return true;
   94     }
   95     //6. Send data
   96     bool Send(const std::string &data){
   97       //Send (descriptor, data, data length, option parameter)
   98       int ret=send(_sockfd,data.c_str(),data.size(),0);
   99       if(ret<0){
  100         perror("send error");
  101         return false;
  102       }
  103       return true;
  104     }
  105     //7. Receive data
  106     bool Recv(std::string *buf){
  107       //Recv (descriptor, buffer, data length, option parameters)
  108       char tmp[4096]={0};
  109       int ret=recv(_sockfd,tmp,4096,0);
  110       if(ret<0){
  111         perror("recv error");
  112         return false;
  113       }else if(ret==0){
  114         printf("connection break\n");
  115         return false;                                                                                                  
  116       }
  117       buf->assign(tmp,ret);//Copy ret size data from tmp to buf
  118       return true;
  119     }
  120     //8. Close the socket
  121     bool Close(){
  122       close(_sockfd);
  123       _sockfd=-1;
  124     }
  125   private:
  126     int _sockfd;
  127 };                                                                                                                     

tcp_cli.cpp

 1 #include <iostream>
  2 #include "tcpsocket.hpp"
  3 #include <stdlib.h>
  4 
  5 //Using encapsulated Tcpsocket class instantiation object to implement tcp server program
  6 
  7 int main(int argc,char *argv[]){
  8   if(argc!=3){//Indicates the number of parameters. See whether the input parameter is 3
  9     printf("em: ./tcp_cli 192.168.122.132 9000--Address of service binding\n");
 10     return -1;
 11   }
 12   std::string ip=argv[1];
 13   uint16_t port =atoi(argv[2]);//stoi converts strings to numbers                                                                  
 14 
 15   TcpSocket cli_sock;
 16   //Create socket
 17   CHECK_RET(cli_sock.Socket());
 18   //Binding address information (not recommended)
 19   //CHECK_RET(cli_sock.Bind(ip,port));
 20   //Send a request to the server
 21   CHECK_RET(cli_sock.Connect(ip,port));
 22   //Cyclic transceiver data
 23   while(1){
 24     printf("client say:");
 25     fflush(stdout);
 26     std::string buf;
 27     std::cin>>buf;
 28 
 29     //Because the client does not have files for multiple sockets, it is OK to exit directly in case of an error in the current socket
 30     //When the process exits, it will release resources and close the socket
 31     CHECK_RET(cli_sock.Send(buf));
 32     buf.clear();
 33     CHECK_RET(cli_sock.Recv(&buf));
 34     printf("server say:%s\n",buf.c_str());
 35   }
 36   cli_sock.Close();
 37   return 0;
 38 }
~

 tcp_srv.cpp

                                                                      
  1 #include <iostream>
  2 #include <stdlib.h>
  3 #include "tcpsocket.hpp"
  4 
  5 
  6 //Using encapsulated Tcpsocket class instantiation object to implement tcp server program
  7 
  8 int main(int argc,char *argv[]){
  9   if(argc!=3){//Indicates the number of parameters. See whether the input parameter is 3
 10     printf("em: ./tcp_srv 192.168.122.132 9000\n");
 11     return -1;
 12   }
 13   std::string ip=argv[1];
 14   uint16_t port =atoi(argv[2]);//stoi converts strings to numbers                                                                  
 15 
 16   TcpSocket lst_sock;
 17   CHECK_RET(lst_sock.Socket());//Create socket
 18   CHECK_RET(lst_sock.Bind(ip,port));//Binding address information
 19   CHECK_RET(lst_sock.Listen());//Start listening
 20   while(1){
 21     TcpSocket cli_sock;
 22     std::string cli_ip;
 23     uint16_t cli_port;
 24     //accept class member function, private member used_ sockfd is lst_ Private member of sock
 25     cli_sock The address is passed in to obtain accept Communication socket descriptor returned by interface
 26     bool ret=lst_sock.Accept(&cli_sock,&cli_ip,&cli_port);//Get new socket
 27     if(ret==false){
 28       //Failed to get a new connection. You can continue to get the next one again
 29       continue;
 30     }
 31     std::string buf;
 32     if( cli_sock.Recv(&buf)==false){
 33 
 34       cli_sock.Close();//Error in receiving data from communication socket. Communication socket is closed
 35       continue;
 36     }
 37     printf("client:[%s:%d] say:%s\n",&cli_ip[0],cli_port,&buf[0]);
 38     std::cout<<"server say:";
 39     fflush(stdout);
 40     buf.clear();
 41     std::cin>>buf;
 42     if(cli_sock.Send(buf)==false){
 43       cli_sock.Close();
 44       continue;
 45     }
 46   }
 47   lst_sock.Close();
 48   return 0;
 49 }
~

Why can the TCP server only communicate with one client once and not continuously-- Process blocking

1. Server program blocking location: accept to obtain a new connection

2. Server program blocking location: recv/send at the communication with the client. If there is no data in the receiving buffer, the recv blocking solution

while(1){

1. Get new connection -- accept interface is a blocking function -- if there is no new connection, block and wait

2. Communicate with the client through a new connection

ssize_t ret=recv();-- receive data

ssize_t ret=send();-- send data

}

Take an example to illustrate the process of process blocking:

Why can the TCP server only communicate with one client once and not continuously? Solutions:

To prevent process blocking, it is necessary to ensure that an execution flow is only responsible for one function. An execution flow only obtains a new connection. When the new connection is successful, it creates a new execution flow to communicate with the client. Multi execution flow solution (multi process and multi thread)

Multi process: more stable

1. The parent process creates a child process with unique data, each with a cli_sock; However, the child process passes through cli_sock communication, but the parent process does not need it, so the parent process closes its cli_sock;

2. The parent process should wait for the child process to exit to avoid zombie processes; In order that the parent process is only responsible for obtaining new connections, the callback waiting is customized for SIGCHLD signal processing.

Orphan process: the parent process exits before the child process. The child process becomes an orphan process and runs in the background. The parent process becomes init process 1

Zombie process: the child process exits before the parent process. When the child process exits, the operating system sends a SIGCHLD signal to the parent process to notify the parent process. Your child process exits. The processing method is ignore. Therefore, the parent process does not pay attention to the exit state of the child process, and the child process becomes rigid

    1 #include <iostream>                                                                                                                                                                  
    2 #include <stdlib.h>
    3 #include "tcpsocket.hpp"
    4 #include <signal.h>
    5 #include <sys/wait.h>
    6 
    7 //Using encapsulated Tcpsocket class instantiation object to implement tcp server program
    8 
W>  9 void sigcb(int signo){
   10   //When the child process exits, it will send a sigchld signal to the parent process to call back this function,
   11   //The return value of waitpid > 0 indicates that an exiting child process has been processed
   12   //Waitpid < = 0 indicates that there are no child processes exiting
   13   while(waitpid(-1,0,WNOHANG)>0);//A callback loop will handle all the child processes that exit
   14 }
   15 int main(int argc,char *argv[]){
   16   if(argc!=3){//Indicates the number of parameters. See whether the input parameter is 3
   17     printf("em: ./tcp_srv 192.168.122.132 9000\n");
   18     return -1;
   19   }
   20   std::string ip=argv[1];
   21   uint16_t port =atoi(argv[2]);//stoi converts strings to numbers
   22 
   23   signal(SIGCHLD,sigcb);//If a child process exits, it will receive SIGCHLD and call back sigcb
   24   TcpSocket lst_sock;
   25   CHECK_RET(lst_sock.Socket());//Create socket
   26   CHECK_RET(lst_sock.Bind(ip,port));//Binding address information
   27   CHECK_RET(lst_sock.Listen());//Start listening
   28   while(1){
   29     TcpSocket cli_sock;
   30     std::string cli_ip;
   31     uint16_t cli_port;
   32     bool ret=lst_sock.Accept(&cli_sock,&cli_ip,&cli_port);//Get new socket
   33     if(ret==false){
   34       continue;
   35     }
   36     printf("new connnect:[%s:%d]\n",cli_ip.c_str(),cli_port);
   37     //---------------------------------------------------------------
   38     pid_t pid=fork();
   39     if(pid==0){//Child processes copy parent processes -- all data -- code sharing
   40 
   41       //Let the child process process communicate with the client
   42       while(1){
   43         std::string buf;
   44         if( cli_sock.Recv(&buf)==false){
   45 
   46           cli_sock.Close();//Error in receiving data from communication socket. Communication socket is closed
   47           exit(0);
   48         }
   49         printf("client:[%s:%d] say:%s\n",&cli_ip[0],cli_port,&buf[0]);
   50         std::cout<<"server say:";
   51         fflush(stdout);
   52         buf.clear();
   53         std::cin>>buf;
   54         if(cli_sock.Send(buf)==false){
   55           cli_sock.Close();
   56           exit(0);
   57         }
   58       }
   59       cli_sock.Close();
   60       exit(0);
   61     }
   62     //The parent-child process data is unique and will have cli_sock, but the parent process does not communicate
   63     cli_sock.Close();//This shutdown has no impact on the child process, because each has a copy of the data to avoid resource leakage
   64     wait(NULL);//Wait for the child process to exit to prevent zombie processes
   65   }
   66   lst_sock.Close();
   67   return 0;
   68 }                                                                                                                                                                                    

Corpse process. The parent process needs to wait for the child process, obtain the return value and release the child process resources.

Multithreading: more flexible, (the cost of using threads is low, and the cost of resources, scheduling, creation and destruction)

1. The main thread obtains a new connection and then creates a new thread to communicate with the client, but the socket descriptor needs to be passed into the thread execution function

2. However, when this descriptor is passed in, the address of the local variable cannot be used (the space of the local variable will be released after the loop is completed). The value of the descriptor or the new object can be passed in

3. In C + +, there are many restrictions on strong type conversion and passing data values as pointers. Try to overcome them.

4. Cli is not applicable in the main thread_ Sock, but cli cannot be closed_ Sock, because resources are shared between threads, one thread is released and the other thread cannot be used.

cli_ The sock is an object instantiated by the TcpSocket class_ sock_fd is cli_ A member variable in the socket -- used to save the file descriptor returned by creating the socket

cli_sock.Recv(buf) receives data, and recv uses cli internally_ sockfd of sock receives data

   1 #include <iostream>
    2 #include <stdlib.h>
    3 #include "tcpsocket.hpp"
    4 #include <sys/wait.h>
    5 #include <pthread.h>
    6 
    7 //Using encapsulated Tcpsocket class instantiation object to implement tcp server program
    8 
    9 void *thr_start(void *arg){
   10   long fd=(long)arg;
   11   TcpSocket cli_sock;
   12   cli_sock.SetFd(fd);
   13   while(1){
   14     std::string buf;
   15     if( cli_sock.Recv(&buf)==false){
   16 
   17       cli_sock.Close();//Error in receiving data from communication socket. Communication socket is closed
   18       pthread_exit(NULL);//Thread exit, exit(0) process exit
   19     }
   20     printf("client:say:%s\n",&buf[0]);
   21     std::cout<<"server say:";
   22     fflush(stdout);
   23     buf.clear();
   24     std::cin>>buf;
   25     if(cli_sock.Send(buf)==false){
   26       cli_sock.Close();
   27       pthread_exit(NULL);                                                                                                                                                            
   28     }
   29   }
   30     return NULL;
   31     cli_sock.Close();
   32 }
   33 int main(int argc,char *argv[]){
   34   if(argc!=3){//Indicates the number of parameters. See whether the input parameter is 3
   35     printf("em: ./tcp_srv 192.168.122.132 9000\n");
   36     return -1;
   37   }
   38   std::string ip=argv[1];
   39   uint16_t port =atoi(argv[2]);//stoi converts strings to numbers
   40 
   41   TcpSocket lst_sock;
   42   CHECK_RET(lst_sock.Socket());//Create socket
   43   CHECK_RET(lst_sock.Bind(ip,port));//Binding address information
   44   CHECK_RET(lst_sock.Listen());//Start listening
   45   while(1){
   46     TcpSocket cli_sock;
   47     std::string cli_ip;
   48     uint16_t cli_port;
   49     //accept class member function, private member used_ sockfd is lst_ Private member of sock
   50     cli_sock The address is passed in to obtain accept Communication socket descriptor returned by interface
   51     bool ret=lst_sock.Accept(&cli_sock,&cli_ip,&cli_port);//Get new socket
   52     if(ret==false){
   53       //Failed to get a new connection. You can continue to get the next one again
   54       continue;
   55     }
   56     printf("new connnect:[%s:%d]\n",cli_ip.c_str(),cli_port);
   57     //----------------------------------------------------------------------
   58     pthread_t tid;
   59     //The communication socket is passed to the thread as a parameter to let the thread communicate with the client
   60     //cli_sock is a local variable -- the resource will be released after the loop is completed
   61     pthread_create(&tid,NULL,thr_start,(void*)cli_sock.GetFd());//Create thread
   62     pthread_detach(tid);//Do not care about the return value of the thread, separate the thread, and then automatically release the resources after exiting
   63    //The main thread cannot close cli_sock socket, because multithreads share descriptors
   64   }
   65   lst_sock.Close();
   66   return 0;
   67 }                                                                                                                                                                                    

The performance of connection disconnection at the sender and receiver (the same as the pipeline read-write characteristics):

1. Receiving end: if the connection is disconnected, recv returns 0; Conversely, if recv returns 0, it indicates that the connection is disconnected (socket write end is closed - duplex communication)

2. Sender: if the connection is disconnected, send triggers an exception, causing the process to exit

The Conduit:

If all the read ends of the pipeline are closed and writing continues, a SIGPIPE exception will be triggered

All write ends of the pipeline are closed, and 0 is returned when reading is continued

Summary:

1.udp programming process and interface:

Client: socket/sendto/recvfrom/close

Server: socket/bind/recvfrom/sendto/close

2.tcp programming process and interface:

Client: socket/connect/send/recv/close

Server: socket/bind/listen/accept/recv/send/close

3. Byte order conversion interface:

htons/ntohs/htonl/ntohl/inet_addr/inet_ntoa/inet_ntop/inet_pton

4. Multi execution flow tcp server process

Multi execution flow idea (multi process, multi thread)

Topics: socket Multithreading udp TCPIP osi