2012-11-20 38 views
5

नीचे एक सी # कार्यक्रम समस्या का प्रदर्शन है।सॉकेट क्यों है। टाइमआउट को अनंत पर सेट होने पर आधा बंद कनेक्शन पर समय-समय पर रिसीव करें?

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

विंडोज़ पर, क्लाइंट का रिसीव कॉल हमेशा 2 मिनट के बाद विफल रहता है "एक कनेक्शन प्रयास विफल हुआ क्योंकि कनेक्टेड पार्टी ने समय के बाद ठीक से जवाब नहीं दिया था, या स्थापित कनेक्शन विफल हुआ क्योंकि कनेक्टेड होस्ट जवाब देने में विफल रहा है" यहां तक ​​कि हालांकि टाइमआउट अनंत पर सेट है।

यदि मैं लिनक्स में मोनो के साथ प्रोग्राम चलाता हूं, तो टाइमआउट नहीं होता है, भले ही मैं "लंबा ऑपरेशन" 10 मिनट तक सेट करता हूं, लेकिन यह विंडोज में होता है चाहे मैं इसे मोनो या .NET के साथ चलाता हूं। यदि मैं टाइमआउट को 1 सेकंड पर सेट करता हूं, तो यह 1 सेकंड के बाद समाप्त हो जाता है। दूसरे शब्दों में, यह निर्धारित समय या 2 मिनट में जो भी हो, जो भी कम हो।

एक समान नमूना प्रोग्राम जिसमें सर्वर क्लाइंट को संदेश भेजता है, क्लाइंट से सर्वर तक कोई संदेश नहीं है और कोई आधा नज़दीक नहीं है, बिना किसी टाइमआउट के अपेक्षित कार्य करता है।

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

जो मैं समझता हूं उससे, केवल उसके आधा बंद भेजने वाला कनेक्शन अनिश्चित काल तक डेटा प्राप्त करना जारी रखने में सक्षम होना चाहिए। ऐसा लगता है कि विंडोज के इस तरह के मौलिक हिस्से में एक बग होगा। क्या मुझसे कुछ गलत हो रही है?

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Net.Sockets; 
using System.Net; 
using System.Threading.Tasks; 
using System.Diagnostics; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Start server thread 
      Thread serverThread = new Thread(ServerStart); 
      serverThread.IsBackground = true; 
      serverThread.Start(); 

      // Give the server some time to start listening 
      Thread.Sleep(2000); 

      ClientStart(); 
     } 

     static int PortNumber = 8181; 

     static void ServerStart() 
     { 
      TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, PortNumber)); 
      listener.Start(); 
      while (true) 
      { 
       TcpClient client = listener.AcceptTcpClient(); 
       Task connectionHandlerTask = new Task(ConnectionEntryPoint, client); 
       connectionHandlerTask.Start(); 
      } 
      listener.Stop(); 
     } 

     static void ConnectionEntryPoint(object clientObj) 
     { 
      using (TcpClient client = (TcpClient)clientObj) 
      using (NetworkStream stream = client.GetStream()) 
      { 
       // Read from client until client closes its send half. 
       byte[] requestBytes = new byte[65536]; 
       int bufferPos = 0; 
       int lastReadSize = -1; 
       while (lastReadSize != 0) 
       { 
        lastReadSize = stream.Read(requestBytes, bufferPos, 65536 - bufferPos); 
        bufferPos += lastReadSize; 
       } 
       client.Client.Shutdown(SocketShutdown.Receive); 
       string message = Encoding.UTF8.GetString(requestBytes, 0, bufferPos); 

       // Sleep for 2 minutes, 30 seconds to simulate a long-running calculation, then echo the client's message back 
       byte[] responseBytes = Encoding.UTF8.GetBytes(message); 
       Console.WriteLine("Waiting 2 minutes 30 seconds."); 
       Thread.Sleep(150000); 

       try 
       { 
        stream.Write(responseBytes, 0, responseBytes.Length); 
       } 
       catch (SocketException ex) 
       { 
        Console.WriteLine("Socket exception in server: {0}", ex.Message); 
       } 
      } 
     } 

     static void ClientStart() 
     { 
      using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) 
      { 
       // Set receive timeout to infinite. 
       socket.ReceiveTimeout = -1; 

       // Connect to server 
       socket.Connect(IPAddress.Loopback, PortNumber); 

       // Send a message to the server, then close the send half of the client's connection 
       // to let the server know it has the entire message. 
       string requestMessage = "Hello"; 
       byte[] requestBytes = Encoding.UTF8.GetBytes(requestMessage); 
       socket.Send(requestBytes); 
       socket.Shutdown(SocketShutdown.Send); 

       // Read the server's response. The response is done when the server closes the connection. 
       byte[] responseBytes = new byte[65536]; 
       int bufferPos = 0; 
       int lastReadSize = -1; 

       Stopwatch timer = Stopwatch.StartNew(); 
       try 
       { 
        while (lastReadSize != 0) 
        { 
         lastReadSize = socket.Receive(responseBytes, bufferPos, 65536 - bufferPos, SocketFlags.None); 
         bufferPos += lastReadSize; 
        } 

        string responseMessage = Encoding.UTF8.GetString(responseBytes, 0, bufferPos); 
        Console.WriteLine(responseMessage); 
       } 
       catch (SocketException ex) 
       { 
        // Timeout always occurs after 2 minutes. Why? 
        timer.Stop(); 
        Console.WriteLine("Socket exception in client after {0}: {1}", timer.Elapsed, ex.Message); 
       } 
      } 
     } 
    } 
} 

निम्नलिखित कार्यक्रम नहीं बल्कि संदेश के अंत का संकेत करने के लिए socket.Shutdown (SocketShutdown.Send) का उपयोग करने से एक 4-बाइट संदेश लंबाई के साथ संदेश उपसर्ग। टाइमआउट इस कार्यक्रम में नहीं होता है।

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Net.Sockets; 
using System.Net; 
using System.Threading.Tasks; 
using System.Diagnostics; 
using System.Threading; 

namespace WithoutShutdown 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Start server thread 
      Thread serverThread = new Thread(ServerStart); 
      serverThread.IsBackground = true; 
      serverThread.Start(); 

      // Give the server some time to start listening 
      Thread.Sleep(2000); 

      ClientStart(); 
     } 

     static int PortNumber = 8181; 

     static void ServerStart() 
     { 
      TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, PortNumber)); 
      listener.Start(); 
      while (true) 
      { 
       TcpClient client = listener.AcceptTcpClient(); 
       Task connectionHandlerTask = new Task(ConnectionEntryPoint, client); 
       connectionHandlerTask.Start(); 
      } 
      listener.Stop(); 
     } 

     static void SendMessage(Socket socket, byte[] message) 
     { 
      // Send a 4-byte message length followed by the message itself 
      int messageLength = message.Length; 
      byte[] messageLengthBytes = BitConverter.GetBytes(messageLength); 
      socket.Send(messageLengthBytes); 
      socket.Send(message); 
     } 

     static byte[] ReceiveMessage(Socket socket) 
     { 
      // Read 4-byte message length from the client 
      byte[] messageLengthBytes = new byte[4]; 
      int bufferPos = 0; 
      int lastReadSize = -1; 
      while (bufferPos < 4) 
      { 
       lastReadSize = socket.Receive(messageLengthBytes, bufferPos, 4 - bufferPos, SocketFlags.None); 
       bufferPos += lastReadSize; 
      } 
      int messageLength = BitConverter.ToInt32(messageLengthBytes, 0); 

      // Read the message 
      byte[] messageBytes = new byte[messageLength]; 
      bufferPos = 0; 
      lastReadSize = -1; 
      while (bufferPos < messageLength) 
      { 
       lastReadSize = socket.Receive(messageBytes, bufferPos, messageLength - bufferPos, SocketFlags.None); 
       bufferPos += lastReadSize; 
      } 

      return messageBytes; 
     } 

     static void ConnectionEntryPoint(object clientObj) 
     { 
      using (TcpClient client = (TcpClient)clientObj) 
      { 
       byte[] requestBytes = ReceiveMessage(client.Client); 
       string message = Encoding.UTF8.GetString(requestBytes); 

       // Sleep for 2 minutes, 30 seconds to simulate a long-running calculation, then echo the client's message back 
       byte[] responseBytes = Encoding.UTF8.GetBytes(message); 
       Console.WriteLine("Waiting 2 minutes 30 seconds."); 
       Thread.Sleep(150000); 

       try 
       { 
        SendMessage(client.Client, responseBytes); 
       } 
       catch (SocketException ex) 
       { 
        Console.WriteLine("Socket exception in server: {0}", ex.Message); 
       } 
      } 
     } 

     static void ClientStart() 
     { 
      using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) 
      { 
       // Set receive timeout to infinite. 
       socket.ReceiveTimeout = -1; 

       // Connect to server 
       socket.Connect(IPAddress.Loopback, PortNumber); 

       // Send a message to the server 
       string requestMessage = "Hello"; 
       byte[] requestBytes = Encoding.UTF8.GetBytes(requestMessage); 
       SendMessage(socket, requestBytes); 

       // Read the server's response. 
       Stopwatch timer = Stopwatch.StartNew(); 
       try 
       { 
        byte[] responseBytes = ReceiveMessage(socket); 
        string responseMessage = Encoding.UTF8.GetString(responseBytes); 
        Console.WriteLine(responseMessage); 
       } 
       catch (SocketException ex) 
       { 
        // Timeout does not occur in this program because it does not call socket.Shutdown(SocketShutdown.Send) 
        timer.Stop(); 
        Console.WriteLine("Socket exception in client after {0}: {1}", timer.Elapsed, ex.Message); 
       } 
      } 
     } 
    } 
} 
+0

के लिए उपहार क्या है? आपने अपने प्रश्न का उत्तर दिया है ना? – jeremy

+0

हां, बक्षीस पोस्ट करने के बाद और कोई वास्तविक जवाब नहीं मिलने के कई दिन बाद। –

उत्तर

4

यह व्यवहार डिज़ाइन द्वारा है। जब ग्राहक ने कनेक्शन पर अपना आधा बंद कर दिया है और सर्वर बंद को स्वीकार करता है, तो ग्राहक FIN_WAIT_2 स्थिति में है, सर्वर को कनेक्शन बंद करने की प्रतीक्षा कर रहा है। http://support.microsoft.com/kb/923200 बताता है कि 2 मिनट का FIN_WAIT_2 टाइमआउट है। यदि 2 मिनट की अवधि में कोई कनेक्शन प्राप्त नहीं होता है, तो कनेक्शन FIN_WAIT_2 स्थिति में होता है, तो क्लाइंट जबरन कनेक्शन (आरएसटी के साथ) बंद कर देता है।

Windows Server 2003 में डिफ़ॉल्ट रूप से, TCP कनेक्शन के बाद TCP कनेक्शन राज्य दो मिनट के लिए FIN_WAIT_2 करने के लिए सेट कर दिया गया है बंद करना होगा।

This old Apache article टाइमआउट के लिए कारण पता चलता है: दुर्भावनापूर्ण या दुर्व्यवहार अनुप्रयोगों कभी नहीं कनेक्शन के अपने अंत बंद करके अनिश्चित काल के लिए FIN_WAIT_2 में कनेक्शन के दूसरे छोर रखने के लिए और इस प्रकार ऑपरेटिंग सिस्टम संसाधनों को बाँध सकती।

Linux apparently has a timeout as well आप

$ बिल्ली/proc/sys/नेट/आईपीवी 4 के साथ मूल्य की जाँच कर सकते/

tcp_fin_timeout मुझे यकीन है कि क्यों टाइमआउट लिनक्स पर मेरे लिए होने वाली नहीं किया गया था नहीं हूँ। शायद क्योंकि यह लूपबैक कनेक्शन था और इसलिए डीओएस हमले चिंता नहीं हैं या लूपबैक कनेक्शन विभिन्न कोड का उपयोग करते हैं जो tcp_fin_timeout सेटिंग का उपयोग नहीं करते हैं?

नीचे पंक्ति: ऑपरेटिंग सिस्टम का कनेक्शन समय निकालने का एक अच्छा कारण है। शटडाउन को एप्लिकेशन-लेयर सिग्नलिंग तंत्र के रूप में उपयोग करने से बचें और इसके बजाय वास्तविक एप्लिकेशन-लेयर विधि का उपयोग करें।

0

Socket.Receive दो मिनट का ReceiveTimeout पर एक ऊपरी सीमा है लगता है। यह कुछ ऐसा है जो माना जाता है कि रजिस्ट्री में निर्दिष्ट किया गया है, हालांकि मुझे इसकी सच्चाई या संशोधित करने के लिए कुंजी का ठोस प्रमाण नहीं मिल सका। यह लिनक्स बनाम विंडोज पर विभिन्न व्यवहार की व्याख्या कर सकता है।

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

string boundary = string.Format("--{0}--", Guid.NewGuid()); 
byte[] boundaryBytes = Encoding.ASCII.GetBytes(boundary); 

//Every 15 seconds write a byte to the stream. 
for (int i = 0; i < 10; i++) 
{ 
    stream.WriteByte(0); 
    Thread.Sleep(15000); 
} 

//Indicate where the end of the heartbeat bytes is. 
stream.Write(boundaryBytes, 0, boundaryBytes.Length); 

//Same code as before. 
try 
{ 
    stream.Write(responseBytes, 0, responseBytes.Length); 
} 
catch (SocketException ex) 
{ 
    Console.WriteLine("Socket exception in server: {0}", ex.Message); 
} 

क्या मैं यहाँ किया है एक लंबे चल रहा कार्य (यह कुल में 2.5 मिनट के लिए सोने जाएगा) अनुकरण है, लेकिन प्रत्येक 15 सेकंड में यह धारा के लिए एक एकल बाइट लिखते समय समाप्ति को रोकने के लिए।

ऐसा करने में समस्या यह है कि आप प्रतिक्रिया की शुरुआत में अवांछित कचरे के गुच्छा के साथ हवादार हो जाते हैं। यह वह जगह है जहां boundaryBytes आते हैं: इनके साथ आप अवांछित बिट्स को वास्तविक परिणाम से स्पष्ट रूप से अलग कर सकते हैं।महत्वपूर्ण बात यह है कि क्लाइंट होना चाहिए कि सीमा क्या आगे है।

संपादित करें:

मैं नीचे कि दूर करने socket.Shutdown(SocketShutdown.Send) अपनी टिप्पणी से देख चाल करने के लिए लग रहा था। मैंने इस बारे में सोचा था, लेकिन वास्तव में इसकी जांच नहीं की थी।

जो मुझे समझ में नहीं आता है, इस विधि को कॉल करने से इसका असर क्यों होता है। कुछ अपघटन करना, Shutdown विधि मूल रूप से पिनवोक के माध्यम से अंतर्निहित WinSock लाइब्रेरी (ws2_32.dll) में shutdown विधि को कॉल करती है, कुछ त्रुटि हैंडलिंग करता है, फिर सॉकेट को डिस्कनेक्ट करने के लिए सेट करता है। किसी अन्य जानकारी की अनुपस्थिति में, यह सुझाव देता है कि WinSock कॉल में 2 मिनट की समस्या बनाई गई है।

मैंने इवेंट व्यूअर में enabling WinSock logging द्वारा इसका निदान करने का प्रयास किया, लेकिन ऐसा कुछ भी स्पष्ट नहीं था जो यह इंगित करता था कि यह क्यों हो रहा था।

Winsock recv not working after shutdown

Multipe Send()'s and Recv()'s using Winsock2

Why HTTP server close the connection when client close only the send half of the connection?

Why does .Net Socket.Disconnect take two minutes?

आम विषय है कि socket.Shutdown(SocketShutdown.Send) हो रहा है:

WinSock स्तर पर कुछ और अधिक शोध नीचे कर इन सवालों कर दिया इतना अच्छा विचार नहीं है यदि आप प्राप्त करने के लिए बाद में सॉकेट का उपयोग करना चाहते हैं। तथ्य यह है कि यह विधि socket.Connected संपत्ति को गलत पर सेट कर सकती है।

उपरोक्त सूची में अंतिम लिंक 2 मिनट की स्थिति के साथ चिह्न के करीब लग रहा था, हालांकि ओपी रजिस्ट्री सेटिंग्स को संदर्भित करता है लेकिन यह नहीं कहता कि वे क्या हैं।

+0

अनपेक्षित टाइमआउट केवल सॉकेट का उपयोग करते समय होता है। संदेश के अंत को सिग्नल करने के लिए शटडाउन (सॉकेटशूटडाउन। सैंड)। मैंने एक उदाहरण जोड़ा है जो 4-बाइट संदेश लंबाई वाले संदेशों को उपसर्ग करता है और सॉकेट का उपयोग नहीं करता है। शटडाउन (सॉकेटशूटडाउन। सैंड)। यह उदाहरण ** ** ** 2 मिनट के बाद समय नहीं है। –