2008-10-21 11 views
26

मैं एक ऐसा एप्लिकेशन बनाना चाहता हूं जो आंतरिक रूप से वेब पेजों परोसता है और उसी मशीन पर कई उदाहरणों में चलाया जा सकता है। ऐसा करने के लिए, मैं एक HttpListener कि एक बंदरगाह है कि है पर सुनता है बनाना चाहते हैं:मैं सी # में एक यादृच्छिक बंदरगाह पर एक HttpListener कक्षा कैसे बना सकता हूँ?

  1. यादृच्छिक रूप से चुने
  2. वर्तमान में अप्रयुक्त

अनिवार्य रूप से, की तरह कुछ है कि मैं क्या चाहते हैं:

mListener = new HttpListener(); 
mListener.Prefixes.Add("http://*:0/"); 
mListener.Start(); 
selectedPort = mListener.Port; 

मैं इसे कैसे पूरा कर सकता हूं?

उत्तर

15

कैसे कुछ इस तरह के बारे में:

static List<int> usedPorts = new List<int>(); 
    static Random r = new Random(); 

    public HttpListener CreateNewListener() 
    { 
     HttpListener mListener; 
     int newPort = -1; 
     while (true) 
     { 
      mListener = new HttpListener(); 
      newPort = r.Next(49152, 65535); // IANA suggests the range 49152 to 65535 for dynamic or private ports. 
      if (usedPorts.Contains(newPort)) 
      { 
       continue; 
      } 
      mListener.Prefixes.Add(string.Format("http://*:{0}/", newPort)); 
      try 
      { 
       mListener.Start(); 
      } 
      catch 
      { 
       continue; 
      } 
      usedPorts.Add(newPort); 
      break; 
     } 

     return mListener; 
    } 

मुझे यकीन है कि आप बंदरगाहों कि उस मशीन पर उपयोग में हैं के सभी कैसे मिलेगा, लेकिन यदि आप पर सुनने के लिए कोशिश आप एक अपवाद मिलना चाहिए नहीं कर रहा हूँ एक बंदरगाह जिसका पहले से उपयोग किया जा रहा है, इस मामले में विधि बस एक और बंदरगाह उठाएगी।

+2

आप इसके बजाय 'मिनपोर्ट'/'मैक्सपोर्ट 'स्थिरांक का उपयोग करने पर विचार करना चाहेंगे, [एमएसडीएन लिंक] (http://msdn.microsoft.com/en-us/library/vstudio/system.net.ipendpoint_fields (v = बनाम .110) .aspx) –

+0

@ जोसेफलेनॉक्स, 'मिनपोर्ट 'निरंतर शून्य है। उत्तर में दिया गया न्यूनतम मान अधिक उपयुक्त है। –

+0

@Snooganz, पकड़ ब्लॉक शायद 'mListener' का निपटान करना चाहिए। साथ ही, 'प्रयुक्त पोर्ट्स' रैखिक सदस्यता परीक्षण के लिए '' सेट हो सकता है। व्यक्तिगत रूप से मैं विधि के भीतर 'प्रयुक्त पोर्ट' का दायरा लेता हूं (या इसे पूरी तरह से छुटकारा पाता हूं) जैसे कि आपने इसे बहुत लंबे समय तक चलने वाली प्रणाली में उपयोग किया है, तो आप अंततः बंदरगाहों से बाहर निकल सकते हैं, जिससे 'लूप' हमेशा के लिए चलते हैं, भले ही बंदरगाह समय के साथ उपलब्ध हो जाते हैं। –

37

यदि आप बंदरगाह 0.

public static int GetRandomUnusedPort() 
{ 
    var listener = new TcpListener(IPAddress.Any, 0); 
    listener.Start(); 
    var port = ((IPEndPoint)listener.LocalEndpoint).Port; 
    listener.Stop(); 
    return port; 
} 
+0

क्या बंदरगाह अवरुद्ध है, अगर बंदरगाह अवरुद्ध है? –

+2

नहीं, इसके बारे में जानने का कोई तरीका नहीं है। –

+5

यह एचटीपी लिस्टर वर्ग के बारे में सवाल का जवाब नहीं देता है। – jnm2

0

मैं नहीं मानता कि यह संभव है करने के लिए बाध्य TcpListener एक यादृच्छिक संयुक्त राष्ट्र से इस्तेमाल बंदरगाह पर सुनने के लिए मिल जाएगा। UriBuilder.Port के लिए प्रलेखन कहता है, "यदि पोर्ट को यूआरआई के हिस्से के रूप में निर्दिष्ट नहीं किया गया है, तो ... प्रोटोकॉल योजना के लिए डिफ़ॉल्ट पोर्ट मान होस्ट से कनेक्ट करने के लिए उपयोग किया जाएगा।"

दुर्भाग्य https://msdn.microsoft.com/en-us/library/system.uribuilder.port(v=vs.110).aspx

0

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

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

आप एक HttpListener (और इसलिए TCP कनेक्शन) आप IPGlobalProperties वस्तु की GetActiveTcpListeners पद्धति का उपयोग करके सक्रिय टीसीपी श्रोताओं की एक सूची प्राप्त और उनके Port संपत्ति निरीक्षण कर सकते हैं का उपयोग कर रहे के बाद से।

संभव समाधान कुछ ऐसा दिखाई देगा:

private static bool TryGetUnusedPort(int startingPort, ref int port) 
{ 
    var listeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners(); 

    for (var i = startingPort; i <= 65535; i++) 
    { 
     if (listeners.Any(x => x.Port == i)) continue; 
     port = i; 
     return true; 
    } 

    return false; 
} 

इस कोड startingPort पोर्ट नंबर से शुरुआत पहले अप्रयुक्त बंदरगाह खोजने के लिए और true वापस आ जाएगी। यदि सभी बंदरगाह पहले से ही कब्जे में हैं (जो बहुत ही असंभव है) विधि false लौटाती है।

यह भी एक दौड़ की स्थिति की संभावना को ध्यान में रखें जो तब हो सकता है जब कुछ अन्य प्रक्रिया आपके सामने पाई जाती है।

+0

दिलचस्प। उस विधि ने मेरी मशीन (पहली कॉल के लिए) पर लगभग 130ms लिया जो कुछ परिदृश्यों में अस्वीकार्य हो सकता है। यहां अभी भी दौड़ की स्थिति है, इसलिए कॉलिंग कोड को इसके खिलाफ बचाव करने की आवश्यकता है। इसके अलावा, 'रेफरी' का उपयोग क्यों करें और बाहर नहीं? –

1

मैं Grapevine को आजमाने की सलाह दूंगा। यह आपको अपने एप्लिकेशन में एक आरईएसटी/HTTP सर्वर एम्बेड करने की अनुमति देता है। इसमें RestCluster कक्षा शामिल है जो आपको अपने सभी RestServer उदाहरणों को एक ही स्थान पर प्रबंधित करने की अनुमति देगी।

using (var server = new RestServer()) 
{ 
    // Grab the next open port (starts at 1) 
    server.Port = PortFinder.FindNextLocalOpenPort(); 

    // Grab the next open port after 2000 (inclusive) 
    server.Port = PortFinder.FindNextLocalOpenPort(2000); 

    // Grab the next open port between 2000 and 5000 (inclusive) 
    server.Port = PortFinder.FindNextLocalOpenPort(200, 5000); 
    ... 
} 

आरंभ करें मार्गदर्शिका: https://sukona.github.io/Grapevine/en/getting-started.html

0

यहाँ एक जवाब, Snooganz's answer से ली गई है

सेट प्रत्येक उदाहरण इस तरह एक यादृच्छिक, खुला पोर्ट नंबर का उपयोग करें। यह उपलब्धता के परीक्षण के बीच दौड़ स्थिति, और बाद में बाध्यकारी से बचाता है।

public static bool TryBindListenerOnFreePort(out HttpListener httpListener, out int port) 
{ 
    // IANA suggested range for dynamic or private ports 
    const int MinPort = 49215; 
    const int MaxPort = 65535; 

    for (port = MinPort; port < MaxPort; port++) 
    { 
     httpListener = new HttpListener(); 
     httpListener.Prefixes.Add($"http://localhost:{port}/"); 
     try 
     { 
      httpListener.Start(); 
      return true; 
     } 
     catch 
     { 
      // nothing to do here -- the listener disposes itself when Start throws 
     } 
    } 

    port = 0; 
    httpListener = null; 
    return false; 
} 

मेरी मशीन पर यह विधि औसतन 15ms लेती है, जो मेरे उपयोग के मामले के लिए स्वीकार्य है। मनाइए कि यह किसी और के लिए सहायक हो।

संबंधित मुद्दे