SoFunction
Updated on 2024-10-30

Python select and selectors module concepts in detail

 1. select module

For SELECT, there are several other concepts to understand first:

File Descriptor:

A file descriptor is formally a non-negative integer. In practice, it is an index value that points to a table of records maintained by the kernel for each process for files opened by that process. When a program opens an existing file or creates a new file, the kernel returns a file descriptor to the process.

Kernel space:

Linux simplifies the segmentation mechanism so that the virtual address is always the same as the linear address, and therefore, the virtual address space of Linux is also from 0 to 4 G. The Linux kernel divides this 4G byte space into two parts. The highest 1G bytes (from virtual address 0xC0000000 to 0xFFFFFFFF) are used by the kernel and are called "kernel space". The lower 3G bytes (from virtual address 0x00000000 to 0xBFFFFFFF) are used by each process and are called "user space".) Since each process has access to the kernel through system calls, the Linux kernel is shared by all processes in the system. Thus, from the point of view of a specific process, each process can have 4 gigabytes of virtual space.

The kernel space holds the kernel code and data, while the user space of the process holds the code and data of the user program. Whether it is kernel space or user space, they are both in virtual space.

Kernel space and user space generally communicate through system calls.

select is to monitor against a number of file descriptors (fd for short), and it has three arguments:

  • rlist -- wait until ready for reading
  • wlist -- wait until ready for writing
  • xlist -- wait for an "exceptional condition"

The first parameter monitors the list of incoming data fd's, select monitors this list, waits for these fd's to send data, and once the data has been sent (and can be read), returns a list of fd's to read.

The second parameter monitors the list of outgoing data fd's. select monitors this list, waits for these fd's to send out the data, and once the fd's are ready to be sent (and can be written to), returns a list of fd's that are writable.

The third parameter monitors the fd list and returns the list of fd's with exceptions.

Server:

import select
import socket
import sys
import queue

# Generate socket object
server = ()
# Set non-blocking mode
(False)

# Bind the address, set up the listener
(('localhost',9999))
(5)

# Put yourself on the to-be-monitored list
inputs = [server, ]
outputs = []
message_queues = {}

while True:
  '''
  About socket readable and writable judgment, you can refer to the blog: /majianfei1023/article/details/45788591
  '''
  rlist, wlist, elist = (inputs,outputs,inputs) #If no fd is ready, the program will keep blocking here. #

  for r in rlist: # Iterate over the fd's that are ready to be read.
    if r is server: # If the fd is a server, i.e., the server has data to be received and read, it means that a new client is connecting.
      conn, client_addr = ()
      print("new connection from",client_addr)
      (False)
      (conn) # Add this new client connection to the list of detections
      message_queues[conn] = () # Use a queue to store the data sent by the client and wait for the server to return the data uniformly

    else:     # This readable r is not the server, it's some client. That is, the client has sent data over, and that data is in the pending read state
      try:    # Exception handling, this is to prevent the client from abnormal disconnection error reporting (e.g. manually closing the client black window, the server will follow and report an error exit)
        data = (1024)
        if data:  # Determine if the client is disconnected based on determining if data is empty
          print("Data received from [%s]:" % ()[0], data)
          message_queues[r].put(data)  # The received data will be put into the queue and returned to the client in a while.
          if r not in outputs:
            (r)   # Put into the list of writable fd's to indicate that these fd's are ready to send data.
        else:  # If the data is null, it indicates that the client is disconnected
          print('Client disconnected')
          if r in outputs:
            (r)  # Clear disconnected connections
          (r)     # Clear disconnected connections
          del message_queues[r]  # Clear disconnected connections
      except ConnectionResetError:   # If an error is reported, the client is disconnected #
        print("The client is abnormally disconnected.", r)
        if r in outputs:
          (r)  # Clear disconnected connections
        (r)    # Clear disconnected connections
        del message_queues[r] # Clear disconnected connections

  for w in wlist:    # Iterate through the list of writable fd's, i.e. the ones ready to send data to
    # Determine if the queue is empty
    try :
      next_msg = message_queues[w].get_nowait()
    except :
      # print("client [%s]" % ()[0], "queue is empty..")
      (w)
    # If the queue is not empty, change the data in the queue to uppercase and send it back as it is
    else:
      # print("sending msg to [%s]"% ()[0], next_msg)
      (next_msg.upper())

  for e in elist:  # Handle the fd that reported an error
    ()
    print("Error occured in ",())
    (e)
    if e in outputs:
      (e)
    del message_queues[e]

Client:

import socket
import sys

sock = ()
(('localhost',9999))
while True:
  c = input('>>>:').strip()
  (())
  data = (1024)
  print(())

()

2. selectors module

Official Documentation:/3/library/

Server:

import selectors
import socket

# According to the platform automatically select the best IO multiplexing mechanism, such as linux will choose epoll, windows will choose select
sel = ()

def accept(sock, mask):
  # Establish client connections
  conn, addr = ()
  print('accepted', conn, 'from', addr)
  # Set non-blocking mode
  (False)
  # Register a connection again, add it to the monitoring list, #
  (conn, selectors.EVENT_READ, read)

def read(conn, mask):
  try:  # Throw client forced close exceptions (e.g., manually closing a client black window)
    data = (1000) # Should be ready
    if data:
      print('echoing', repr(data), 'to', conn)
      (data) # Hope it won't block
    else:
      print('Client closed.', conn)
      # Remove conn from the monitoring list
      (conn)
      ()
  except ConnectionResetError:
    print('Client forcibly closed.', conn)
    # Remove conn from the monitoring list
    (conn)
    ()

# Create socket objects
sock = ()

# Bind ports, set up listening
(('localhost', 1234))
(100)

# Set to non-blocking mode
(False)

# register a file object, monitor it for IO events, data is the data associated with the file object (where a memory address for the accept function is placed)
# register(fileobj, events, data=None)
(sock, selectors.EVENT_READ, accept)

while True:
  '''
  ()
  This looks like a select method, but in fact it automatically chooses whether to use select or epoll depending on the platform.
  It returns a (key, events) tuple, key is a tuple of type namedtuple, which can be used to retrieve the tuple's data.
  The contents of key (fileobj,fd,events,data):
    fileobj The registered file object
    fd is the underlying file descriptor of the fileobj in the first parameter.
    events IO events to wait for
    data Optional. Can store some data related to fileobj, such as the id of sessioin.
  '''
  events = ()   # Monitor for active objects, if not, block here and wait
  for key, mask in events: # There's a target
    callback =    # is the accept function passed at registration
    callback(, mask)  #  That's what passes socket boyfriend

Client:

import socket
tin=()
(('localhost',1234))
while True:
  inp=input('>>>>')
  (('utf8'))
  data=(1024)
  print(('utf8'))

This is the whole content of this article.