तुरंत सर्वर सॉकेट से क्लाइंट वियोग का पता लगाएं


82

मैं कैसे पता लगा सकता हूं कि एक क्लाइंट ने मेरे सर्वर से डिस्कनेक्ट कर दिया है?

मेरी AcceptCallBackविधि में निम्नलिखित कोड है

static Socket handler = null;
public static void AcceptCallback(IAsyncResult ar)
{
  //Accept incoming connection
  Socket listener = (Socket)ar.AsyncState;
  handler = listener.EndAccept(ar);
}

मुझे जल्द से जल्द खोजने का तरीका खोजने की जरूरत है कि ग्राहक को किसने डिस्कनेक्ट किया है handler सॉकेट ।

मैंने कोशिश की:

  1. handler.Available;
  2. handler.Send(new byte[1], 0, SocketFlags.None);
  3. handler.Receive(new byte[1], 0, SocketFlags.None);

जब आप सर्वर से कनेक्ट कर रहे हैं तो उपरोक्त दृष्टिकोण काम करते हैं और यह पता लगाना चाहते हैं कि सर्वर कब डिस्कनेक्ट होता है लेकिन जब आप सर्वर होते हैं तो वे काम नहीं करते हैं और क्लाइंट डिस्कनेक्ट का पता लगाना चाहते हैं।

किसी भी तरह की सहायता को आभार समझेंगे।


10
@Samuel: THE टीसीपी और कनेक्शन टैग कर रहे हैं बहुत ज्यादा में है कि टीसीपी संबंध बनाए रखता है इस पोस्ट के लिए प्रासंगिक (जबकि इस तरह के यूडीपी के रूप में अन्य नेटवर्क प्रोटोकॉल नहीं करते हैं)।
नोल्डोरिन

3
मेरे ब्लॉग से दिल की धड़कन के समाधान के बारे में अधिक जानकारी: हाफ-ओपन (ड्राप्ड) कनेक्शंस का पता लगाना
स्टीफन क्लीरी

यहाँ वर्णित समाधान मेरे लिए अच्छी तरह से काम करता है: stackoverflow.com/questions/1387459/…
रॉ मर्क

जवाबों:


110

चूंकि सॉकेट डिस्कनेक्ट होने पर संकेत देने के लिए कोई ईवेंट उपलब्ध नहीं हैं, इसलिए आपको इसे एक आवृत्ति पर परागण करना होगा जो आपको स्वीकार्य है।

इस विस्तार विधि का उपयोग करके, आपके पास एक सॉकेट डिस्कनेक्ट होने का पता लगाने के लिए एक विश्वसनीय तरीका हो सकता है।

static class SocketExtensions
{
  public static bool IsConnected(this Socket socket)
  {
    try
    {
      return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
    }
    catch (SocketException) { return false; }
  }
}

1
यह काम किया। धन्यवाद। मैंने लौटने का तरीका बदल दिया! (Socket.Available == 0 && socket.Poll (1, SelectMode.SelectRead)); क्योंकि मुझे सॉकेट पर संदेह है। उपलब्ध सॉकेट से अधिक तेज है। ()

28
यह विधि तब तक काम नहीं करती है जब तक कि कनेक्शन का दूसरा छोर सॉकेट को बंद / बंद न कर दे। अनप्लग किए गए नेटवर्क / पावर केबल को टाइमआउट अवधि से पहले नहीं देखा जाएगा। तुरंत कनेक्शन की जांच करने के लिए एक दिल की धड़कन समारोह के साथ डिस्कनेक्ट के बारे में सूचित करने का एकमात्र तरीका है।
कास्पर होल्डम

7
@Smart एलेक: वास्तव में, आपको उदाहरण का उपयोग करना चाहिए क्योंकि यह ऊपर दिखाया गया है। यदि आप ऑर्डर बदलते हैं तो संभावित दौड़ की स्थिति है: यदि socket.Availableरिटर्न 0 मिलता है और आपको socket.Pollकॉल किया जाता है, तो Pollवह सही होने से पहले एक पैकेट प्राप्त करता है , सही लौटेगा और विधि वापस आ जाएगी false, हालांकि सॉकेट वास्तव में अभी भी स्वस्थ है।
Groo

4
यह समय का 99% अच्छा काम करता है, कभी-कभी यह एक गलत डिस्कनेक्ट देता है।
मैथ्यू फिनेले

5
जैसे मैथ्यू फिनले ने देखा, यह कभी-कभी झूठे डिस्कनेक्ट की सूचना देगा क्योंकि पोल विधि के परिणाम और उपलब्ध संपत्ति की जांच के बीच अभी भी एक दौड़ की स्थिति है। एक पैकेट पढ़ने के लिए लगभग तैयार हो सकता है, लेकिन अभी तक नहीं, इस प्रकार उपलब्ध होने के 0 - लेकिन बाद में एक मिलीसेकंड, पढ़ने के लिए डेटा है। एक बेहतर विकल्प एक बाइट और सॉकेटफ्लैग.पिक फ्लैग प्राप्त करने का प्रयास करना है। या दिल की धड़कन के कुछ रूप को लागू करें और कनेक्शन की स्थिति को उच्च स्तर पर रखें। या अपने भेजने / प्राप्त करने के तरीकों (और कॉलबैक, यदि एसिंक्रियन संस्करणों का उपयोग कर रहे हैं) पर त्रुटि से निपटने पर भरोसा करें।
मार्बेल

22

किसी ने टीसीपी सॉकेट की रखने की क्षमता का उल्लेख किया है। यहाँ यह अच्छी तरह से वर्णित है:

http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html

मैं इसे इस तरह से उपयोग कर रहा हूं: सॉकेट कनेक्ट होने के बाद, मैं इस फ़ंक्शन को कॉल कर रहा हूं, जो KeepAlive को सेट करता है। keepAliveTimeपहले-रखें पैकेट भेज दिया जाता है जब तक पैरामीटर टाइमआउट, मिलीसेकेंड में निर्दिष्ट करता है, बिना किसी गतिविधि के। keepAliveIntervalपैरामीटर, अंतराल निर्दिष्ट करता है, जब लगातार रखें जिंदा पैकेट अगर कोई पावती प्राप्त होता है भेजा जाता है के बीच मिलीसेकंड में।

    void SetKeepAlive(bool on, uint keepAliveTime, uint keepAliveInterval)
    {
        int size = Marshal.SizeOf(new uint());

        var inOptionValues = new byte[size * 3];

        BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0);
        BitConverter.GetBytes((uint)keepAliveTime).CopyTo(inOptionValues, size);
        BitConverter.GetBytes((uint)keepAliveInterval).CopyTo(inOptionValues, size * 2);

        socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    }

मैं अतुल्यकालिक पढ़ने का भी उपयोग कर रहा हूँ:

socket.BeginReceive(packet.dataBuffer, 0, 128,
                    SocketFlags.None, new AsyncCallback(OnDataReceived), packet);

और कॉलबैक में, यहां टाइमआउट पकड़ा गया है SocketException, जो तब रखता है जब सॉकेट को रखने के पैकेट के बाद एसीके सिग्नल नहीं मिलता है।

public void OnDataReceived(IAsyncResult asyn)
{
    try
    {
        SocketPacket theSockId = (SocketPacket)asyn.AsyncState;

        int iRx = socket.EndReceive(asyn);
    }
    catch (SocketException ex)
    {
        SocketExceptionCaught(ex);
    }
}

इस तरह, मैं टीसीपी क्लाइंट और सर्वर के बीच सुरक्षित रूप से वियोग का पता लगाने में सक्षम हूं।


6
हां, रखने योग्य + अंतराल को कम मूल्य पर सेट करें, कोई भी सर्वेक्षण / प्रेषक रखने के बाद विफल हो जाना चाहिए + 10 * अंतराल मिलीसेकंड। विस्टा के बाद से 10 रिट्रीस हार्डकोड किए गए लगते हैं? तब भी काम करता है जब आप अधिकांश अन्य उत्तरों के विपरीत केबल को अनप्लग करते हैं। यह स्वीकृत उत्तर होना चाहिए।
टस्टर-सीएक्स

15

यह बस संभव नहीं है। आपके और सर्वर के बीच कोई शारीरिक संबंध नहीं है (अत्यंत दुर्लभ मामले को छोड़कर जहां आप दो कंपू के बीच लूपबैक केबल से जुड़ रहे हैं)।

जब कनेक्शन इनायत से बंद हो जाता है, तो दूसरा पक्ष अधिसूचित किया जाता है। लेकिन यदि कनेक्शन को किसी अन्य तरीके से काट दिया जाता है (कहते हैं कि उपयोगकर्ताओं का कनेक्शन हटा दिया गया है) तो सर्वर को तब तक पता नहीं चलेगा जब तक वह बाहर नहीं हो जाता (या कनेक्शन और एक बार आउट करने के लिए लिखने की कोशिश करता है)। यह सिर्फ टीसीपी का काम करता है और आपको इसके साथ रहना होता है।

इसलिए, "तुरन्त" अवास्तविक है। सबसे अच्छा आप जो कर सकते हैं वह टाइमआउट अवधि के भीतर है, जो उस कोड पर निर्भर करता है जिस पर कोड चल रहा है।

संपादित करें: यदि आप केवल ग्रेसफुल कनेक्शन की तलाश कर रहे हैं, तो अपने क्लाइंट के लिए सर्वर पर "DISCONNECT" कमांड क्यों न भेजें?


1
धन्यवाद, लेकिन मैं वास्तव में एक सुंदर सॉफ्टवेयर वियोग का मतलब है, भौतिक वियोग नहीं। मैं

1
यदि वह केवल सुंदर डिस्कनेक्ट की तलाश में है तो उसे कुछ भी भेजने की आवश्यकता नहीं है। उसका पाठ धारा की ओर लौटेगा।
user207421

6

"यह सिर्फ टीसीपी के काम करने का तरीका है और आपको इसके साथ रहना होगा।"

हाँ, तुम सही हो। यह जीवन का एक तथ्य है जिसका मुझे एहसास हुआ है। आप इस प्रोटोकॉल (और यहां तक ​​कि अन्य) का उपयोग करने वाले पेशेवर अनुप्रयोगों में भी प्रदर्शित समान व्यवहार देखेंगे। मैंने इसे ऑनलाइन गेम में देखा है; आप दोस्त "अलविदा" कहते हैं, और जब तक सर्वर "घर साफ नहीं करता" तब तक वह 1-2 मिनट के लिए ऑनलाइन दिखाई देता है।

आप यहां सुझाए गए तरीकों का उपयोग कर सकते हैं, या "दिल की धड़कन" को लागू कर सकते हैं, जैसा कि सुझाव भी दिया गया है। मैं पूर्व को चुनता हूं। लेकिन अगर मैंने बाद का चयन किया, तो मेरे पास बस एक ही बाइट के साथ हर क्लाइंट को हर बार "पिंग" करना होगा, और देखना होगा कि क्या हमारे पास कोई टाइमआउट है या कोई प्रतिक्रिया नहीं है। तुम भी सटीक समय के साथ इसे प्राप्त करने के लिए एक पृष्ठभूमि धागे का उपयोग कर सकते हैं। हो सकता है कि अगर आप वास्तव में इसके बारे में चिंतित हैं, तो भी कुछ प्रकार के विकल्प सूची (एनम फ्लैग या कुछ) में एक संयोजन लागू किया जा सकता है। लेकिन जब तक आप अपडेट नहीं करते, सर्वर को अपडेट करने में थोड़ा विलंब होना कोई बड़ी बात नहीं है। यह इंटरनेट है, और कोई भी यह जादू होने की उम्मीद करता है! :)


3

अपने सिस्टम में दिल की धड़कन को लागू करना एक समाधान हो सकता है। यह तभी संभव है जब क्लाइंट और सर्वर दोनों आपके नियंत्रण में हों। आपके पास उस समय का ट्रैक रखने का दिनांक समय ऑब्जेक्ट हो सकता है जब पिछले बाइट्स सॉकेट से प्राप्त किए गए थे। और मान लें कि एक निश्चित अंतराल पर सॉकेट का जवाब नहीं दिया जाता है। यह केवल तभी काम करेगा जब आपके पास दिल की धड़कन / रिवाज जिंदा रहे।


2

मैंने काफी उपयोगी पाया है, उसके लिए एक और समाधान!

यदि आप नेटवर्क सॉकेट से डेटा पढ़ने के लिए अतुल्यकालिक तरीकों का उपयोग करते हैं (मेरा मतलब है, उपयोग BeginReceive- EndReceive विधियाँ), जब भी कोई कनेक्शन समाप्त होता है; इनमें से एक स्थिति दिखाई देती है: या तो एक संदेश बिना किसी डेटा के साथ भेजा जाता है (आप इसे देख सकते हैं Socket.Available- भले ही BeginReceiveट्रिगर हो, इसका मूल्य शून्य होगा) या Socket.Connectedइस कॉल में मान गलत हो जाता है ( EndReceiveतब उपयोग करने का प्रयास न करें )।

मैं अपने द्वारा उपयोग किए जा रहे फ़ंक्शन को पोस्ट कर रहा हूं, मुझे लगता है कि आप देख सकते हैं कि इससे बेहतर क्या मेरा मतलब है:


private void OnRecieve(IAsyncResult parameter) 
{
    Socket sock = (Socket)parameter.AsyncState;
    if(!sock.Connected || sock.Available == 0)
    {
        // Connection is terminated, either by force or willingly
        return;
    }

    sock.EndReceive(parameter);
    sock.BeginReceive(..., ... , ... , ..., new AsyncCallback(OnRecieve), sock);

    // To handle further commands sent by client.
    // "..." zones might change in your code.
}

2

इसने मेरे लिए काम किया, कुंजी आपको मतदान के साथ सॉकेट स्थिति का विश्लेषण करने के लिए एक अलग धागे की आवश्यकता है। सॉकेट का पता लगाने में विफल होने पर इसे उसी थ्रेड में करना।

//open or receive a server socket - TODO your code here
socket = new Socket(....);

//enable the keep alive so we can detect closure
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

//create a thread that checks every 5 seconds if the socket is still connected. TODO add your thread starting code
void MonitorSocketsForClosureWorker() {
    DateTime nextCheckTime = DateTime.Now.AddSeconds(5);

    while (!exitSystem) {
        if (nextCheckTime < DateTime.Now) {
            try {
                if (socket!=null) {
                    if(socket.Poll(5000, SelectMode.SelectRead) && socket.Available == 0) {
                        //socket not connected, close it if it's still running
                        socket.Close();
                        socket = null;    
                    } else {
                        //socket still connected
                    }    
               }
           } catch {
               socket.Close();
            } finally {
                nextCheckTime = DateTime.Now.AddSeconds(5);
            }
        }
        Thread.Sleep(1000);
    }
}

1

यहाँ उदाहरण कोड http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx दिखाता है कि यह निर्धारित करने के लिए कि सॉकेट अभी भी किसी भी डेटा को भेजे बिना जुड़ा हुआ है या नहीं।

यदि आपने सर्वर प्रोग्राम पर सॉकेट.बेगिनिविव () कॉल किया और फिर क्लाइंट ने "ग्रेसफुल" कनेक्शन बंद कर दिया, तो आपका प्राप्त कॉलबैक कहा जाएगा और एंडरसीव () 0 बाइट्स लौटाएगा। इन 0 बाइट्स का मतलब है कि ग्राहक "डिस्कनेक्ट" हो सकता है। फिर आप यह सुनिश्चित करने के लिए MSDN उदाहरण कोड में दिखाई गई तकनीक का उपयोग कर सकते हैं ताकि यह सुनिश्चित हो सके कि कनेक्शन बंद था या नहीं।


1

स्वीकार किए गए उत्तर पर mbargiel और mycelo द्वारा टिप्पणियों पर विस्तार करना, क्लाइंट को बंद कर दिया गया है या नहीं यह बताने के लिए निम्नलिखित का उपयोग सर्वर अंत पर एक गैर-अवरुद्ध सॉकेट के साथ किया जा सकता है।

यह दृष्टिकोण दौड़ की स्थिति को पीड़ित नहीं करता है जो स्वीकृत उत्तर में पोल ​​पद्धति को प्रभावित करता है।

// Determines whether the remote end has called Shutdown
public bool HasRemoteEndShutDown
{
    get
    {
        try
        {
            int bytesRead = socket.Receive(new byte[1], SocketFlags.Peek);

            if (bytesRead == 0)
                return true;
        }
        catch
        {
            // For a non-blocking socket, a SocketException with 
            // code 10035 (WSAEWOULDBLOCK) indicates no data available.
        }

        return false;
    }
}

दृष्टिकोण इस तथ्य पर आधारित है कि Socket.Receiveदूरस्थ शून्य द्वारा सॉकेट को बंद करने के तुरंत बाद विधि शून्य हो जाती है और हमने इसके सभी डेटा पढ़ लिए हैं। सॉकेट से। प्रभावी दस्तावेज :

यदि दूरस्थ होस्ट शटडाउन विधि के साथ सॉकेट कनेक्शन को बंद कर देता है, और सभी उपलब्ध डेटा प्राप्त हो गया है, तो प्राप्तकर्ता विधि तुरंत पूरी हो जाएगी और शून्य बाइट्स वापस कर देगी।

यदि आप नॉन-ब्लॉकिंग मोड में हैं, और प्रोटोकॉल स्टैक बफर में कोई डेटा उपलब्ध नहीं है, तो प्राप्त करने का तरीका तुरंत पूरा हो जाएगा और सॉकेटएक्सलाइट को फेंक देगा।

दूसरा बिंदु ट्राइ-कैच की आवश्यकता की व्याख्या करता है।

का उपयोग SocketFlags.Peekध्वज किसी भी प्राप्त डेटा को पढ़ने के लिए एक अलग प्राप्त तंत्र से अछूता छोड़ देता है।

उपरोक्त एक अवरुद्ध सॉकेट के साथ भी काम करेगा , लेकिन ध्यान रखें कि कोड रिसीव कॉल पर ब्लॉक करेगा (जब तक कि डेटा प्राप्त नहीं होता है या प्राप्त समय समाप्त हो जाता है, फिर से परिणाम होता है SocketException)।


0

क्या आप केवल चयन का उपयोग नहीं कर सकते?

कनेक्टेड सॉकेट पर चयन का उपयोग करें। यदि चयन आपके सॉकेट के साथ रेडी के रूप में रिटर्न करता है, लेकिन बाद में रिसीव करता है 0 बाइट्स, जिसका अर्थ है कि ग्राहक ने कनेक्शन काट दिया है। AFAIK, यह निर्धारित करने का सबसे तेज़ तरीका है कि ग्राहक डिस्कनेक्ट हो गया है या नहीं।

मुझे C # का पता नहीं है, इसलिए यदि मेरा समाधान C # में फिट नहीं होता है, तो इसे अनदेखा करें (C # हालांकि चयन नहीं करता है ) या यदि मुझे संदर्भ गलत समझा था।


0

SetSocketOption विधि का उपयोग करके, आप KeepAlive सेट करने में सक्षम होंगे जो आपको बताएगा कि जब भी सॉकेट डिस्कनेक्ट हो जाता है

Socket _connectedSocket = this._sSocketEscucha.EndAccept(asyn);
                _connectedSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);

http://msdn.microsoft.com/en-us/library/1011kecd(v=VS.90).aspx

आशा है कि इससे सहायता मिलेगी! रामिरो रिनाल्दी


2
यह 'आपको पता नहीं होने देगा कि जब भी एक सॉकेट काट दिया जाता है'। यह इसका पता लगाएगा , अंत में, अगर जिंदा टाइमर समाप्त हो रहा है। डिफ़ॉल्ट रूप से इसे दो घंटे पर सेट किया जाता है। आप इसका वर्णन 'जब भी' कर सकते हैं। आप इसे 'लेट [टिंग] के रूप में भी नहीं बता सकते हैं जो आप जानते हैं: आपको अभी भी पता लगाने में विफलता के लिए एक रीड या राइट करना है। -1
user207421

0

मैं एक ही समस्या थी, यह कोशिश:

void client_handler(Socket client) // set 'KeepAlive' true
{
    while (true)
    {
        try
        {
            if (client.Connected)
            {

            }
            else
            { // client disconnected
                break;
            }
        }
        catch (Exception)
        {
            client.Poll(4000, SelectMode.SelectRead);// try to get state
        }
    }
}

0

यह वीबी में है, लेकिन यह मेरे लिए अच्छा काम करता है। यह पिछली पोस्ट की तरह 0 बाइट रिटर्न के लिए दिखता है।

Private Sub RecData(ByVal AR As IAsyncResult)
    Dim Socket As Socket = AR.AsyncState

    If Socket.Connected = False And Socket.Available = False Then
        Debug.Print("Detected Disconnected Socket - " + Socket.RemoteEndPoint.ToString)
        Exit Sub
    End If
    Dim BytesRead As Int32 = Socket.EndReceive(AR)
    If BytesRead = 0 Then
        Debug.Print("Detected Disconnected Socket - Bytes Read = 0 - " + Socket.RemoteEndPoint.ToString)
        UpdateText("Client " + Socket.RemoteEndPoint.ToString + " has disconnected from Server.")
        Socket.Close()
        Exit Sub
    End If
    Dim msg As String = System.Text.ASCIIEncoding.ASCII.GetString(ByteData)
    Erase ByteData
    ReDim ByteData(1024)
    ClientSocket.BeginReceive(ByteData, 0, ByteData.Length, SocketFlags.None, New AsyncCallback(AddressOf RecData), ClientSocket)
    UpdateText(msg)
End Sub

-1

यदि आप मतदान करना चाहते थे तो आप सॉकेट की संपत्ति को भी देख सकते हैं।


2
यह ऊपर दिए गए किसी भी परिदृश्य (सर्वर / क्लाइंट) के लिए काम नहीं करेगा

1
इसके अलावा, संपत्ति कहा जाता है।
क्वर्टी

यह मनमाने ढंग से डिस्कनेक्ट का पता नहीं लगाता है। आपको अभी भी कुछ I / O करना है।
user207421

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.