Python network programming

Python provides two levels of access to network services. :

socket

View the help of the socket class as follows

import socket  #Import the socket module
>>> help(socket.socket)

Focus on the initialization function:

__ init__(self, family=<AddressFamily.AF_INET:2>, type=<SocketKind.SOCK_STREAM:1>, proto=0, fileno=None)

The following is a TCP chat room and a UDP chat room

TCP chat room##

Outline design###

Get the handling of multiple connections

Open the accept thread, the execution of the accept operation starts to block, and when there is a client connection, another thread recv is opened for data reception processing. Then the accept thread continues to block, waiting for subsequent client connections.

Blocking processing

When the server processes the client connection, there are two obstructions, namely:

Therefore, two threads need to be opened for separate processing, otherwise the main thread will be blocked.

Handling of active client disconnection

When the client actively disconnects, if the server is not notified, the client connection saved on the server will not be cleaned up, which is unreasonable. Therefore, when the client actively disconnects, we agree at the application layer that before the client launches, it needs to send the /quit command to the server, and then the server closes the socket.

TCP chat room-server

The server side of the chat room mainly monitors the port, handles the connection from the client side, and distributes data to all client sides

Code

import socket
import threading

classTcpChatServer:
 def __init__(self, ip='192.168.110.13', port=9001):
  self.ip = ip
  self.port = port
  self.clients ={}
  self.sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
  self.event = threading.Event()

 def recv(self, so, ip ,port):while not self.event.is_set():
   data = so.recv(1024).decode()  #Convert the received byte data bytes to utf-8 format string
   if data.strip()=='/quit':  #Handling when the client actively disconnects
    so.close()
    self.clients.pop((ip, port))returnfor s in self.clients.values():  #Broadcast
    s.send('{}:{}\n{}'.format(ip, port, data).encode())

 def accept(self):while not self.event.is_set():
   so,(ip, port)= self.sock.accept()
   self.clients[(ip, port)]= so
   # Because so.recv will block, so a separate thread is opened to process the receiving part of the data. In this way accept can continue to accept links from other clients
   threading.Thread(target=self.recv, args=(so, ip, port), name='client-{}:{}'.format(ip, port)).start()

 def start(self):
  self.sock.bind((self.ip, self.port))
  self.sock.listen()
  t = threading.Thread(target=self.accept, daemon=True)  #In order not to block the main thread, open a separate thread to handle accept (accept will block the thread)
  try:
   t.start()
   t.join()  #Block until KeyboardInterrupt is obtained
  except KeyboardInterrupt:
   self.stop()

 def stop(self):for s in self.clients.values():
   s.close()
  self.sock.close()
  self.event.set()  #Stop all loops

if __name__ =='__main__':
 tcp_chat_server =TcpChatServer()
 tcp_chat_server.start()

TCP chat room-client

The client side of the chat room mainly initiates a connection, connects to the server side, and accepts messages broadcast and distributed from the server side.

Code

import socket
import threading

classTcpChatClient:
 def __init__(self):
  self.sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
  self.event = threading.Event()

 def recv(self):  #The client needs to always receive messages broadcast and distributed by the server
  while not self.event.is_set():
   data = self.sock.recv(1024).decode()
   data = data.strip()print(data)

 def send(self):  #Send message
  while not self.event.is_set():
   data =input()
   self.sock.send(data.encode())if data.strip()=='/quit':  #send/Close itself when quit
    self.stop()

 def start(self, ip, port):
  self.sock.connect((ip, port))
  s = threading.Thread(target=self.send, daemon=False)
  r = threading.Thread(target=self.recv, daemon=False)
  s.start()
  r.start()

 def stop(self):
  self.sock.close()
  self.event.set()if __name__ =='__main__':
 tcp_chat_client =TcpChatClient()
 tcp_chat_client.start('192.168.110.13',9001)

UDP chat room##

Outline design###

Blocking processing

When the UDP server receives the client's message, the socket.recvfrom(1024) method is used to save the client's address information. This method will block the current thread, so the thread needs to be opened for separate processing.

Handling of active client disconnection

After the UDP client is actively closed, the server cannot detect that the client has been closed. We can use the following two methods:

  1. If it is similar to the method that TCP adopts the agreed exit instruction, then the client will call the close method after sending the exit instruction, and then the server will remove the corresponding client in the client dictionary according to the received instruction.
  2. It is also possible to send heartbeats to the server periodically through the client, and the server uses the heartbeat to determine whether the client process is alive.

UDP chat room-server

The UDP server program starts a thread waiting to receive the client's data, and then broadcasts it to other clients, and checks whether the heartbeats of all connections have timed out.

Code

import socket
import datetime
import threading

classUdpChatServer:
 def __init__(self, ip='192.168.110.13', port=9001):
  self.addr =(ip, port)
  self.sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
  self.clients ={}
  self.event = threading.Event()
    
 def recv(self):while not self.event.is_set():
   data, addr = self.sock.recvfrom(1024)
   data = data.decode().strip()
   now = datetime.datetime.now()if data =='#ping#':  #Determine whether to receive a heartbeat
    self.clients[addr]= now  #When receiving the heartbeat, save the client address and update the timestamp
    continue
                
   disconnected =set()  #Judge all broken links without receiving data once
   for addr, timestamp in self.clients.items():if(now - timestamp).total_seconds()>10:  #Failure condition: 2 times (that is, 10s), the client is judged to be closed without receiving a heartbeat
     disconnected.add(addr)else:
     self.sock.sendto('{}:{}\n{}'.format(addr[0], addr[1], data).encode(), addr)for addr in disconnected:
    self.clients.pop(addr)
            
 def start(self):
  self.sock.bind(self.addr)  #After binding the port, start the thread and always accept the client&#39;s data
  t = threading.Thread(target=self.recv(), daemon=True)try:
   t.start()
   t.join()
  except KeyboardInterrupt:
   self.stop()
        
 def stop(self):
  self.event.set()
  self.sock.close()if __name__ =='__main__':
 udp_chat_server =UdpChatServer()
 udp_chat_server.start()

UDP chat room-client

The main thread of the UDP client has been waiting for the user to input data and then sending the data to the server. At the same time, a heartbeat process and a thread for receiving broadcast data from the server are started.

Code

import socket
import threading
import time

classUdpChatClient:
 def __init__(self, ip, port):
  self.addr =(ip, port)
  self.sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
  self.event = threading.Event()
    
 def heartbeat(self):  #Heartbeat thread function: send a heartbeat every 5s
  while not self.event.wait(5):
   self.sock.sendto(b'#ping#', self.addr)
            
 def recv(self):  #Waiting to receive the data broadcast by the udp server
  while not self.event.is_set():
   data = self.sock.recv(1024)print(data.decode())
    
 def start(self):
  threading.Thread(target=self.heartbeat, name='heartbeat', daemon=True).start()
  threading.Thread(target=self.recv, name='recv', daemon=True).start()print('Please speak in 5s')
  time.sleep(5)  #Because the server must receive a heartbeat before saving the secondary client, it needs to wait for 5s
  print('Please start speaking')while not self.event.is_set():
   data =input('')
   data = data.strip()if data =='/quit':
    self.event.set()
    self.sock.close()return
   self.sock.sendto(data.encode(), self.addr)if __name__ =='__main__':
 udp_chat_client =UdpChatClient('192.168.110.13',9001)
 udp_chat_client.start()

SocketServer

TODO (Flowsnow): Rewrite the TcpChatServer and UdpChatServer of the chat room program

Appendix 1: The essential difference between TCP and UDP

Attachment 2: Reference Materials

  1. socketserver — A framework for network servers

Recommended Posts

Python network programming
Detailed Python IO programming
Python GUI interface programming
Talking about Python functional programming
Google Python Programming Style Guide
Python3 script programming commonly used.md
Analysis of Python object-oriented programming
XTU programming Python training three
Black Hat Programming Application Python2
Black hat programming application of Python1
How to understand python object-oriented programming
Python classic programming questions: string replacement
Python multithreading
Python CookBook
Python FAQ
Python3 dictionary
python (you-get)
Python string
Python basics
Python descriptor
Python basics 2
Can I learn python without programming foundation
Python exec
Python notes
Python3 tuple
CentOS + Python3.6+
Python advanced (1)
Python IO
Python multithreading
Python toolchain
Python3 list
Python multitasking-coroutine
Python overview
python introduction
Python analytic
Python basics
07. Python3 functions
Python basics 3
Python multitasking-threads
Python functions
Analysis of common methods of Python multi-process programming
python sys.stdout
python operator
Python entry-3
Centos 7.5 python3.6
Python string
python queue Queue
Python basics 4
Python basics 5