Today's content
- socket programming
- Simple server and client code implementation
- Communication cycle
- Packet sticking (TCP protocol)
- Header making, struct module and packaging form
Detailed content
1, socket programming
Implement a program that can interact with data.
When they exchange information, they have to transmit data through the network, which will certainly involve the operation of OSI layer 7 protocol. Each time they transmit data, they have to operate OSI layer 7 protocol, and many similar works will be repeated. At this time, a socket module appears, which encapsulates the operation code of OSI layer 7 protocol. When we transmit data, You can easily and quickly call the operation method in the form of points through the object instantiated by socket.
- socket module
Next, I will use the socket module to realize the simplest socket programming
Note: only after the server is started can the client successfully connect to the server and realize data interworking
Server
import socket # Instantiate a socket object server = socket.socket() # Bind an IP + port number to the 'server (application)' as the unique identification server.bind(('192.168.11.134', 8080)) # Set a semi connection pool with a maximum capacity (number of connections waiting) of 5 server.listen(5) # Listen. When a client applies for connection, obtain its socket object and application address (IP + port) sock, address = server.accept() # receive data data = sock.recv(1024) print(data.decode('utf8')) # send data sock.send('elijah is very good'.encode('utf8')) # Close the socket object connected to the client sock.close() # Close the socket object of the server server.close()
client
import socket # Instantiate the socket object of the client client = socket.socket() # The client accurately connects to the server according to the IP + port client.connect(('192.168.11.134', 8080)) # Send data to the server client.send('i am client'.encode('utf8')) # Receive the data returned by the server data = client.recv(1024) print(data.decode('utf8')) # Close client client.close()
2, Communication cycle
Upgrade the simple version to a lower level, so that the server and the client can enter customized information, and will not end the process as soon as the data is transmitted, so that the server is always in the listening state and can connect with the client at any time.
Server
import socket # Instantiate a socket object server = socket.socket() # Bind an IP + port number to the 'server (application)' as a unique identification server.bind(('192.168.11.134', 8080)) # Set a semi connection pool with a maximum capacity (number of connections waiting) of 5 server.listen(5) # Listen. When a client applies for connection, obtain its socket object and application address (IP + port) sock, address = server.accept() while True: # receive data client_data = sock.recv(1024) print(client_data.decode('utf8')) # send data server_data = input('Enter your reply>>>: ') sock.send(server_data.encode('utf8'))
client
import socket # Instantiate the socket object of the client client = socket.socket() # The client accurately connects to the server according to the IP + port client.connect(('192.168.11.134', 8080)) while True: # Send data to the server client_data = input('Enter your information>>>: ') client.send(client_data.encode('utf8')) # Receive the data returned by the server data = client.recv(1024) print(data.decode('utf8'))
Realize your one sentence and my one sentence communication:
Code optimization
1,Keep the server in the listening state. When the server replies to an empty message, it will disconnect from the current client and return to the listening state 2,The message sent by the client cannot be empty (avoid both being empty) revc Wait state) 3,Add compatibility code to the server( mac linux) 4,The server restarts frequently and reports port occupation error from socket import SOL_SOCKET, SO_REUSEADDR server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # Add before bind 5,The client closes abnormally and the server reports an error Exception capture 6.Server link loop 7.Semi connection pool Set the number of clients that can wait (an error will be reported if the number exceeds)
Server
import socket # Instantiate a socket object server = socket.socket() # Bind an IP + port number to the 'server (application)' as a unique identification server.bind(('192.168.11.134', 8080)) # Set a semi connection pool with a maximum capacity (number of connections waiting) of 5 server.listen(5) while True: # Listen. When a client applies for connection, obtain its socket object and application address (IP + port) sock, address = server.accept() while True: # Handling client exception disconnect errors try: # receive data client_data = sock.recv(1024) print(client_data.decode('utf8')) # send data server_data = input('Enter your reply>>>: ') if len(server_data) == 0: break sock.send(server_data.encode('utf8')) except Exception as e: print(e) break
client
import socket # Instantiate the socket object of the client client = socket.socket() # The client accurately connects to the server according to the IP + port client.connect(('192.168.11.134', 8080)) while True: # Send data to the server client_msg = input('Enter your information>>>: ') if len(client_msg) == 0: continue client.send(client_msg.encode('utf8')) # Receive the data returned by the server data = client.recv(1024) print(data.decode('utf8'))
3, Sticking problem
Because TCP protocol is also a streaming protocol, data will be transmitted like pipelining
When the client sends a command request to the server, the server returns a large amount of data, while the client only receives 1024 bytes at a time. Obviously, the remaining data will not be lost, but will be blocked in the transmission channel. When the client sends an application command next time, the remaining data will flow in, resulting in an unexpected answer.
The TCP protocol has a feature
When the amount of data sent is relatively small, it is sent multiple times, and the transmission time interval is relatively short that TCP It will automatically package all these data into a packet to receive sock.send('litle') sock.send('ll') sock.send('a') client.recv() client.recv() client.recv() ---> litlella
Solve the sticking problem
- Make header
The header is used to identify the specific information of the upcoming data
For example, the specific size of the data, so that the receiver can decide how much data to receive according to the specific information of the data size
client.recv(4343534)
Simple version header
import socket import subprocess import json import struct server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) while True: sock, address = server.accept() while True: data = sock.recv(1024) # Receive cmd command command_cmd = data.decode('utf8') sub = subprocess.Popen(command_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) res = sub.stdout.read() + sub.stderr.read() # The results can be great # 1. Make header data_first = struct.pack('i', len(res)) # 2. Send header sock.send(data_first) # 3. Send real data sock.send(res) import socket import struct client = socket.socket() # Buy a mobile phone client.connect(('127.0.0.1', 8080)) # dial while True: msg = input('Please enter cmd command>>>:').strip() if len(msg) == 0: continue client.send(msg.encode('utf8')) # 1. First receive header data with a fixed length of 4 recv_first = client.recv(4) # 2. Parse header real_length = struct.unpack('i',recv_first)[0] # 3. Receive real data real_data = client.recv(real_length) print(real_data.decode('gbk'))
The header can be made into a dictionary, so that it can receive not only the specific size information of the data, but also more other data information, and the 'i' mode will not report errors due to too large data when strut s is packaged
Expand knowledge
When reading the source code 1.The variable name followed by a colon indicates the data type that the variable name needs to refer to 2.The greater than sign after the function means the return value type of the function