Python Network and concurrent programming 01 Socket programming

Forward looking knowledge

C/S architecture

C/S architecture is a two-tier architecture composed of Server and Client.

The Internet is full of C/S architecture (Client/Server). For example, if we need to play hero League, we must connect to the Server of hero League. For our players, its hero League Server is the Server side, and we must download a hero League Client side to interact with the Server side of hero League.

Five layer agreement

The internet protocol is actually produced to allow computers to communicate with each other. There is no hierarchy in itself.

For ease of understanding, we can divide them into 5 or 7 layers according to their functions, as shown in the following table:

HierarchyfunctionRelated agreements
application layerIt is used to specify the format of application data and provide it to various applications for communication with each otherHTTP,FTP
Transport layerUsed to distinguish the only network application on the systemTCP,UDP
network layerIt is used to distinguish broadcast domains and prevent network stormsIP
data link layerIt is used to divide electrical signals and provide the function of mutual conversion between IP address and MAC addressEthernet protocol, ARP Protocol
physical layerIt is used to transmit electrical signals. It is the cornerstone of network data transmission

socket abstraction layer

The core of computer network is a pile of protocols. If you want to develop software based on network communication, you must abide by these protocols.

However, due to the huge cost of learning protocols, we need a highly abstract middle layer to connect the preceding and the following, so as to facilitate our rapid development.

At this time, the socket came into being. The socket is located between the application layer and the transport layer. It encapsulates various protocols downward. Users can quickly develop software based on network communication only through the interface provided by the socket, and do not need to deeply study some underlying protocols, such as TCP, UDP, etc.

Why learn socket? Because it is the bottom implementation of various network related application frameworks, such as Django, requests, etc., as long as the framework or module related to the network is inseparable from socket.

Brief description of TCP protocol

Streaming transmission

TCP protocol is a form based on byte stream. What is stream? Because the data transmission is like a tap, there is no exact boundary when it is turned on, as shown in the figure below:

Three handshakes

If the Client and Server want to interact with each other normally, they must first go through a three-time handshake process to determine their relationship and establish a two-way link channel:

Status definitions can be viewed through the netstat command:

  • SYN_SENT: the Client will enter this state immediately after sending a link establishment request, and will maintain this state before receiving a response from the Server. Generally speaking, the duration of this state is very short and almost unpredictable
  • ESTABLISHED: when one party enters this state, it means that it can send data to the other party
  • LISTEN: the state of the Server side when waiting for the Client side to establish three handshakes, that is, listening to the Client side's requests
  • SYN_RCVD: when the Server side enters this state, it means that it has received three handshake link requests from the Client side

Information interpretation:

  • SYN: flag bit used to establish link
  • ACK: flag bit of confirmation request
  • seq: it can be understood as a code to confirm that the information has not been modified

In addition, SYN flooding attack:

  • When the Server side enters syn for a long time_ When RCVD is in status, be careful whether it is attacked by SYN Flood.
  • For the Server side, the TCP three-time handshake will infinitely reply to the SYN requests sent by the Client side, and one will be returned when one is received.
  • If a hacker simulates thousands of clients to initiate SYN requests to the Server and sends a Yo Yo after the first handshake, the Server is still foolishly waiting for the third handshake reply, which will put a lot of pressure on the Server. So TCP protocol is also called good man protocol

Supplementary, semi linked pool backlog:

  • If the Server cannot handle all the requests at one time, it will put the incoming requests into the semi link pool for queuing
  • One of the effective strategies to prevent SYN Flood attacks is to increase the maximum number of backlog link pools, but this strategy is generally not adopted
  • Another strategy is to reduce the number of times the Server side returns each request (if the Server side finds that the Client side ignores itself, it will constantly respond to the last information. The initial value is 5s, send it once after 5s, then turn it into 3s, send it once after 3s, turn it into 1s, and then send it again... Until you don't want to send it, you won't pay attention to the request.)
  • When you can't open a web page, there is a possibility that if someone else's backlog is full, you can only wait outside

Bidirectional link channel

After three handshakes between Client and Server, TCP protocol will create a two-way link channel for data interaction between Client and Server, as shown in the figure:

Reliable transmission

TCP protocol is also called reliable transmission protocol, that is, after the sender sends data to the receiver, the receiver must respond to the sender's ack confirmation of the received information. If the sender does not receive the ACK confirmation, the data will be sent again, as shown in the figure below.

Note: the data interaction during the three handshakes does not take the two-way link channel, but the data transmission in the figure below takes the two-way link channel

Four waves

When the Client side wants to disconnect from the Server side, it must go through a process of four waves.

Why does it take only three times to create a link and four times to break it?

It can be seen that there is no data transmission before the three handshakes, and the second of them sends a request and an acknowledgement at one time. Therefore, one operation is reduced.

Four waves involve data transmission, so it is impossible to simplify it into three waves.

In addition, the four waves are also different from the three handshakes. The four waves are also based on the two-way link channel, which has not been successfully established at the time of the three handshakes:

Status definition:

  • FIN_WAIT_1: Initiate a disconnect request on behalf of
  • FIN_WAIT_2: It means that the Client side will no longer actively send data to the Server side
  • TIME_WAIT: on behalf of the Client, the Client will reply to the last confirmation message. After the reply, the two-way link will be officially closed
  • CLOSE_WAIT: on behalf of closing the wait
  • LAST_ACK: represents continuous confirmation (that is, as long as the Client does not reply to the fourth message, the Server will continue to try to send a FIN request to disconnect the link)

In addition, in real life scenarios, the server actively disconnects links, because it involves communication with many clients, and some clients are still queuing, so it is impossible to waste too much time on one client.

You can understand this sentence as:

  • The server is a scum man. Many girls (Client side) like him and write love letters to him. After replying to a girl's love letter, he will open the next love letter immediately, not just one.

UDP protocol overview

Datagram transmission

UDP protocol is a datagram based format (also known as message based transmission), which is different from the byte stream format of TCP. UDP datagram format has a beginning and an end, which is very important.

As shown in the figure:

Unreliable transmission

UDP protocol does not need to establish a two-way link channel in data transmission.

And UDP sending messages is different from TCP. Sending just sends messages. No matter whether the receiver receives the messages or not, it will not send them again. Therefore, this is also the reason why UDP protocol is called unreliable transmission protocol.

Because UDP protocol does not have this ACK confirmation mechanism, although it has decreased a lot for data reliability, it has obvious hints for data transmission.

Therefore, both DHCP service and DNS domain name resolution service use UDP protocol because it is faster. In addition, early QQ also uses UDP protocol for communication.

Socket development history

Socket origin

Socket originated from the version of Unix at the University of California, Berkeley in the 1970s. It was originally designed to allow multiple applications on the same host to communicate, that is, process communication or IPC. There are two kinds of sockets:

  • File based sockets
  • Network based socket

Communication is not allowed between different processes on the same machine, but data interaction can be carried out through sockets.

In addition, sockets also allow applications to insert I/O (input / output) into the network and communicate with other applications in the network. Network-based sockets are the combination of IP address and port.

Therefore, socket is also called IP+PORT.

File based socket family

Name: AF_UNIX

Function: Unix is all about files. File based sockets call the underlying file system to access data. Two socket processes run on the same machine and can communicate indirectly by accessing the same file system.

Network based socket family

Name: AF_INET

Function: IPV4 protocol socket. With IP + PORT, we can communicate with any application on the Internet.

In addition, there is another one called AF_INET6 socket, that is, IPV6 Based socket.

Socket workflow

Socket workflow based on TCP protocol

Due to the complexity of TCP protocol itself, the overall process of writing programs using sockets based on TCP protocol is also complex:

Socket workflow based on UDP protocol

The socket workflow based on UDP protocol is simpler than that based on TCP protocol, because there is no need to establish a two-way link channel:

TCP/Socket simple communication

Basic implementation

Since the TCP protocol needs to establish a two-way link channel, the Server side must be opened first and then the Client side, otherwise an exception will occur.

The Server side code is as follows:

from socket import *

# 1. Instantiate the socket object and add port reuse
# AF_INET: IPV4
# SOCK_STREAM: TCP protocol
server = socket(family=AF_INET, type=SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

# 2. Bind IP address and port number
# localhost: native only
# 127.0.0.1: only allowed for local use
# 0.0.0.0: allow any client link
server.bind(("localhost", 8888))

# 3. Set the size of the half link pool
server.listen(5)

# 4. Block waiting for three handshake requests
# conn: bidirectional link channel
# clientAddr: client link information
conn, clientAddr = server.accept()

# 5. Receive the information sent by the client in bytes
data = conn.recv(1024)

# 6. Send information to client
conn.send(data.upper())

# 7. Close the two-way link channel and release the system resources occupied by the underlying port
conn.close()

# 8. Shut down the server and release the memory resources occupied by Python applications
server.close()

The Client code is as follows:

from socket import *

# 1. Instantiate socket object
# AF_INET: IPV4
# SOCK_STREAM: TCP protocol
client = socket(family=AF_INET, type=SOCK_STREAM)

# 2. Send a request to the server
# Start shaking hands three times and create a two-way link channel
client.connect(("localhost", 8888))

# 3. Start communication and send a message
client.send("hello world".encode("u8"))

# 4. Receive messages from the server
msg = client.recv(1024)
print(msg.decode("u8"))

# 5. Close the client
client.close()

Double layer cycle

In the above example, the Server side will accept the information from the Client side and return it after upper().

In other words, the interaction between the Server side and the Client side is only once, which is obviously unreasonable.

Therefore, we need to make some small changes to the Server-side code so that it can continuously process the Client-side requests, rather than only once.

The specific logic is:

  • Add a link loop to receive three handshake requests from different clients
  • Increase the communication cycle so that the Client and Server can communicate for a long time

The Server side code is as follows:

from socket import *

server = socket(family=AF_INET, type=SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

server.bind(("localhost", 8888))

server.listen(5)

while 1:
    # Continuously accept new requests, that is, the server never stops running (link loop)
    conn, clientAddr = server.accept()
    print("%s connect server" % clientAddr[0])

    # The server can always maintain communication with the client. When the client enters exit, it will stop serving the current client (communication cycle)
    while 1:
        data = conn.recv(1024)

        if data.decode("u8") == "exit":
            break

        conn.send(data.upper())

    print("%s close connect" % clientAddr[0])
    conn.close()

The Client code is as follows:

from socket import *

client = socket(family=AF_INET, type=SOCK_STREAM)

client.connect(("localhost", 8888))

# Continuously interact with the server
while 1:
    sendMsg = input(">>>")
    client.send(sendMsg.encode("u8"))

    # If exit is sent, the link is broken
    if sendMsg == "exit":
        break

    recvMsg = client.recv(1024)
    print(recvMsg.decode("u8"))

print("client close")
client.close()

Exception caused by Client sending null information

Now the Server side is all ready, but the Client side still has a problem.

When > > > appears on the Client side, the Server side will get stuck after directly clicking enter. This problem can be solved only by restarting the Server side.

If the code is debugged, it can be observed that the Client side is in the recv() state and the Server side is also in the recv() state, which means that the carriage return "empty message" sent by the Client side has not been received by the Server side at all.

Why did this happen? We also need to start with the underlying principle.

In fact, either send() or recv() is a system call issued by the socket application to the operating system. During this period, the CPU working state will change from user state to kernel state.

The memory data in user mode cannot directly interact with the memory data in kernel mode, so it can only rely on a mapping relationship (which can be understood as copy, but not accurate) to map the content to be sent.

If the Client side enters a carriage return and sends it to the Server side, the Server side cannot receive the information because the mapping of recv() cannot read the "empty message":

  • The Client sends its own carriage return "empty message"
  • Because recv() on the Server side cannot read the "empty message", it will get stuck directly

As shown in the figure below:

To solve this problem, we only need to limit the messages sent by the Client side so that they are not empty:

from socket import *

client = socket(family=AF_INET, type=SOCK_STREAM)

client.connect(("localhost", 8888))

# Continuously interact with the server
while 1:
    sendMsg = input(">>>")

    # Do not let clients send empty messages
    if not sendMsg:
        continue
        
    client.send(sendMsg.encode("u8"))
    recvMsg = client.recv(1024)
    print(recvMsg.decode("u8"))

    # If exit is sent, the link is broken
    if sendMsg == "exit":
        break

print("client close")
client.close()

Exceptions caused by forcibly closing the Client side

When the Server side is linking with the Client side, if the Client side is forcibly closed at this time, an exception will occur on the Server side.

  • Under Windows platform, the Server side will directly throw ConnectionResetError exception
  • recv() on the Server side of Unix platform will receive empty information without restriction

As follows:

ConnectionResetError: [WinError 10054] The remote host forced an existing connection to close.

Why does this happen? Because the link between the Server side and the Client side is bidirectional, once one party closes the link channel, the link channel will collapse, resulting in this exception on the Server side.

How to resolve this exception? try, except and if judgment are required, as shown below:

from socket import *

server = socket(family=AF_INET, type=SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

server.bind(("localhost", 8888))

server.listen(5)

while 1:

    conn, clientAddr = server.accept()
    print("%s connect server" % clientAddr[0])

    while 1:

        # try: for Windows Environment
        try:

            data = conn.recv(1024)
            
            # if: for Unix Environment
            if not data:
                break

            conn.send(data.upper())

        except ConnectionResetError as e:
            break

    print("%s close connect" % clientAddr[0])
    conn.close()

Final code

The final code is as follows.

Server side:

from socket import *

server = socket(family=AF_INET, type=SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

server.bind(("localhost", 8888))

server.listen(5)

while 1:

    conn, clientAddr = server.accept()
    print("%s connect server" % clientAddr[0])

    while 1:

        # try: for Windows environments
        try:

            data = conn.recv(1024)
            
            # if: for Unix Environment
            if not data:
                break

            conn.send(data.upper())

        except ConnectionResetError as e:
            break

    print("%s close connect" % clientAddr[0])
    conn.close()

Client side:

from socket import *

client = socket(family=AF_INET, type=SOCK_STREAM)

client.connect(("localhost", 8888))

# Continuously interact with the server
while 1:
    sendMsg = input(">>>")

    # Do not let clients send empty messages
    if not sendMsg:
        continue

    # If exit is sent, the link is broken
    if sendMsg == "exit":
        break

    client.send(sendMsg.encode("u8"))
    recvMsg = client.recv(1024)
    print(recvMsg.decode("u8"))


print("client close")
client.close()

UDP/Socket simple communication

Basic implementation

The following is socket communication based on UDP protocol. Since UDP protocol has no two-way link channel, there will be no error when starting either end first.

The Server side code is as follows:

from socket import *

# 1. Instantiate the socket object and add port reuse
# AF_INET: IPV4
# SOCK_DGRAM: UDP protocol
server = socket(family=AF_INET, type=SOCK_DGRAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

# 2. Bind IP address and port number
# localhost: native only
# 127.0.0.1: only allowed for local use
# 0.0.0.0: allow any client link
server.bind(("localhost", 8888))

# 3. Receive data from client
data, clientAddr = server.recvfrom(1024)

# 4. Reply to client's information
server.sendto(data.upper(), clientAddr)

# 5. Shut down the server and release the memory resources occupied by Python applications
server.close()

The Client code is as follows:

from socket import *

# 1. Instantiate socket object
# AF_INET: IPV4
# SOCK_DGRAM: UDP protocol
client = socket(family=AF_INET, type=SOCK_DGRAM)

# 2. Send data to the server
client.sendto("hello world".encode("u8"), ("localhost", 8888))

# 3. Receive data from the server
msg, serverAddr = client.recvfrom(1024)
print(msg.decode("u8"))

# 4. Close the client
client.close()

Add single layer circulation

Since the communication based on UDP protocol will not establish a two-way link channel, we only need to add a "communication cycle".

The Server side improvement code is as follows:

from socket import *

server = socket(family=AF_INET, type=SOCK_DGRAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

server.bind(("localhost", 8888))

while 1:
    data, clientAddr = server.recvfrom(1024)
    server.sendto(data.upper(), clientAddr)

The Client side improvement code is as follows:

from socket import *

client = socket(family=AF_INET, type=SOCK_DGRAM)

while 1:
    sendMsg = input(">>>")

    if sendMsg == "exit":
        break

    client.sendto(sendMsg.encode("u8"), ("localhost", 8888))

    msg, serverAddr = client.recvfrom(1024)
    print(msg.decode("u8"))

client.close()

Bug test

We BUG tested the two pieces of code and found no exceptions.

1) Will forcibly stopping the Client side cause the Server side to crash abnormally?

  • No, because the communication of UDP protocol is not based on two-way link channel

2) Does the client send a carriage return or any empty message cause recvfrom() to get stuck?

  • No, we should start with the data format of UDP. Because UDP is sent in datagram format, even if the message body is empty, there is a message header in it

    Therefore, the whole section of UDP data cannot be empty, which will not cause the kernel buffer to get stuck because it can't read the data

Solve the problem of port occupation

In socket programming, the port will be occupied. In fact, the server sends the last ACK response to the client, which is the fourth step of the four waves.

At this time, the status of the server should be: TIME_WAIT (wait for a period of time to ensure that all the information in the two-way link channel is read). This is normal. Don't panic.

The solution is as follows:

1) Add a socket configuration to reuse ip and port:

# This code is placed before bind

server = socket(family=AF_INET, type=SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # *
server.bind(("localhost", 8888))

2) For Linux platforms:

A large number of problems were found in the system TIME_WAIT State of the connection by adjusting linux Kernel parameter resolution,
vi / etc / sysctl.conf

Edit the file and add the following:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30

Then execute / sbin / sysctl - p
 Let the parameter take effect.

net.ipv4.tcp_syncookies = 1
 Indicates on SYN
Cookies. When appear SYN Enable when waiting for queue overflow cookies To deal with, can prevent a small amount of SYN Attack, the default is 0, which means it is closed;

net.ipv4.tcp_tw_reuse = 1
 Indicates that reuse is enabled. Allow will TIME - WAIT
sockets Reuse for new TCP Connection, the default is 0, which means it is closed;

net.ipv4.tcp_tw_recycle = 1
 Indicates on TCP Connecting TIME - WAIT
sockets The default value is 0, which means it is closed.

net.ipv4.tcp_fin_timeout
 Modify the system default
TIMEOUT
 time

Verify link legitimacy

In many cases, our TCP server can set up a client link verification mechanism to prevent network flooding.

In fact, the implementation of this verification mechanism is also very simple. The idea is that before entering the communication cycle, the Client and Server go through link authentication first. Only the authenticated Client can continue to link with the Server. The core point is that both Server and Client must have the same verification comparison value.

The implementation is shown in the figure:

The Server side code is as follows:

from socket import *
import json
import os
import hmac


class TcpServer:
    def __init__(self, ip_port=("localhost", 8888), backlog=5, bufsize=1024) -> None:
        self.salt = b"SOCKET SERVER"
        self.verificationString = None

        self.bufsize = bufsize
        self.backlog = backlog
        self.ip_port = ip_port
        self.socket = None
        self.conn = None
        self.clientAddr = None

    def run(self):
        self.initialization()
        while 1:
            self.requestHandler()

            if not self.verification():
                print("verification fail")
                continue

            while 1:
                try:
                    self.communicateHandler()

                except ConnectionResetError as e:
                    self.conn.close()
                    break

    def initialization(self):
        """Initialization data"""
        self.socket = socket(family=AF_INET, type=SOCK_STREAM)
        self.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        self.socket.bind(self.ip_port)
        self.socket.listen(5)
        self.socket.listen(self.backlog)

    def requestHandler(self):
        """Processing link requests"""
        self.conn, self.clientAddr = self.socket.accept()

    def verification(self):
        """Verify link legitimacy"""

        # Generate random values of 32-bit bytes type
        self.verificationString = os.urandom(32)

        # Send the random value to the client
        self.conn.send(self.verificationString)

        # Receive information from client
        recvMsg = self.conn.recv(self.bufsize)

        # hash encrypt the random value and salt and compare them with recvMsg. If they are the same, the verification passes
        # Otherwise, the authentication fails and the two-way channel is closed
        h = hmac.new(self.salt, self.verificationString)
        digest = h.digest()

        print(digest, "\n", recvMsg)
        return hmac.compare_digest(digest, recvMsg)

    def communicateHandler(self):
        """Processing communication requests"""

        data = self.conn.recv(self.bufsize)

        if not data:
            raise ConnectionResetError(
                "client %s close" % str(self.clientAddr))

        data = json.loads(data.decode("u8"))
        self.conn.send(
            json.dumps(data).encode("u8")
        )


if __name__ == "__main__":
    server = TcpServer()
    server.run()

The Client code is as follows:

import json
import hmac

from socket import *


class TcpClient:
    def __init__(self, server_ip_port=("localhost", 8888), backlog=5, bufsize=1024) -> None:
        self.salt = b"SOCKET SERVER"
        self.bufsize = bufsize
        self.backlog = backlog
        self.server_ip_port = server_ip_port

        self.socket = None

    def run(self):
        """Initialization data"""
        self.socket = socket()
        self.socket.connect(self.server_ip_port)
        self.verification()
        self.communicateHandler()

    def verification(self):
        """Verify link legitimacy"""

        # Get the authentication string sent by the server
        recvVerificationString = self.socket.recv(self.bufsize)

        # The validation string is mixed with the local salt to get a new result
        h = hmac.new(self.salt, recvVerificationString)
        digest = h.digest()

        # Send the new results to the server for comparison
        self.socket.send(digest)

    def communicateHandler(self):
        """Processing communication requests"""
        while 1:
            sendMsg = input(">>>")

            # Do not let clients send empty messages
            if not sendMsg:
                continue

            # If exit is sent, the link is broken
            if sendMsg == "exit":
                break

            self.socket.send(
                json.dumps(sendMsg).encode("u8")
            )

            recvMsg = json.loads(
                self.socket.recv(1024).decode("u8")
            )

            print(recvMsg)

        self.socket.close()

if __name__ == "__main__":
    client = TcpClient()
    client.run()

Complete collection of socket module methods

The following methods are provided by the socket module:

methoddescribe
s.bind()Bind the address (host, port) to the socket in AF_ Under INET, the address is represented in the form of tuple (host, port)
s.listen()Start TCP listening. backlog specifies the maximum number of connections that the operating system can suspend before rejecting connections. This value is at least 1, and most applications can be set to 5
s.accept()Passively accept TCP client connections (blocking) and wait for the connection to arrive
s.connect()Actively initialize TCP server connection. Generally, the format of address is tuple (hostname, port). If there is a link error, a socket will be thrown Error exception
s.connect_ex()The extended version of the connect() function. When an error occurs, it returns an error code instead of throwing an exception
s.recv()Receive TCP data. The data is returned as a string. bufsize specifies the maximum amount of data to be received. flag provides additional information about the message, which can usually be ignored
s.send()Send TCP data and send the data in string to the connected socket. The return value is the number of bytes to send, which may be less than the byte size of string
s.sendall()Send TCP data completely, send the data in string to the connected socket, but try to send all data before returning. If None is returned successfully, an exception will be thrown if it fails
s.recvfrom()Receive UDP data, similar to recv(), but the return value is (data, address). Where data is the string containing the received data, and address is the socket address where the data is sent
s.sendto()Send UDP data and send the data to the socket. Address is a tuple in the form of (ipaddr, port), specifying the ip address and port number of the receiver. The return value is the number of bytes sent
s.close()Close socket
s.getpeername()Returns the remote address of the connection socket. The return value is usually a tuple (ipaddr, port)
s.getsockname()Returns the socket's own address. Usually a tuple (ipaddr, port)
s.setsockopt(level,optname,value)Sets the value of the given socket option
s.getsockopt(level,optname[.buflen])Returns the value of the socket option
s.settimeout(timeout)Set the link timeout. The default is None, that is, never timeout
s.gettimeout()Returns the value of the current timeout period in seconds. If the timeout period is not set, it returns None
s.fileno()Returns the file descriptor of the socket
s.setblocking(flag)If the flag is 0, set the socket to non blocking mode, otherwise set the socket to blocking mode (default). In non blocking mode, if the call recv() does not find any data, or the call send() cannot send data immediately, it will cause socket Error exception
s.makefile()Create a file associated with the socket

For more methods, refer to the official documents: Click me to jump

Added by bufhal on Fri, 21 Jan 2022 14:53:47 +0200