Python - Socket Programming

Python Socket Programming

Socket programming là một kỹ thuật trong đó chúng ta giao tiếp giữa hai nút được kết nối trong một mạng, nơi nút máy chủ lắng nghe các yêu cầu đến từ các nút máy khách.

Trong Python, mô-đun socket được sử dụng cho lập trình socket. Mô-đun socket trong thư viện chuẩn bao gồm các chức năng cần thiết cho việc giao tiếp giữa máy chủ và máy khách ở cấp độ phần cứng.

Mô-đun này cung cấp quyền truy cập vào giao diện socket BSD. Nó có sẵn trên tất cả các hệ điều hành như Linux, Windows, MacOS.

What are Sockets?

Soket là các điểm cuối của một kênh truyền thông hai chiều. Soket có thể giao tiếp trong một tiến trình, giữa các tiến trình trên cùng một máy, hoặc giữa các tiến trình trên các lục địa khác nhau.

Một socket được xác định bởi sự kết hợp giữa địa chỉ IP và số cổng. Nó cần được cấu hình đúng ở cả hai đầu để bắt đầu giao tiếp.

connection IP_address

Các socket có thể được triển khai qua nhiều loại kênh khác nhau: socket miền Unix, TCP, UDP, và nhiều loại khác. Thư viện socket cung cấp các lớp cụ thể để xử lý các giao thức truyền thông phổ biến cũng như một giao diện tổng quát để xử lý các giao thức khác.

Thuật ngữ lập trình socket ám chỉ việc thiết lập socket một cách lập trình để có thể gửi và nhận dữ liệu.

Có hai loại giao thức truyền thông -

  • giao thức hướng kết nối

  • giao thức không kết nối

TCP hay Giao thức Điều khiển Truyền (Transmission Control Protocol) là một giao thức hướng kết nối. Dữ liệu được truyền qua các gói bởi máy chủ và được lắp ráp theo thứ tự truyền tải bởi máy nhận. Vì các socket ở cả hai đầu của kết nối cần được thiết lập trước khi bắt đầu, nên phương pháp này đáng tin cậy hơn.

UDP hay Giao thức Datagram Người dùng là không kết nối. Phương thức này không đáng tin cậy vì các socket không yêu cầu thiết lập bất kỳ kết nối nào và quy trình kết thúc để truyền dữ liệu.

Python socket Module

Mô-đun socket được sử dụng để tạo và quản lý lập trình socket cho các nút kết nối trong một mạng. Mô-đun socket cung cấp một lớp socket . Bạn cần tạo một socket bằng cách sử dụng bộ tạo socket.socket() .

Một đối tượng của lớp socket đại diện cho cặp tên máy chủ và số cổng.

Syntax

Cú pháp của hàm khởi tạo socket.socket() như sau –

socket.socket (socket_family, socket_type, protocol=0)

Parameters

  • family − AF_INET theo mặc định. Các giá trị khác - AF_INET6 (tám nhóm bốn chữ số thập lục phân), AF_UNIX, AF_CAN (Mạng Khu vực Điều khiển) hoặc AF_RDS (Soket Datagrams Đáng tin cậy).

  • socket_type − nên là SOCK_STREAM (mặc định), SOCK_DGRAM, SOCK_RAW hoặc có thể là một trong những hằng số SOCK_ khác.

  • protocol − số thường là không và có thể bị bỏ qua.

Return Type

Phương thức này trả về một đối tượng socket.

Khi bạn đã có đối tượng socket, bạn có thể sử dụng các phương thức cần thiết để tạo chương trình client hoặc server của mình.

Server Socket Methods

Socket được khởi tạo trên máy chủ được gọi là socket máy chủ. Các phương thức sau đây có sẵn cho đối tượng socket trên máy chủ −

  • bind() method − Phương thức này gán socket với địa chỉ IP và số cổng đã chỉ định.

  • listen() method − Phương thức này khởi động máy chủ và chạy trong một vòng lặp lắng nghe các yêu cầu kết nối từ khách hàng.

  • accept() method − Khi yêu cầu kết nối bị máy chủ chặn, phương thức này chấp nhận yêu cầu và xác định socket của khách hàng với địa chỉ của nó.

Để tạo một socket trên máy chủ, hãy sử dụng đoạn mã sau −

import socket
server = socket.socket()
server.bind(('localhost',12345))
server.listen()
client, addr = server.accept()
print ("connection request from: " + str(addr))

Theo mặc định, máy chủ được liên kết với địa chỉ IP của máy cục bộ 'localhost' đang lắng nghe tại một số cổng trống tùy ý.

Client Socket Methods

Socket tương tự được thiết lập ở phía khách. Nó chủ yếu gửi yêu cầu kết nối đến socket máy chủ đang lắng nghe tại địa chỉ IP và số cổng của nó.

connect() method

Phương thức này nhận một đối tượng tuple có hai mục làm tham số. Hai mục này là địa chỉ IP và số cổng của máy chủ.

obj=socket.socket()
obj.connect((host,port))

Khi kết nối được máy chủ chấp nhận, cả hai đối tượng socket có thể gửi và/hoặc nhận dữ liệu.

send() method

Máy chủ gửi dữ liệu đến khách hàng bằng cách sử dụng địa chỉ mà nó đã chặn.

client.send(bytes)

Socket của client gửi dữ liệu đến socket mà nó đã thiết lập kết nối.

sendall() method

tương tự như send(). Tuy nhiên, khác với send(), phương thức này tiếp tục gửi dữ liệu từ bytes cho đến khi tất cả dữ liệu đã được gửi hoặc xảy ra lỗi. Không có giá trị nào được trả về khi thành công.

sendto() method

Phương thức này chỉ được sử dụng trong trường hợp giao thức UDP.

recv() method

Phương thức này được sử dụng để lấy dữ liệu được gửi đến khách hàng. Trong trường hợp của máy chủ, nó sử dụng socket từ xa mà yêu cầu đã được chấp nhận.

client.recv(bytes)

recvfrom() method

Phương pháp này được sử dụng trong trường hợp giao thức UDP.

Python - Socket Server

Để viết các máy chủ Internet, chúng ta sử dụng hàm socket có sẵn trong mô-đun socket để tạo một đối tượng socket. Đối tượng socket sau đó được sử dụng để gọi các hàm khác nhằm thiết lập một máy chủ socket.

Bây giờ hãy gọi hàm bind(hostname, port) để chỉ định một cổng cho dịch vụ của bạn trên máy chủ đã cho.

Tiếp theo, gọi phương thức accept của đối tượng đã được trả về. Phương thức này sẽ chờ cho đến khi một khách hàng kết nối với cổng mà bạn đã chỉ định, và sau đó trả về một đối tượng kết nối đại diện cho kết nối đến khách hàng đó.

Example of Server Socket

import socket
host = "127.0.0.1"
port = 5001
server = socket.socket()
server.bind((host,port))
server.listen()
conn, addr = server.accept()
print ("Connection from: " + str(addr))
while True:
   data = conn.recv(1024).decode()
   if not data:
      break
   data = str(data).upper()
   print (" from client: " + str(data))
   data = input("type message: ")
   conn.send(data.encode())
conn.close()

Python - Socket Client

Hãy viết một chương trình client rất đơn giản, mở một kết nối đến cổng 5001 và localhost đã cho. Việc tạo một client socket bằng cách sử dụng hàm module socket của Python là rất đơn giản.

socket.connect(hosname, port) mở một kết nối TCP tới tên máy chủ trên cổng. Khi bạn đã mở được một socket, bạn có thể đọc từ nó như bất kỳ đối tượng IO nào. Khi hoàn tất, hãy nhớ đóng nó lại, như bạn sẽ đóng một tệp.

Example of Client Socket

Đoạn mã sau là một client rất đơn giản kết nối đến một host và port nhất định, đọc bất kỳ dữ liệu nào có sẵn từ socket, và sau đó thoát khi nhập 'q'.

import socket
host = '127.0.0.1'
port = 5001
obj = socket.socket()
obj.connect((host,port))
message = input("type message: ")
while message != 'q':
   obj.send(message.encode())
   data = obj.recv(1024).decode()
   print ('Received from server: ' + data)
   message = input("type message: ")
obj.close()
  • Chạy mã Server trước. Nó bắt đầu lắng nghe.

  • Sau đó, khởi động mã khách. Nó gửi yêu cầu.

  • Yêu cầu đã được chấp nhận. Địa chỉ khách hàng đã được xác định.

  • Gõ một số văn bản và nhấn Enter.

  • Dữ liệu nhận được được in ra. Gửi dữ liệu đến khách hàng.

  • Dữ liệu từ máy chủ đã được nhận.

  • Vòng lặp sẽ kết thúc khi nhập 'q'.

Sự tương tác giữa máy chủ và máy khách được hiển thị dưới đây −

server_client_interaction

Chúng tôi đã triển khai giao tiếp client-server bằng mô-đun socket trên máy cục bộ. Để đặt mã máy chủ và máy khách trên hai máy khác nhau trong một mạng, chúng tôi cần tìm địa chỉ IP của máy chủ.

Trên Windows, bạn có thể tìm địa chỉ IP bằng cách chạy lệnh ipconfig. Lệnh ifconfig là lệnh tương đương trên Ubuntu.

ipv4_address

Thay đổi chuỗi máy chủ trong cả mã máy chủ và mã khách với giá trị địa chỉ IPv4 và chạy chúng như trước.

Python File Transfer with Socket Module

Chương trình dưới đây minh họa cách giao tiếp qua socket có thể được sử dụng để truyền một tệp từ máy chủ đến máy khách.

Server Code

Mã để thiết lập kết nối giống như trước. Sau khi yêu cầu kết nối được chấp nhận, một tệp trên máy chủ được mở ở chế độ nhị phân để đọc, và các byte được đọc liên tiếp và gửi đến luồng khách hàng cho đến khi kết thúc tệp được đạt tới.

import socket
host = "127.0.0.1"
port = 5001
server = socket.socket()
server.bind((host, port))
server.listen()
conn, addr = server.accept()
data = conn.recv(1024).decode()
filename='test.txt'
f = open(filename,'rb')
while True:
   l = f.read(1024)
   if not l:
      break
   conn.send(l)
   print('Sent ',repr(l))
f.close()
print('File transferred')
conn.close()

Client Code

Ở phía khách hàng, một tệp mới được mở ở chế độ wb . Dòng dữ liệu nhận được từ máy chủ được ghi vào tệp. Khi dòng dữ liệu kết thúc, tệp đầu ra sẽ được đóng lại. Một tệp mới sẽ được tạo trên máy khách.

import socket

s = socket.socket()
host = "127.0.0.1"
port = 5001

s.connect((host, port))
s.send("Hello server!".encode())

with open('recv.txt', 'wb') as f:
   while True:
      print('receiving data...')
      data = s.recv(1024)
      if not data:
         break
      f.write(data)
      
f.close()
print('Successfully received')
s.close()
print('connection closed')

The Python socketserver Module

Mô-đun socketserver trong thư viện chuẩn của Python là một khung để đơn giản hóa nhiệm vụ viết máy chủ mạng. Có các lớp sau trong mô-đun, đại diện cho các máy chủ đồng bộ −

socketserver_module

Các lớp này làm việc với các lớp RequestHandler tương ứng để triển khai dịch vụ. BaseServer là lớp cha của tất cả các đối tượng Server trong mô-đun.

TCPServer lớp sử dụng giao thức TCP của internet, để cung cấp các luồng dữ liệu liên tục giữa client và server. Hàm khởi tạo tự động cố gắng gọi server_bind() và server_activate(). Các tham số khác được truyền cho lớp cơ sở BaseServer.

Bạn cũng phải tạo một lớp con của lớp StreamRequestHandler . Lớp này cung cấp các thuộc tính self.rfile và self.wfile để đọc hoặc ghi dữ liệu yêu cầu hoặc trả lại dữ liệu cho khách hàng.

  • UDPServer DatagramRequestHandler − Các lớp này được thiết kế để sử dụng cho giao thức UDP.

  • DatagramRequestHandler UnixDatagramServer − Các lớp này sử dụng socket miền Unix; chúng không có sẵn trên các nền tảng không phải Unix.

Server Code

Bạn phải viết một lớp RequestHandler. Nó được khởi tạo một lần cho mỗi kết nối đến máy chủ và phải ghi đè phương thức handle() để thực hiện giao tiếp với khách hàng.

import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
   def handle(self):
      self.data = self.request.recv(1024).strip()
      host,port=self.client_address
      print("{}:{} wrote:".format(host,port))
      print(self.data.decode())
      msg=input("enter text .. ")
      self.request.sendall(msg.encode())

Trên số cổng được chỉ định của máy chủ, một đối tượng của lớp TCPServer gọi phương thức forever() để đưa máy chủ vào chế độ lắng nghe và chấp nhận các yêu cầu đến từ khách hàng.

if __name__ == "__main__":
   HOST, PORT = "localhost", 9999
   with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
      server.serve_forever()

Client Code

Khi làm việc với socketserver, mã của client gần giống với ứng dụng client socket.

import socket
import sys

HOST, PORT = "localhost", 9999

while True:
   with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
      # Connect to server and send data
      sock.connect((HOST, PORT))
      data = input("enter text .. .")
      sock.sendall(bytes(data + "\n", "utf-8"))
      
      # Receive data from the server and shut down
      received = str(sock.recv(1024), "utf-8")
      print("Sent: {}".format(data))
      print("Received: {}".format(received))

Chạy mã máy chủ trong một cửa sổ dòng lệnh. Mở nhiều cửa sổ cho các phiên bản khách. Bạn có thể mô phỏng một cuộc giao tiếp đồng thời giữa máy chủ và nhiều khách hàng.

Server Client-1 Client-2
D:\socketsrvr>python myserver.py 127.0.0.1:54518 wrote: from client-1 enter text .. hello 127.0.0.1:54522 wrote: how are you enter text .. fine 127.0.0.1:54523 wrote: from client-2 enter text .. hi client-2 127.0.0.1:54526 wrote: good bye enter text .. bye bye 127.0.0.1:54530 wrote: thanks enter text .. bye client-2 D:\socketsrvr>python myclient.py enter text .. . from client-1 Sent: from client-1 Received: hello enter text .. . how are you Sent: how are you Received: fine enter text .. . good bye Sent: good bye Received: bye bye enter text .. . D:\socketsrvr>python myclient.py enter text .. . from client-2 Sent: from client-2 Received: hi client-2 enter text .. . thanks Sent: thanks Received: bye client-2 enter text .. .