2009-09-24 36 views
12

Tôi tự dạy mình mạng Python, và tôi nhớ lại khi tôi đang dạy bản thân mình, tôi bắt gặp this page, vì vậy tôi đã sao chép các tập lệnh, cập nhật chúng cho Python 3.1.1 và chạy chúng. Họ làm việc hoàn hảo.Tại sao máy chủ lưu trữ hủy kết nối?

Sau đó, tôi đã thực hiện một vài sửa đổi. Mục tiêu của tôi là làm điều gì đó đơn giản:

  1. Khách hàng chọn một số nguyên và gửi đến máy chủ.
  2. Máy chủ nhận số nguyên đã chọn, giải nén nó, tăng gấp đôi, sau đó chọn và gửi lại cho khách hàng.
  3. Khách hàng nhận được số nguyên đã được ngâm (và tăng gấp đôi), giải mã nó và xuất kết quả.

Đây là máy chủ:

import pickle 
import socket 
import threading 

class ClientThread(threading.Thread): 
    def __init__(self, channel, details): 
     self.channel = channel 
     self.details = details 
     threading.Thread.__init__ (self) 

    def run(self): 
     print('Received connection:', self.details[0]) 
     request = self.channel.recv(1024) 
     response = pickle.dumps(pickle.loads(request) * 2) 
     self.channel.send(response) 
     self.channel.close() 
     print('Closed connection:', self.details [ 0 ]) 

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
server.bind(('', 2727)) 
server.listen(5) 

while True: 
    channel, details = server.accept() 
    ClientThread(channel, details).start() 

Và đây là các khách hàng:

import pickle 
import socket 
import threading 

class ConnectionThread(threading.Thread): 
    def run(self): 
     client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     client.connect(('localhost', 2727)) 

     for x in range(10): 
      client.send(pickle.dumps(x)) 
      print('Sent:',str(x)) 
      print('Received:',repr(pickle.loads(client.recv(1024)))) 

     client.close() 

for x in range(5): 
    ConnectionThread().start() 

Các máy chủ chạy tốt, và khi tôi chạy client nó kết nối thành công và bắt đầu gửi các số nguyên và nhận chúng trở lại gấp đôi như mong đợi. Tuy nhiên, rất nhanh chóng ngoại lệ:

Exception in thread Thread-2: 
Traceback (most recent call last): 
    File "C:\Python30\lib\threading.py", line 507, in _bootstrap_inner 
    self.run() 
    File "C:\Users\Imagist\Desktop\server\client.py", line 13, in run 
    print('Received:',repr(pickle.loads(client.recv(1024)))) 
socket.error: [Errno 10053] An established connection was aborted by the softwar 
e in your host machine 

Máy chủ tiếp tục chạy và nhận kết nối tốt; chỉ có khách hàng bị treo. Điều gì gây ra điều này?

EDIT: Tôi đã nhận khách hàng làm việc với đoạn mã sau:

import pickle 
import socket 
import threading 

class ConnectionThread(threading.Thread): 
    def run(self): 
     for x in range(10): 
      client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
      client.connect(('localhost', 2727)) 
      client.send(pickle.dumps(x)) 
      print('Sent:',str(x)) 
      print('Received:',repr(pickle.loads(client.recv(1024)))) 
      client.close() 

for x in range(5): 
    ConnectionThread().start() 

Tuy nhiên, tôi vẫn không hiểu những gì đang xảy ra. Đây không phải là chỉ mở và đóng socket một loạt các lần? Không nên có giới hạn thời gian cho điều đó (bạn không nên mở một ổ cắm ngay sau khi đóng nó)?

+0

Có một số lỗi rõ ràng trong các mã, có vẻ như giả định 1 gửi cuộc gọi trên một kết quả bên trong 1 cuộc gọi recv ở phía bên kia, mà có thể không đúng, TCP là một giao thức stream, nó không phải là thông điệp hoặc gói theo định hướng. Tuy nhiên, không chắc chắn cách liên quan đến thông báo lỗi. – leeeroy

+0

@leeroy Tôi rõ ràng là loại mới này, vì vậy tôi hoàn toàn mở cửa cho những lời chỉ trích ở đây. Bạn dường như được gợi ý rằng tôi không nên sử dụng TCP vì nó là dòng chứ không phải là gói theo định hướng; nhưng không thể đại diện cho một gói chỉ đơn giản là một luồng dữ liệu rất ngắn? Tôi biết nó không phải là nó * nên * được sử dụng nhưng đây chỉ là để thử nghiệm ra; rõ ràng là tôi có ý định đẩy nhiều dữ liệu hơn một số nguyên duy nhất. – Imagist

+0

@leeroy (tiếp theo) Mục tiêu của tôi là làm việc để thực hiện một cái gì đó như thế này: http://www.mcwalter.org/technology/java/httpd/tiny/index.html chỉ bằng Python. – Imagist

Trả lời

7

Khách hàng của bạn bây giờ là đúng - bạn muốn mở ổ cắm gửi dữ liệu, nhận được câu trả lời và sau đó đóng socket.

Lỗi ban đầu lỗi là do máy chủ đóng socket sau khi nó gửi phản hồi đầu tiên khiến khách hàng nhận được thông báo kết nối khi nó cố gắng gửi thư thứ hai trên cùng một kết nối.

Tuy nhiên, tôi vẫn không hiểu những gì đang diễn ra. Không phải là điều này chỉ mở và đóng ổ cắm một bó lần?

Có. Điều này là chấp nhận được, nếu không phải là cách hiệu quả cao nhất để làm việc.

Nên không có được thời gian hạn chế đó (bạn không nên thể mở một ổ cắm nên ngay sau khi đóng nó)?

Bạn có thể mở ổ cắm máy khách nhanh như bạn muốn mỗi khi mở ổ cắm, bạn sẽ nhận được số cổng cục bộ mới, nghĩa là các kết nối sẽ không can thiệp. Trong mã máy chủ ở trên, nó sẽ bắt đầu một chuỗi mới cho mỗi kết nối đến.

Có 4 phần cho mọi kết nối IP (source_address, source_port, destination_address, destination_port) và quad này (như nó được biết) phải thay đổi cho bao giờ kết nối. Tất cả mọi thứ ngoại trừ source_port được cố định cho một ổ cắm máy khách để đó là những gì hệ điều hành thay đổi cho bạn.

ổ cắm máy chủ khai mạc là phiền hà hơn - nếu bạn muốn mở một ổ cắm máy chủ mới một cách nhanh chóng, bạn

server.bind(('', 2727)) 

Trên thì bạn cần phải đọc lên trên SO_REUSEADDR.

-15

Đó không phải là cách bạn học lập trình mạng python. Không ai làm bất cứ điều gì nghiêm trọng sẽ bao giờ chương trình như thế, vì vậy bạn đang học cách KHÔNG làm lập trình mạng python.

  1. Ổ cắm quá thấp và python là ngôn ngữ cấp cao. Sau đó, để làm lập trình mạng python, theo cách pythonic, bạn muốn tránh giao dịch với các socket hoàn toàn, và để nó cho một số module hoặc gói. Nếu bạn muốn tìm hiểu cách hoạt động của các công cụ cấp thấp, việc nghiên cứu các triển khai hiện có là cần thiết, vì vậy việc học một số high-level library là điều cần thiết.
  2. pickle không thực sự là cách bạn tuần tự hóa các đối tượng python để gửi chúng xuống dây. Chúng là một vấn đề bảo mật tiềm năng mà bạn đang cố gắng tạo ra, vì bạn có thể tạo ra một gói dữ liệu giả mạo để thực hiện mã lệnh khi bị giải mã. Tốt hơn là sử dụng phương pháp tuần tự an toàn - vì có nhiều phương thức sẵn có, như gọi số str() trên đối tượng số nguyên và int() ở phía bên kia.
  3. Để phục vụ/nghe nhiều khách hàng cùng một lúc, không sử dụng luồng. Đặc biệt, không bao giờ sử dụng phương pháp "một luồng cho mỗi kết nối" mà bạn dường như đang sử dụng. Nó không quy mô như bạn nghĩ. Vì việc sử dụng các luồng cũng dễ bị lỗi và khó gỡ lỗi, và hoàn toàn không có lợi thế cho kết quả cuối cùng của bạn, bringing problems instead, chỉ cần cố gắng tránh chúng hoàn toàn.Sockets (và do đó higher level libraries như đã thảo luận trong mục 1) có thể làm việc trong một chế độ non-blocking, nơi bạn có thể hoạt động trong khi các mã khác đang chạy, và có thể được thực hiện mà không sử dụng đề.

Tôi vừa mới thấy nhận xét của bạn về vấn đề:

@leeroy (tiếp theo) Mục tiêu của tôi là làm việc lên đến thực hiện một cái gì đó như thế này: http://www.mcwalter.org/technology/java/httpd/tiny/index.html chỉ bằng Python

Vâng, đây là triển khai hoạt động:

from SimpleHTTPServer import SimpleHTTPRequestHandler as RH 
from SocketServer import TCPServer 

print "serving current directory at port 8000" 
TCPServer(("", 8000), RH).serve_forever() 
+18

-1. Tôi không nghĩ rằng có bất kỳ nhu cầu cho một câu trả lời thù địch như vậy, đặc biệt là đối với một người mới để Python. – dcrosta

+0

@dcrosta: xin lỗi nhưng đâu là câu trả lời của tôi? – nosklo

+1

@dcrosta - Đối với hồ sơ, tôi không phải là người mới với Python, chỉ mới với mạng. Tôi đã trả lời câu hỏi Python trong một thời gian trên SO. :) – Imagist

Các vấn đề liên quan