socket programming

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

Keywords: Python

Added by pkedpker on Wed, 12 Jan 2022 19:41:40 +0200