Hướng dẫn python socket recv hangs - python socket recv bị treo

I have implemented a socket server and client using python. Idea is to simply send an image from client to server and get back a text message from the server. The client will select an image file (.jpg) from file system and send to server. The server would receive it and save it with a different name (current date time stamp) and send back a processed text message. The image is sent and saved successfully at the server side but the issue is that client hangs when .recv() method is called to get message from server.

Following is the code

Server

import socket
import datetime
import time
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1",12345))
server.listen()

print("Server started and listening at port 12345")

BUFFER_SIZE=4096

while True:
    client_socket, address = server.accept();
    # client_socket.setblocking(False)
    print(f"Connection from {address} has been established.")
    file_name = str(datetime.datetime.now()).replace(" ", "").replace(":", "-")
    file_name = file_name + ".jpg"
    
    with open(file_name, "wb") as file:
        recv_data = client_socket.recv(BUFFER_SIZE)
        while (recv_data):
            file.write(recv_data)
            recv_data = client_socket.recv(BUFFER_SIZE)
    
    print("File writing done, preapring data to send...")
    client_socket.send(bytes("Hey there!!!", "utf-8"))
    client_socket.close()

Client

import socket
import threading
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 12345))

BUFFER_SIZE = 4096

with open("client_file.jpg", 'rb') as file:
    file_data = file.read(BUFFER_SIZE)
    while (file_data):
        client.send(file_data)
        file_data = file.read(BUFFER_SIZE)
msg = client.recv(1024) # <-------[[[ hangs here ]]]
print(msg.decode("utf-8"))

Đã đăng vào thg 2 18, 2021 6:26 SA 4 phút đọc 4 phút đọc

Socket là gì?

Socket là giao diện lập trình ứng dụng mạng được dùng để truyền và nhận dữ liệu trên internet. Giữa hai chương trình chạy trên mạng cần có một liên kết giao tiếp hai chiều (two-way communication) để kết nối 2 process trò chuyện với nhau. Điểm cuối (endpoint) của liên kết này được gọi là socket.

Một chức năng khác của socket là giúp các tầng TCP hoặc TCP Layer định danh ứng dụng mà dữ liệu sẽ được gửi tới thông qua sự ràng buộc với một cổng port (thể hiện là một con số cụ thể), từ đó tiến hành kết nối giữa client và server.

Mô tả mô hình

  1. Trước tiên chúng ta sẽ tạo ra một máy chủ bằng cách mở một socket - Socket()
  2. Sau đó chúng ta sẽ liên kết nó với một host hoặc một máy và một port - Bind()
  3. Tiếp theo server sẽ bắt đầu lắng nghe trên port đó - Listen()
  4. Yêu cầu kết nối từ client được gửi tới server - Connect()
  5. Server sẽ accept yêu cầu từ client và sau đó kết nối được thiết lập - Accept()
  6. Bây giờ cả hai đều có thể gửi và nhận tin tại thời điểm đó - Read() / Write()
  7. Và cuối cùng khi hoàn thành chúng có thể đóng kết nối - Close()

Bây giờ chúng ta thực hành lập trình socket bằng module socket trong python.

Tạo server

Chúng ta sẽ tạo một DCP IP server nhằm nhiệm vụ lắng nghe trên một cổng cho client gửi request đến.

Các bước để tạo lên 1 chương trình phía server:

  1. Tạo socket với hàm socket () chúng ta có thể truyền vào tham số hoặc để trống, ở đây mình truyền vào 2 tham số là hằng socket.AF_INET (tham số truyền vào phiên bản IP chúng ta sẽ sử dụng ở đây là phiên bản 4), tiếp theo là hằng số socket.SOCK_STREAM chỉ loại kết nối TCP IP hoặc UDP,.. (ở đây mình dùng TCP)
  2. Gán địa chỉ cho socket
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    0 với các tham số là máy chủ và cổng
  3. Chỉ định socket lắng nghe kết nối
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    1 có thể điền số lượng tối đa các kết nối đang chờ
  4. Chờ/chấp nhận kết nối
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    2. Hàm này trả về hai giá tri
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    3: kết nối;
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    4: địa chỉ của request hoặc địa chỉ của client
  5. Sau đó server có thể gửi dữ liệu qua hàm
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    5.
  6. Sau khi thực hiện xong thì đóng kết nối lại
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    6.
#server.py 

import socket 

# Định nghĩa host và port mà server sẽ chạy và lắng nghe
host = 'localhost'
port = 4000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))

s.listen(1) # 1 ở đây có nghĩa chỉ chấp nhận 1 kết nối
print("Server listening on port", port)

c, addr = s.accept()
print("Connect from ", str(addr))

#server sử dụng kết nối gửi dữ liệu tới client dưới dạng binary
c.send(b"Hello, how are you")
c.send("Bye", encode())
c.close()

Tạo client

Chúng ta sẽ tạo một DCP IP client để kết nối với server ở cổng 4000.

  1. Tạo socket với hàm
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    7
  2. Taọ kết nối với server với hàm
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    8
  3. Đọc sữ liệu được server gửi tới
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    9
  4. Đóng socket
    import socket
    import threading
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 12345))
    
    BUFFER_SIZE = 4096
    
    with open("client_file.jpg", 'rb') as file:
        file_data = file.read(BUFFER_SIZE)
        while (file_data):
            client.send(file_data)
            file_data = file.read(BUFFER_SIZE)
    msg = client.recv(1024) # <-------[[[ hangs here ]]]
    print(msg.decode("utf-8"))
    
    6
#client .py

import socket 

# Như mình đã nói ở trên thì chúng ta không truyền tham số vào vẫn ok
s = socket.socket()
s.connect(("localhost", 4000)) 

# 1024 là số bytes mà client có thể nhận được trong 1 lần
# Phần tin nhắn đầu tiên
msg = s.recv(1024)

# Phần tin nhắn tiếp theo 
while msg:
  print("Recvied ", msg.decode())
  msg = s.recv(1024)

s.close()

Sau khi chỉ chạy server.py rồi chạy client.py thì ta thấy client đã nhận được dữ liệu do server gửi tới.

Gửi file

Chỉnh sửa nội dùng 2 file trên một chút thì chúng ta có thể thực hiện chức năng server gửi nội dung file mà client yêu cầu tới.

Phía server

Server sẽ gửi nội dung một file mà client yêu cầu.

# fileserver.py

import socket 

host = 'localhost'
port = 6767

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(1)
print("Server listening on port", port)

c, addr = s.accept()

#Nhận tên file do client gửi tới
filename = c.recv(1024)
try:
  f =  open(filename, 'rb')
  content = f.read()
  
  # Gửi dữ liệu trong file cho client
  c.send(content)
  f.close()
  
except FileExistsError:
  c.send("File not found") #nếu file không tồn tại bảo với client rằng "File not found"
  
c.close()

Phía client

Nhập vào tên file muốn lấy dữ liệu

# fileclient.py

import socket 
s = socket.socket()
s.connect(("localhost", 6767)) #lắng nghe ở cổng 6767

#Nhập vào tên file 
filename = input("Enter a filename ")

#Gửi tên file cho server
s.send(filename.encode())

#Nhận được dữ liệu từ server gửi tới
content = s.recv(1024)

print(content.decode())
s.close()

Đây là kết quả nhận được từ server.

Trên đây là những ví dụ đơn giản về lập trình socket trong python mà mình tìm hiểu được ^.^

All rights reserved