2009-05-04 33 views
14

Hãy nói rằng tôi muốn đọc một dòng từ một ổ cắm, sử dụng socket mô-đun tiêu chuẩn:socket Python đệm

def read_line(s): 
    ret = '' 

    while True: 
     c = s.recv(1) 

     if c == '\n' or c == '': 
      break 
     else: 
      ret += c 

    return ret 

gì chính xác xảy ra trong s.recv(1)? Nó sẽ phát hành một cuộc gọi hệ thống mỗi lần? Tôi đoán tôi nên thêm một số đệm, dù sao:

Đối phù hợp nhất với phần cứng và mạng thực tế, giá trị của bufsize nên một sức mạnh tương đối nhỏ của 2, ví dụ, 4096.

http://docs.python.org/library/socket.html#socket.socket.recv

Nhưng dường như không dễ dàng để viết bộ đệm hiệu quả và an toàn theo chủ đề. Nếu tôi sử dụng file.readline() thì sao?

# does this work well, is it efficiently buffered? 
s.makefile().readline() 
+0

"Hệ thống có phát ra cuộc gọi hệ thống mỗi lần không?" Vì sao vấn đề này? –

+6

Vì cuộc gọi hệ thống chậm. Tốt hơn là tìm nạp một lượng lớn dữ liệu (nếu có), sau đó xử lý nó. Bây giờ tôi biết rằng Python không phải là đặc biệt nhanh, và có lẽ điều này không thực sự quan trọng. Nhưng các tài liệu nói rằng nó tốt hơn để đọc bởi khối lớn anyway. –

+7

Lưu ý rằng việc xây dựng một chuỗi bằng cách sử dụng '+ =' là một không-không vì nó có khả năng bậc hai, trong khi xây dựng một danh sách bằng cách nối thêm 'str.join' vào cuối luôn luôn là tuyến tính. –

Trả lời

18

Cuộc gọi recv() được xử lý trực tiếp bằng cách gọi hàm C thư viện.

Nó sẽ chặn việc chờ ổ cắm có dữ liệu. Trong thực tế, nó sẽ chỉ cho phép khối cuộc gọi hệ thống recv().

file.readline() là triển khai đệm hiệu quả. Nó không phải là luồng an toàn, bởi vì nó giả định nó là người duy nhất đọc tệp. (Ví dụ bằng cách đệm đầu vào sắp tới.)

Nếu bạn đang sử dụng đối tượng tệp, mỗi lần read() được gọi với đối số dương, mã cơ bản sẽ chỉ số lượng dữ liệu được yêu cầu, trừ khi dữ liệu đã được lưu vào bộ đệm.

Nó sẽ được đệm nếu:

  • bạn đã gọi readline(), mà đọc một bộ đệm đầy đủ

  • cuối dòng là trước khi kết thúc của bộ đệm

Vì vậy, để lại dữ liệu trong bộ đệm. Nếu không thì bộ đệm nói chung không được lấp đầy.

Mục tiêu của câu hỏi không rõ ràng. nếu bạn cần xem dữ liệu có sẵn trước khi đọc hay không, bạn có thể select() hoặc đặt ổ cắm thành chế độ không chặn với s.setblocking(False). Sau đó, lần đọc sẽ trả về trống, thay vì chặn, nếu không có dữ liệu chờ.

Bạn có đang đọc một tệp hoặc ổ cắm có nhiều luồng không? Tôi sẽ đặt một nhân viên duy nhất vào việc đọc ổ cắm và cho các vật phẩm nhận được vào một hàng đợi để xử lý bởi các chủ đề khác.

Đề xuất tư vấn Python Socket Module sourceC Source that makes the system calls.

+0

Tôi không thực sự biết tại sao tôi hỏi về an toàn luồng, tôi không cần nó trong dự án hiện tại của tôi. Trong thực tế, tôi muốn viết lại một chương trình Java bằng Python. Trong Java thật dễ dàng để có được đọc đệm, và tôi đã tự hỏi nếu mô-đun socket của Python cung cấp cùng một đệm (trên thực tế, tôi tự hỏi tại sao một người nào đó sẽ không muốn đệm và gọi trực tiếp các cuộc gọi hệ thống thay thế). –

+0

realines() không phải là thời gian thực. do đó, nó vô ích cho các dịch vụ TCP tương tác như SMTP, readline dường như làm việc mặc dù. – Jasen

22

Nếu bạn quan tâm đến hiệu suất và kiểm soát ổ cắm hoàn toàn (bạn không chuyển nó vào thư viện), hãy thử thực hiện bộ đệm của riêng bạn bằng Python - Python string.find và string.split và thật nhanh chóng.

def linesplit(socket): 
    buffer = socket.recv(4096) 
    buffering = True 
    while buffering: 
     if "\n" in buffer: 
      (line, buffer) = buffer.split("\n", 1) 
      yield line + "\n" 
     else: 
      more = socket.recv(4096) 
      if not more: 
       buffering = False 
      else: 
       buffer += more 
    if buffer: 
     yield buffer 

Nếu bạn mong đợi payload để bao gồm dòng mà không phải là quá lớn, mà nên chạy khá nhanh, và tránh nhảy qua quá nhiều lớp chức năng cuộc gọi không cần thiết. Tôi muốn được thú vị khi biết cách so sánh này với tệp.readline() hoặc sử dụng socket.recv (1).

6
def buffered_readlines(pull_next_chunk, buf_size=4096): 
    """ 
    pull_next_chunk is callable that should accept one positional argument max_len, 
    i.e. socket.recv or file().read and returns string of up to max_len long or 
    empty one when nothing left to read. 

    >>> for line in buffered_readlines(socket.recv, 16384): 
    ... print line 
    ... 
    >>> # the following code won't read whole file into memory 
    ... # before splitting it into lines like .readlines method 
    ... # of file does. Also it won't block until FIFO-file is closed 
    ... 
    >>> for line in buffered_readlines(open('huge_file').read): 
    ... # process it on per-line basis 
     ... 
    >>> 
    """ 
    chunks = [] 
    while True: 
    chunk = pull_next_chunk(buf_size) 
    if not chunk: 
     if chunks: 
     yield ''.join(chunks) 
     break 
    if not '\n' in chunk: 
     chunks.append(chunk) 
     continue 
    chunk = chunk.split('\n') 
    if chunks: 
     yield ''.join(chunks + [chunk[0]]) 
    else: 
     yield chunk[0] 
    for line in chunk[1:-1]: 
     yield line 
    if chunk[-1]: 
     chunks = [chunk[-1]] 
    else: 
     chunks = []