day21 network programming (Part 2)
Course objective: learn the necessary knowledge of network programming development.
Today's summary:
- OSI7 layer model
- TCP and UDP
- Sticky bag
- Blocking and non blocking
- IO multiplexing
1. OSI layer 7 model
The 7-layer OSI model may not be easy to understand, so let's explain it through a case:
Suppose you enter some keywords in the browser, find the corresponding IP through DNS, and then send the data. The internal will do the following:
-
Application layer: Specifies the format of data.
"GET /s?wd=Hello HTTP/1.1\r\nHost:www.baidu.com\r\n\r\n"
-
Presentation layer: coding, compression (decompression), blocking, encryption (decryption) and other tasks of application layer data.
"GET /s?wd=Hello HTTP/1.1\r\nHost:www.baidu.com\r\n\r\n Hello".encode('utf-8')
-
Session layer: responsible for establishing and interrupting the connection with the target.
Before sending data, you need to send a "connection" request first, and then send data after establishing a connection with the remote. Of course, after sending, it also involves the operation of disconnecting.
-
Transport layer: establishing port to port communication actually determines the port information of both sides.
Data:"GET /s?wd=Hello HTTP/1.1\r\nHost:www.baidu.com\r\n\r\n Hello".encode('utf-8') Port: - Target: 80 - Local: 6784
-
Network layer: mark target IP information (IP protocol layer)
Data:"GET /s?wd=Hello HTTP/1.1\r\nHost:www.baidu.com\r\n\r\n Hello".encode('utf-8') Port: - Target: 80 - Local: 6784 IP: - target IP: 110.242.68.3(Baidu) - local IP: 192.168.10.1
-
Data link layer: group data and set source and destination mac addresses
Data:"POST /s?wd=Hello HTTP/1.1\r\nHost:www.baidu.com\r\n\r\n Hello".encode('utf-8') Port: - Target: 80 - Local: 6784 IP: - target IP: 110.242.68.3(Baidu) - local IP: 192.168.10.1 MAC: - target MAC: FF-FF-FF-FF-FF-FF - Local machine MAC: 11-9d-d8-1a-dd-cd
-
Physical layer: transfer binary data on physical media.
Send binary data through network cable
Each layer performs its own duties and ultimately ensures that the data is presented to users.
It can be simply understood as express delivery: 7 boxes are set outside the data. When the end user receives the box, he needs to open 7 boxes to get the data. During transportation, some boxes will be disassembled and replaced, for example:
Final shipping target: Shanghai ~ In Beijing (a transfer station may be required on the way), the box will be opened at the transfer station to view the information and forward it. - For secondary transfer station (layer-2 switch): open the box of the data link layer and view it mac Address information. - For level-3 transfer station (router or layer-3 switch): open the box of the network layer and view it IP Information.
In fact, it can only be reflected in the development process: application layer, presentation layer, session layer and transport layer. The processing of other layers is automatically completed in network equipment.
import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('110.242.68.3', 80)) # Sent a packet to the server key = "Hello" # application layer content = "GET /s?wd={} http1.1\r\nHost:www.baidu.com\r\n\r\n".format(key) # Presentation layer content = content.encode("utf-8") client.sendall(content) result = client.recv(8196) print(result.decode('utf-8')) # Session layer & transport layer client.close()
2. UDP and TCP protocols
The protocol is actually some provisions for connecting and sending and receiving data.
In the OSI transport layer, in addition to defining port information, UDP or TCP protocols can also be specified. The details of connection and data transmission will be different according to different protocols.
-
UDP (User Data Protocol) user datagram protocol is a simple transport layer protocol for connecting to datagrams. UDP does not provide reliability. It just sends the datagrams transmitted by the application to the IP layer, but it does not guarantee that they can reach the destination. Because UDP does not establish a connection between the client and the server before transmitting datagrams, and there is no timeout retransmission mechanism, the transmission speed is very fast.
Common are: voice call, video call, real-time game screen, etc.
-
TCP (Transmission Control Protocol) is a connection oriented protocol, that is, before sending and receiving data, you must establish a reliable connection with the other party, and then send and receive data.
Common: website, mobile phone APP Data acquisition, etc.
2.1 UDP and TCP sample code
UDP examples are as follows:
UDP does not need to establish a connection.
-
Server
import socket server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# socket.SOCK_DGRAM UDP and TCP protocols, this parameter is different server.bind(('127.0.0.1', 8002)) while True: data, (host, port) = server.recvfrom(1024) # block print(data, host, port) server.sendto("well".encode('utf-8'), (host, port))
-
client
import socket client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) while True: text = input("Please enter the content to send:") if text.upper() == 'Q': break client.sendto(text.encode('utf-8'), ('127.0.0.1', 8002)) data, (host, port) = client.recvfrom(1024) print(data.decode('utf-8')) client.close()
TCP examples are as follows:
-
Server
import socket # 1. Listen to the local IP and port sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8001)) sock.listen(5) while True: # 2. Wait for someone to connect (blocking) conn, addr = sock.accept() # 3. Wait, the connector sends a message (blocking) client_data = conn.recv(1024) print(client_data) # 4. Reply to the connector conn.sendall(b"hello world") # 5. Close the connection conn.close() # 6. Stop the server program sock.close()
-
client
import socket # 1. Send a connection request to the specified IP client = socket.socket() client.connect(('127.0.0.1', 8001)) # 2. After the connection is successful, send a message. What does b mean here? [bytes like object] # Error without b: TypeError: a bytes like object is required instead of 'str' client.sendall(b'hello') # 3. Wait for the reply of the message (blocking) reply = client.recv(1024) print(reply) # 4. Close the connection client.close()
2.2 TCP three handshakes and four waves
ACK:It is used to confirm the received data. The confirmed data is represented by the confirmation serial number. SYN:Used for synchronization signal when establishing connection. FIN:It means that there is no data to be sent later, which usually means that the established connection needs to be closed. for the first time: A client error occurred while establishing a connection syn package(seq=j) Go to the server and enter syn_sent(Half connected, synchronous (sent) status, waiting for server confirmation; syn: Synchronization sequence number. The second time: Server received syn Package, the client must be confirmed syn(ack=j+1),At the same time, it also happened syn package(seq=k),Namely syn+ack Package, and the server enters syn_recv Status.(syn_recv It refers to that after the server is opened passively, it receives a message from the client syn And sent ack Status of the.) third time: Client received from server syn+ack Package and confirm the package to the server ack(ack=k+1),After the package occurs, the client and server enter ESTABLISHED(TCP The connection is successful) status, and three handshakes are completed. ESTABLISHED((formally established)
This is a common interview question.
What is? tcp Three handshakes? Why do you shake hands three times when you connect and four times when you disconnect? Why three handshakes? It is mainly for information peer-to-peer and preventing dirty connections caused by request timeout.
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
If both parties in the network want to communicate based on TCP connection, they must go through:
-
To create a connection, the client and server should shake hands three times.
# Server import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8001)) sock.listen(5) while True: conn, addr = sock.accept() # Waiting for client connection ...
# client import socket client = socket.socket() client.connect(('127.0.0.1', 8001)) # Initiate connection
client Server 1. SYN-SENT --> <seq=100><CTL=SYN> --> SYN-RECEIVED 2. ESTABLISHED <-- <seq=300><ack=101><CTL=SYN,ACK> <-- SYN-RECEIVED 3. ESTABLISHED --> <seq=101><ack=301><CTL=ACK> --> ESTABLISHED At this point, both the client and server have received an acknowledgment of the connection. The steps 1, 2 establish the connection parameter (sequence number) for one direction and it is acknowledged. The steps 2, 3 establish the connection parameter (sequence number) for the other direction and it is acknowledged. With these, a full-duplex communication is established.
-
Transmit data
In the process of sending and receiving data, there will be a response only when there is data transmission( ack),without ack,Then the internal will try to send repeatedly.
-
Close the connection, and the client and server should wave four times.
import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8001)) sock.listen(5) while True: conn, addr = sock.accept() ... conn.close() # Close connection sock.close()
import socket client = socket.socket() client.connect(('127.0.0.1', 8001)) ... client.close() # Close connection
TCP A TCP B 1. FIN-WAIT-1 --> <seq=100><ack=300><CTL=FIN,ACK> --> CLOSE-WAIT 2. FIN-WAIT-2 <-- <seq=300><ack=101><CTL=ACK> <-- CLOSE-WAIT 3. TIME-WAIT <-- <seq=300><ack=101><CTL=FIN,ACK> <-- LAST-ACK 4. TIME-WAIT --> <seq=101><ack=301><CTL=ACK> --> CLOSED
3. Sticking package
When two computers send and receive data, they do not directly transmit the data to each other.
- For the sender, when sending a message by sendall/send, the data is first sent to the write buffer of his own network card, and then the buffer sends the data to the read buffer of the other network card.
- For the receiver, when recv receives a message, it obtains data from the read buffer of its own network card.
Therefore, if the sender sends two pieces of information continuously and quickly, the receiver will consider it as one piece of information when reading, that is, the two packets are stuck together. For example:
# socket client (sender) import socket client = socket.socket() client.connect(('127.0.0.1', 8001)) # You can also use send, which is not recommended. When the buffer location is not enough, we can only write part of the data to the buffer, but we can't directly perceive it. We can also know it by the return value. client.sendall('Emma Playing'.encode('utf-8')) client.sendall('game'.encode('utf-8')) client.close() # socket server (receiver) import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8001)) sock.listen(5) conn, addr = sock.accept() client_data = conn.recv(1024) print(client_data.decode('utf-8')) conn.close() sock.close() # Output: Emma is playing a game. [two messages were actually sent, but the receiver did receive one message.]
How to solve the problem of sticking package?
Each time a message is sent, the message is divided into two parts: header (fixed byte length) and data. For example, in the header, the length of the following data is represented by 4 bytes.
- To send data, first send the length of the data, and then send the data (or splice it together and then send it).
- When receiving data, you can know the data length in your packet by reading 4 bytes first, and then read the data according to the length.
The header needs a number and is fixed to 4 bytes. This function can be realized with the help of python's struct package:
import struct # ########### The value is converted to a fixed range of 4 bytes -2147483648 <= number <= 2147483647 ########### v1 = struct.pack('i', 199) print(v1) # b'\xc7\x00\x00\x00' for item in v1: print(item, bin(item)) # ########### 4 Convert bytes to numbers ########### v2 = struct.unpack('i', v1) # v1= b'\xc7\x00\x00\x00' print(v2) # (199,)
Example code:
-
Server
import socket import struct # AF_INET Internet domain SOCK_STREAM socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8001)) sock.listen(5) conn, addr = sock.accept() # Fixed read 4 bytes header1 = conn.recv(4) data_length1 = struct.unpack('i', header1)[0] # Get data byte length has_recv_len = 0 data1 = b"" # b byte while True: length = data_length1 - has_recv_len if length > 1024: lth = 1024 else: lth = length chunk = conn.recv(lth) # If the transmitted data is very long and may not be received at one time, you can calculate the length and use recv again until it is received. 1024 * 8 = 8192 data1 += chunk has_recv_len += len(chunk) if has_recv_len == data_length1: break print(data1.decode('utf-8')) # Fixed read 4 bytes header2 = conn.recv(4) data_length2 = struct.unpack('i', header2)[0] # Data byte length data2 = conn.recv(data_length2) # length print(data2.decode('utf-8')) conn.close() sock.close()
-
client
import socket import struct client = socket.socket() client.connect(('127.0.0.1', 8001)) # Article 1 data data1 = 'emma Playing'.encode('utf-8') header1 = struct.pack('i', len(data1)) client.sendall(header1) client.sendall(data1) # The second data, first send its length, and then send the data data2 = 'game'.encode('utf-8') header2 = struct.pack('i', len(data2)) client.sendall(header2) client.sendall(data2) client.close()
Case: Message & file upload
-
Server
import os import json import socket import struct def recv_data(conn, chunk_size=1024): # Get header information: data length has_read_size = 0 bytes_list = [] # Byte list while has_read_size < 4: chunk = conn.recv(4 - has_read_size) has_read_size += len(chunk) bytes_list.append(chunk) header = b"".join(bytes_list) data_length = struct.unpack('i', header)[0] # Replace with integer # get data data_list = [] has_read_data_size = 0 while has_read_data_size < data_length: size = chunk_size if (data_length - has_read_data_size) > chunk_size else data_length - has_read_data_size chunk = conn.recv(size) data_list.append(chunk) has_read_data_size += len(chunk) data = b"".join(data_list) return data def recv_file(conn, save_file_name, chunk_size=1024): save_file_path = os.path.join('files', save_file_name) # Get header information: data length, read fixed four bytes, and use unpack to convert to integer has_read_size = 0 bytes_list = [] while has_read_size < 4: chunk = conn.recv(4 - has_read_size) # Subtract the data that has been read bytes_list.append(chunk) has_read_size += len(chunk) header = b"".join(bytes_list) # join string splicing, splicing empty bytes and bytes in the byte list??? data_length = struct.unpack('i', header)[0] # get data file_object = open(save_file_path, mode='wb') has_read_data_size = 0 while has_read_data_size < data_length: # size = chunk_size if (data_length - has_read_data_size) > chunk_size else data_length - has_read_data_size chunk = conn.recv(size) file_object.write(chunk) file_object.flush() has_read_data_size += len(chunk) file_object.close() def run(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # IP can be reused. When the program exits abnormally, it may occupy IP. Using this code, IP can be reused multiple times sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1', 8001)) sock.listen(5) while True: conn, addr = sock.accept() while True: # Get message type message_type = recv_data(conn).decode('utf-8') if message_type == 'close': # Four waves, empty content. print("Close connection") break # File: {'msg_type':'file', 'file_name':"xxxx.xx"} # Message: {'msg_type':'msg'} message_type_info = json.loads(message_type) if message_type_info['msg_type'] == 'msg': data = recv_data(conn) print("Message received:", data.decode('utf-8')) else: file_name = message_type_info['file_name'] print("File received, to save to:", file_name) recv_file(conn, file_name) conn.close() sock.close() if __name__ == '__main__': run()
-
client
import os import json import socket import struct # Define functions and send text messages def send_data(conn, content): #Receive a connection: conn. check that the place where the call is made is the connection object passed through the client data = content.encode('utf-8') # Convert to byte type header = struct.pack('i', len(data)) # Get its length conn.sendall(header) # Length of data sent conn.sendall(data) # send data # Define functions and upload files def send_file(conn, file_path): file_size = os.stat(file_path).st_size # Gets the size of the file header = struct.pack('i', file_size) conn.sendall(header) # Size of data sent has_send_size = 0 file_object = open(file_path, mode='rb') while has_send_size < file_size: chunk = file_object.read(2048) # 2048 bytes read at a time conn.sendall(chunk) # Content of data sent has_send_size += len(chunk) # The loop ends when the read data is equal to the file size file_object.close() def run(): # Establish connection client = socket.socket() client.connect(('127.0.0.1', 8001)) while True: """ Please send a message in the format: - Message: msg|How do you do - File: file|xxxx.png """ content = input(">>>") #Wait for user input, msg or file if content.upper() == 'Q': send_data(client, "close") # The fourth wave, the content sent is empty break input_text_list = content.split('|') if len(input_text_list) != 2: # Separated by "|", if it is not equal to 2, the format error will be prompted print("Format error, please re-enter") continue message_type, info = input_text_list # Send a message if message_type == 'msg': # Message type send_data(client, json.dumps({"msg_type": "msg"})) # Send content send_data(client, info) # Send documents else: file_name = info.rsplit(os.sep, maxsplit=1)[-1] # os.sep parameter, automatically distinguishing system separator and / or\ # Message type send_data(client, json.dumps({"msg_type": "file", 'file_name': file_name})) # Send content send_file(client, info) client.close() if __name__ == '__main__': run()
4. Blocking and non blocking
By default, the network programming codes we write are blocked (waiting). Blocking is mainly reflected in:
# ################### socket Server (receiver)################### import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('127.0.0.1', 8001)) sock.listen(5) # block conn, addr = sock.accept() # block client_data = conn.recv(1024) print(client_data.decode('utf-8')) conn.close() sock.close() # ################### socket Client (sender) ################### import socket client = socket.socket() # block client.connect(('127.0.0.1', 8001)) client.sendall('emma Playing games'.encode('utf-8')) client.close()
If you want to make the code non blocking, you need to write this:
# ################### socket Server (receiver)################### import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setblocking(False) # Plus, it becomes non blocking, and True is blocking sock.bind(('127.0.0.1', 8001)) sock.listen(5) # Non blocking, BlockingIOError, wants to receive a client connection conn, addr = sock.accept() # Non blocking, client_data = conn.recv(1024) print(client_data.decode('utf-8')) conn.close() sock.close() # ################### socket Client (sender) ################### import socket client = socket.socket() client.setblocking(False) # Plus, it becomes non blocking # Non blocking client.connect(('127.0.0.1', 8001)) client.sendall('emma Playing games'.encode('utf-8')) client.close()
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-i6ijcyjz-164299494969) (images / image-202112211408039. PNG)]
If the code becomes non blocking, the BlockingIOError exception will be thrown when the program encounters accept, recv and connect.
This is not an error in code writing, but a fixed error thrown because no relevant IO request is received after the original IO blocking becomes non blocking.
Non blocking code is generally combined with IO multiplexing, which can play a greater role.
5. IO multiplexing
I/O multiplexing means that multiple descriptors can be monitored through a mechanism. Once a descriptor is ready (generally read ready or write ready), it can notify the program to perform corresponding read-write operations.
Note: "a descriptor is usually a number,Represents an open file,process,disk inode wait. The kernel has a large array to maintain this thing. It can also be understood that the descriptor is a in the database table id field,The resource corresponding to the descriptor is the field itself. But the database here is the kernel. "
IO multiplexing + non blocking enables the TCP server to process the requests of multiple clients at the same time, for example:
# ################### socket Server ################### import select import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setblocking(False) # Plus, it becomes non blocking server.bind(('127.0.0.1', 8001)) server.listen(5) inputs = [server, ] # socket object list - > [server, first client connection Conn, second client connection conn] while True: # When the socket object in the parameter 1 sequence is readable (accetp and read), the changed object is obtained and added to the r list. # r = [] when there is no new connection received, r is an empty list, and the code in the loop will not be executed at this time # R = [server,] when there is a new connection for the first time, the server changes. At this time, r = [server,] # r = [first client connection conn,] # r = [server,] # r = [first client connection Conn, second client connection conn] # r = [Second client connection conn,] #The second client is connected to conn. if there is no data, it will no longer listen and will be removed from the list again # Who has changed is in this list. r = [changed connection,] r, w, e = select.select(inputs, [], [], 0.05) for sock in r: # server if sock == server: conn, addr = sock.accept() # Receive new connections. print("New connection") # Conn.sendall() can send a message to the customer service terminal through conn.sendall() # conn.recv("xx") receives the message inputs.append(conn) else: # When the connection is received, sock= The server will execute the code under else data = sock.recv(1024) if data: # If the data is empty, close the connection and remove it from the list print("Message received:", data) else: print("Close connection") inputs.remove(sock) # Deal with other matters for 20s """ advantage: 1. When no new links come, you can handle other things 2. Let the server support multiple clients to connect at the same time. """
select Module parameter interpretation: select Is for many file descriptors(abbreviation fd)For monitoring, it has three parameters: rlist -- wait until ready for reading wlist -- wait until ready for writing xlist -- wait for an "exceptional condition" The first parameter monitors the incoming data fd List, select Monitor this list and wait for these fd Send data, once the data is sent(It's ready to read),It returns a readable fd list The second parameter is the of the monitored data fd List, select Monitor this list and wait for these fd Send out data once fd Ready to send(It's ready to write),Returns a writable fd list Third parameter monitoring fd List and return the exception fd list The fourth parameter is the detection time period
# ################### socket Client 1 ################### import socket client = socket.socket() # block client.connect(('127.0.0.1', 8001)) while True: content = input(">>>") if content.upper() == 'Q': break client.sendall(content.encode('utf-8')) client.close()
# ################### socket Client 2 ################### import socket client = socket.socket() # block client.connect(('127.0.0.1', 8001)) while True: content = input(">>>") if content.upper() == 'Q': break client.sendall(content.encode('utf-8')) client.close() # If you disconnect from the server (wave four times), you will want to send empty data to the server by default.
IO multiplexing + non blocking enables TCP clients to send multiple requests at the same time, such as sending a request to download pictures to a website.
import socket import select import uuid import os client_list = [] # socket object list # Create 5 socket s for i in range(5): client = socket.socket() client.setblocking(False) try: # Connect to Baidu. Although there is an abnormal BlockingIOError, the connection request is sent to Baidu normally client.connect(('47.98.134.86', 80)) except BlockingIOError as e: pass client_list.append(client) recv_list = [] # Put the socket that has successfully connected and sent the request to download the picture while True: # w = [first socket object,] # r = [socket object,] r, w, e = select.select(recv_list, client_list, [], 0.1) for sock in w: # Connect successfully, send data # Request to download pictures sock.sendall(b"GET /nginx-logo.png HTTP/1.1\r\nHost:47.98.134.86\r\n\r\n") recv_list.append(sock) client_list.remove(sock) for sock in r: # After the data is sent successfully, the received return value (picture) is written to the local file data = sock.recv(8196) content = data.split(b'\r\n\r\n')[-1] random_file_name = "{}.png".format(str(uuid.uuid4())) with open(os.path.join("images", random_file_name), mode='wb') as f: f.write(content) recv_list.remove(sock) if not recv_list and not client_list: break """ advantage: 1. Concurrency can be forged. """
Based on the characteristics of IO multiplexing + non blocking, no matter the server or client writing the socket, the performance can be improved. among
- IO multiplexing to monitor whether the socket object has changed (whether the connection is successful, whether there is data coming, etc.).
- Non blocking, socket connect and recv processes no longer wait.
Note: IO multiplexing can only be used to monitor whether IO objects have changed. Common include: whether files are readable and writable, input and output of computer terminal devices, and network requests (common).
In Linux operating systematization, IO multiplexing has three modes: select, poll and epoll. (windows only supports select mode)
Monitor whether the socket object has a new connection or new data.
select select It first appeared in April 1983.2BSD In, it passes through a select()The system call is used to monitor the array of multiple file descriptors when select()After returning, the ready file descriptors in the array will be modified by the kernel, so that the process can obtain these file descriptors for subsequent read and write operations. select At present, it is supported on almost all platforms, and its good cross platform support is also one of its advantages. In fact, from now on, this is also one of its few remaining advantages. select One disadvantage of is that there is a maximum limit on the number of file descriptors that a single process can monitor Linux It is generally 1024, but this limit can be raised by modifying the macro definition or even recompiling the kernel. In addition, select()The maintained data structure storing a large number of file descriptors increases linearly with the increase of the number of file descriptors. At the same time, due to the delay of network response time, a large number of TCP The connection is inactive, but the select()For all socket A linear scan is performed, so it also wastes some overhead. poll poll Born in 1986 System V Release 3,It and select It doesn't make much difference in essence, but poll There is no limit to the maximum number of file descriptors. poll and select There is also a disadvantage that the array containing a large number of file descriptors is copied between the user state and the address space of the kernel as a whole. Regardless of whether these file descriptors are ready or not, its overhead increases linearly with the increase of the number of file descriptors. In addition, select()and poll()After the ready file descriptor is told to the process, if it is not modified by the process IO Operation, then the next call select()and poll()These file descriptors will be reported again, so they generally do not lose the ready message. This method is called horizontal trigger( Level Triggered). #### The above two methods are inefficient epoll until Linux2.6 There is an implementation method directly supported by the kernel, that is epoll,It has almost all the advantages mentioned before and is recognized as Linux2.6 Multi channel with the best performance I/O Ready notification method. epoll It can support both horizontal trigger and edge trigger( Edge Triggered,Only tell the process which file descriptors have just become ready. It only says it once. If we do not take action, it will not tell again. This method is called edge trigger). Theoretically, the performance of edge trigger is higher, but the code implementation is quite complex. epoll Similarly, only those ready file descriptors are notified, and when we call epoll_wait()When you get the ready file descriptor, what is returned is not the actual descriptor, but a value representing the number of ready descriptors. You only need to epoll You can get the corresponding number of file descriptors from the specified array in turn. Memory mapping is also used here( mmap)Technology, which completely eliminates the overhead of copying these file descriptors during system call. Another essential improvement is epoll Event based ready notification is adopted. stay select/poll In, the kernel scans all monitored file descriptors only after the process calls a certain method epoll Prior adoption epoll_ctl()To register a file descriptor. Once a file descriptor is ready, the kernel will adopt a similar method callback The callback mechanism quickly activates this file descriptor when the process calls epoll_wait()You'll be notified when. #### The callback mechanism is equivalent to that whoever has data raises his hand and tells me, I will go to him to get data, which is more efficient.
Supplement: socket + non blocking + IO multiplexing (as long as IO operation objects and files can be monitored).
summary
-
OSI layer 7 model
Application layer, presentation layer, session layer, transport layer, network layer, data link layer and physical layer.
-
Difference between UDP and TCP
UDP,Fast, but can not guarantee the accuracy of data. TCP,You need to create a reliable connection before sending and receiving data( ack).
-
TCP's three handshakes and four waves
-
Why are there sticky bags? How to solve it?
-
How to make socket requests non blocking?
-
What is the role of IO multiplexing?
Monitor multiple IO Whether the object has changed (readable)/Writable).
- IO multiplexing + non blocking + socket server allows the server to process the requests of multiple clients at the same time.
- The IO multiplexing + non blocking + socket client can initiate multiple requests to the server at the same time.