it-swarm.asia

كيفية معرفة ما إذا كان اتصال ميت في الثعبان

أريد أن يكون تطبيق python الخاص بي قادراً على معرفة متى تم إسقاط المقبس الموجود على الجانب الآخر. هل هناك طريقة لهذا؟

54
directedition

ذلك يعتمد على ما تقصد ب "انخفض". بالنسبة إلى المقابس TCP ، إذا أغلقت الطرف الآخر الاتصال إما من خلال الإغلاق () أو إنهاء العملية ، فستكتشف ذلك بقراءة نهاية الملف أو الحصول على خطأ في القراءة ، وعادةً ما يتم تعيين الخطأ إلى أي شيء "يتم إعادة تعيين الاتصال بواسطة النظير" بواسطة نظام التشغيل الخاص بك. بالنسبة للثعبان ، ستقرأ سلسلة ذات طول صفري ، أو مأخذ توصيل. سيتم طرح الإرهاب عند محاولة القراءة أو الكتابة من المقبس.

34
Andy V

اجابة قصيرة:

استخدام recv () غير المحظورة ، أو recv المحظورة ()/select () مع مهلة قصيرة جدًا.

اجابة طويلة:

تتمثل طريقة التعامل مع اتصالات المقابس في القراءة أو الكتابة حسب حاجتك ، وتكون مستعدًا للتعامل مع أخطاء الاتصال.

يميز TCP بين 3 أشكال من "إسقاط" اتصال: المهلة ، إعادة تعيين ، إغلاق.

من هذه ، لا يمكن اكتشاف المهلة بالفعل ، TCP قد تخبرك فقط أن الوقت لم ينته بعد. ولكن حتى لو أخبرتك بذلك ، فقد يستمر الوقت بعد ذلك مباشرة.

تذكر أيضًا أن استخدام إيقاف التشغيل () إما أنت أو نظيرك (الطرف الآخر من الاتصال) قد يغلق فقط تيار البايت الوارد ، ويظل تيار البايت الصادر قيد التشغيل ، أو يغلق الدفق المنتهية ولايته ويظل قيد التشغيل.

بالمعنى الدقيق للكلمة ، تريد التحقق من إغلاق دفق القراءة ، أو إذا كان دفق الكتابة مغلقًا ، أو إذا كان كلاهما مغلقًا.

حتى في حالة "انقطاع" الاتصال ، يجب أن تظل قادرًا على قراءة أي بيانات لا تزال في المخزن المؤقت للشبكة. فقط بعد أن يكون المخزن المؤقت فارغاً ستتلقى قطع اتصال من recv ().

يشبه التحقق مما إذا تم إلغاء الاتصال السؤال "ما الذي سأحصل عليه بعد قراءة جميع البيانات المخزنة مؤقتًا في الوقت الحالي؟" لمعرفة ذلك ، عليك فقط قراءة جميع البيانات المخزنة مؤقتًا في الوقت الحالي.

أستطيع أن أرى كيف أن "قراءة جميع البيانات المخزنة مؤقتًا" ، للوصول إلى نهايتها ، قد تكون مشكلة لبعض الأشخاص ، الذين ما زالوا يفكرون في recv () كدالة للحظر. مع حظر recv () ، سيتم حظر "التحقق" لقراءة عندما يكون المخزن المؤقت فارغًا بالفعل ، وهو ما سيهزم غرض "التحقق".

في رأيي ، فإن أي وظيفة تم توثيقها لحظر العملية بالكامل إلى أجل غير مسمى هي عيب في التصميم ، لكنني أعتقد أنها لا تزال موجودة لأسباب تاريخية ، من عند استخدام مأخذ توصيل تمامًا مثل واصف الملف العادي كان فكرة رائعة.

ما يمكنك القيام به هو:

  • اضبط المقبس على الوضع غير المحظور ، لكنك تحصل على خطأ يعتمد على النظام للإشارة إلى أن المخزن المؤقت للتلقي فارغ ، أو أن المخزن المؤقت للإرسال ممتلئ
  • التمسك وضع حظر ولكن تعيين مهلة مأخذ قصيرة جدا. سيتيح لك ذلك "اختبار الاتصال" أو "التحقق" من المقبس باستخدام recv () ، إلى حد كبير ما تريد القيام به
  • استخدام select () استدعاء أو وحدة غير متزامنة مع مهلة قصيرة جدًا. لا يزال الإبلاغ عن الأخطاء خاصًا بالنظام.

بالنسبة لجزء الكتابة من المشكلة ، فإن إبقاء مخازن القراءة المؤقتة فارغة إلى حد كبير تغطيها. سوف تكتشف اتصال "تم إسقاطه" بعد محاولة قراءة غير محظورة ، ويمكنك اختيار إيقاف إرسال أي شيء بعد أن تُرجع إحدى القنوات قناة مغلقة.

أعتقد أن الطريقة الوحيدة للتأكد من أن البيانات المرسلة قد وصلت إلى الطرف الآخر (ولا تزال في المخزن المؤقت للإرسال) هي إما:

  • تلقي استجابة مناسبة على نفس المقبس للرسالة بالضبط التي أرسلتها. إنك تستخدم بروتوكول المستوى الأعلى بشكل أساسي لتوفير التأكيد.
  • إجراء ناجحة () وإغلاق () على المقبس

سيعود مقبس الثعبان Howto يقول send () 0 بايت مكتوبة إذا تم إغلاق القناة. يمكنك استخدام socket.send () بدون حظر أو مهلة ، وإذا كان إرجاع 0 فلن تتمكن من إرسال البيانات على هذا المقبس. ولكن إذا عاد بدون الصفر ، فأنت قد أرسلت شيئًا بالفعل ، حظًا سعيدًا بذلك :)

هنا أيضًا ، لم أفكر في بيانات مأخذ توصيل OOB (خارج النطاق) هنا كوسيلة للتعامل مع مشكلتك ، لكنني أعتقد أن OOB لم يكن المقصود منك.

43
Toughy

من الرابط نشر Jweede:

مأخذ استثناء. مهلة:

This exception is raised when a timeout occurs on a socket
which has had timeouts enabled via a prior call to settimeout().
The accompanying value is a string whose value is currently
always “timed out”.

فيما يلي برامج الخادم والعميل التجريبي لوحدة المقبس من مستندات python

# Echo server program
import socket

Host = ''                 # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((Host, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
    data = conn.recv(1024)
    if not data: break
    conn.send(data)
conn.close()

والعميل:

# Echo client program
import socket

Host = 'daring.cwi.nl'    # The remote Host
PORT = 50007              # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((Host, PORT))
s.send('Hello, world')
data = s.recv(1024)
s.close()
print 'Received', repr(data)

في صفحة مثال مستندات المستندات التي حصلت عليها منها ، هناك أمثلة أكثر تعقيدًا تستخدم هذه الفكرة ، ولكن فيما يلي الإجابة البسيطة:

على افتراض أنك تكتب برنامج العميل ، فما عليك سوى وضع كل التعليمات البرمجية التي تستخدم المقبس عندما يكون معرضًا لخطر السقوط ، داخل كتلة تجربة ...

try:
    s.connect((Host, PORT))
    s.send("Hello, World!")
    ...
except socket.timeout:
    # whatever you need to do when the connection is dropped
14
Jiaaro

إذا لم أكن مخطئًا ، فعادة ما يتم التعامل مع هذا من خلال مهلة .

4
Jon W

لقد قمت بترجمة نموذج التعليمات البرمجية في منشور المدونة هذا إلى Python: كيفية اكتشاف متى يغلق العميل الاتصال؟ ، ويعمل بشكل جيد بالنسبة لي:

from ctypes import (
    CDLL, c_int, POINTER, Structure, c_void_p, c_size_t,
    c_short, c_ssize_t, c_char, ARRAY
)


__all__ = 'is_remote_alive',


class pollfd(Structure):
    _fields_ = (
        ('fd', c_int),
        ('events', c_short),
        ('revents', c_short),
    )


MSG_DONTWAIT = 0x40
MSG_PEEK = 0x02

EPOLLIN = 0x001
EPOLLPRI = 0x002
EPOLLRDNORM = 0x040

libc = CDLL(None)

recv = libc.recv
recv.restype = c_ssize_t
recv.argtypes = c_int, c_void_p, c_size_t, c_int

poll = libc.poll
poll.restype = c_int
poll.argtypes = POINTER(pollfd), c_int, c_int


class IsRemoteAlive:  # not needed, only for debugging
    def __init__(self, alive, msg):
        self.alive = alive
        self.msg = msg

    def __str__(self):
        return self.msg

    def __repr__(self):
        return 'IsRemoteClosed(%r,%r)' % (self.alive, self.msg)

    def __bool__(self):
        return self.alive


def is_remote_alive(fd):
    fileno = getattr(fd, 'fileno', None)
    if fileno is not None:
        if hasattr(fileno, '__call__'):
            fd = fileno()
        else:
            fd = fileno

    p = pollfd(fd=fd, events=EPOLLIN|EPOLLPRI|EPOLLRDNORM, revents=0)
    result = poll(p, 1, 0)
    if not result:
        return IsRemoteAlive(True, 'empty')

    buf = ARRAY(c_char, 1)()
    result = recv(fd, buf, len(buf), MSG_DONTWAIT|MSG_PEEK)
    if result > 0:
        return IsRemoteAlive(True, 'readable')
    Elif result == 0:
        return IsRemoteAlive(False, 'closed')
    else:
        return IsRemoteAlive(False, 'errored')
4
kay