Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Python

การเขียนโปรแกรม Socket ด้วย Multi-threading ใน Python

แนวคิด Multithreading

Multithreading เป็นแนวคิดหลักของภาษาโปรแกรมมิ่งสมัยใหม่เกือบทุกภาษา โดยเฉพาะ Python เนื่องจากการนำเธรดไปปฏิบัติที่เรียบง่าย

เธรด (Thread) คือ โปรแกรมย่อยภายในโปรแกรมที่สามารถรันได้อย่างอิสระจากส่วนอื่นของโค้ด เธรดทำงานในบริบทเดียวกัน แชร์ทรัพยากรที่รันได้ของโปรแกรม เช่น หน่วยความจำ

เมื่อในกระบวนการเดียว เรากำลังรันเธรดหลายตัวพร้อมกัน เรียกว่า multithreading

โมดูล Python Multithreading สำหรับการนำเธรดไปปฏิบัติ

เพื่อนำเธรดไปปฏิบัติในโปรแกรม Python มีสองโมดูล:

  • thread (สำหรับ Python 2.x) หรือ _thread (สำหรับ Python 3.x) module
  • threading module

โมดูล thread สร้างเธรดเป็นฟังก์ชัน ในขณะที่โมดูล threading ให้วิธีการเชิงออบเจกต์ในการสร้างเธรด

ไวยากรณ์

_thread.start_new_thread(func, args[, kwargs])

ข้างต้นเริ่มเธรดใหม่และคืนค่าตัวระบุของมัน อาร์กิวเมนต์แรกคือฟังก์ชัน func ซึ่งเธรดจะรันด้วยอาร์กิวเมนต์ที่สองที่มีทูเพิลพร้อมรายการอาร์กิวเมนต์ตำแหน่ง อาร์กิวเมนต์ kwargs ที่เป็นตัวเลือกระบุดิกชันนารีของอาร์กิวเมนต์คีย์เวิร์ด เมื่อฟังก์ชันคืนค่า เธรดจะจบอย่างเงียบๆ

ในที่นี้ เราจะเห็นตัวอย่างพื้นฐานของแอปพลิเคชัน client-server โดย clients เปิดการเชื่อมต่อ socket และส่งคำขอไปยัง server server ตอบกลับ

เมื่อรันโดยไม่มีอาร์กิวเมนต์ โปรแกรมนี้เริ่มต้นด้วย TCP socket server ที่ฟังการเชื่อมต่อที่ 127.0.0.1 บนพอร์ต 8000

client_thread1.py

import socket
import sys

def main():
    soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    host = "127.0.0.1"
    port = 8000
    try:
        soc.connect((host, port))
    except:
        print("Connection Error")
        sys.exit()
    print("Please enter 'quit' to exit")
    message = input(" -> ")
    while message != 'quit':
        soc.sendall(message.encode("utf8"))
        if soc.recv(5120).decode("utf8") == "-":
            pass  # null operation
        message = input(" -> ")
    soc.send(b'--quit--')

if __name__ == "__main__":
    main()

ในขณะที่โปรแกรม server คือ:

server_thread1.py

import socket
import sys
import traceback
from threading import Thread

def main():
    start_server()

def start_server():
    host = "127.0.0.1"
    port = 8000  # arbitrary non-privileged port
    soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    print("Socket created")
    try:
        soc.bind((host, port))
    except:
        print("Bind failed. Error : " + str(sys.exc_info()))
        sys.exit()
    soc.listen(6)  # queue up to 6 requests
    print("Socket now listening")
    # infinite loop - do not reset for every requests
    while True:
        connection, address = soc.accept()
        ip, port = str(address[0]), str(address[1])
        print("Connected with " + ip + ":" + port)
        try:
            Thread(target=client_thread, args=(connection, ip, port)).start()
        except:
            print("Thread did not start.")
            traceback.print_exc()
    soc.close()

def clientThread(connection, ip, port, max_buffer_size=5120):
    is_active = True
    while is_active:
        client_input = receive_input(connection, max_buffer_size)
        if "--QUIT--" in client_input:
            print("Client is requesting to quit")
            connection.close()
            print("Connection " + ip + ":" + port + " closed")
            is_active = False
        else:
            print("Processed result: {}".format(client_input))
            connection.sendall("-".encode("utf8"))

def receive_input(connection, max_buffer_size):
    client_input = connection.recv(max_buffer_size)
    client_input_size = sys.getsizeof(client_input)
    if client_input_size > max_buffer_size:
        print("The input size is greater than expected {}".format(client_input_size))
    decoded_input = client_input.decode("utf8").rstrip()
    result = process_input(decoded_input)
    return result

def process_input(input_str):
    print("Processing the input received from client")
    return "Hello " + str(input_str).upper()

if __name__ == "__main__":
    main()

เมื่อรันสคริปต์ข้างต้น ให้รัน server_thread1.py ในเทอร์มินัล:

python server_thread1.py
Socket created
Socket now listening

เราจะดูหน้าต่าง server และเข้าใจโฟลว์ ตอนนี้เปิดเทอร์มินัล client หลายตัว รัน client thread

python client_thread1.py
Enter 'quit' to exit
-> Zack
->

ในเทอร์มินัลอื่น รันโปรแกรม client อีกตัว และดูหน้าต่าง server ด้วย:

python client_thread1.py
Enter 'quit' to exit
-> Python
-> quit

เทอร์มินัลอีกตัว รัน client thread:

python client_thread1.py
Enter 'quit' to exit
-> world!
-> Anothny
->

และเราจะเห็นหน้าต่าง server แสดงผลลัพธ์ประมาณนี้:

Socket created
Socket now listening
Connected with 127.0.0.1:50275
Processing the input received from client
Processed result: Hello ZACK
Connected with 127.0.0.1:50282
Processing the input received from client
Processed result: Hello PYTHON
Processing the input received from client
Client is requesting to quit
Connection 127.0.0.1:50282 closed
Connected with 127.0.0.1:50285
Processing the input received from client
Processed result: Hello WORLD!
Processing the input received from client
Processed result: Hello ANOTHNY

ดังนั้น เธรดจึงให้เทคนิคที่พบบ่อยที่สุดในการจัดการการเชื่อมต่อ socket หลายรายการและ clients