2012-04-24 12 views
11

ओएस लिनक्स है। मेरे पास एक सर्वर प्रक्रिया है जो इसके पोर्ट रीयलटाइम को बदल सकती है। हालांकि अगर मैं बाध्यकारी से पहले एक बंदरगाह मुक्त हूं तो मैं पहले से जानना चाहता हूं।सी में मुझे कैसे मिल सकता है कि एक बंदरगाह उपयोग करने के लिए स्वतंत्र है?

परिदृश्य: सर्वर स्थानीयहोस्ट को बांधता है: 5000 और स्थानीयहोस्ट पर बांधने का अनुरोध प्राप्त करता है: 6000। सर्वर को यह जांचना है कि बंदरगाह मुक्त है या नहीं। यह प्रश्न उन उत्तरों की तलाश करता है जो नियमित रूप से प्रदान करते हैं जो जांचता है कि कोई बंदरगाह मुक्त है या नहीं।

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

#include <iostream> 
#include <sys/socket.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <errno.h> 

int main(int argc, char **argv) { 

    struct sockaddr_in serv_addr; 
    if(argc < 2) 
     return 0; 
    int port = atoi(argv[1]); 

    int sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if(sockfd < 0) { 
     printf("socket error\n"); 
     return 0; 
    } else { 
     printf("Opened fd %d\n", sockfd); 
    } 

    bzero((char *) &serv_addr, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    serv_addr.sin_port = htons(port); 
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 

     if(errno == EADDRINUSE) 
     { 
      printf("the port is not available. already to other process\n"); 
     } else { 
      printf("could not bind to process (%d) %s\n", errno, strerror(errno)); 
     } 
    } 

    if (close (sockfd) < 0) { 
     printf("did not close fd: %s\n", strerror(errno)); 
     return errno; 
    } 

    return 0; 


} 

यहाँ कुछ नमूना रन (आंशिक आउटपुट)

[bash{1051}{51}]:[~/some_sources/checkbind]::./a.out 41067 
the port is not available. already to other process 
[bash{1052}{52}]:[~/some_sources/checkbind]::./a.out 22 
could not bind to process (13) Permission denied 
[bash{1053}{53}]:[~/some_sources/checkbind]::./a.out 22000 
Opened fd 3 
+10

यह के लिए बाध्य की कोशिश करें, अगर यह विफल, यह मुफ़्त नहीं है ... – Nick

+1

Nicks सुझाव शायद सबसे पोर्टेबल समाधान है, लेकिन वहाँ ओएस-विशिष्ट तरीके करने के लिए हो सकता है यह भी। आप किस ऑपरेटिंग सिस्टम का उपयोग करेंगे? –

+0

@ निक लगता है कि बंदरगाह EADDRINUSE (errno) देता है अगर पोर्ट यह मुफ़्त नहीं है। – cateof

उत्तर

12

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

+0

मैं मानता हूं कि एक दौड़ की स्थिति हो सकती है, लेकिन मेरे सिस्टम में यह परिदृश्य संभव नहीं है। इसके अलावा "अगला पोर्ट चुनें" दृष्टिकोण स्वीकार्य नहीं है। – cateof

+0

@cateof: क्या संभव नहीं है?क्या दौड़ की स्थिति संभव नहीं है, या बाध्यकारी कोशिश करना संभव नहीं है और फिर यह विफल रहता है या नहीं? –

+0

यदि आपके पास संसाधन के लिए कम से कम दो प्रक्रियाएं प्रतिस्पर्धा कर रही हैं तो दौड़ की स्थिति संभव है। मेरे सिस्टम में केवल एक प्रक्रिया बांधने की कोशिश करती है। – cateof

7

यदि आपका सर्वर बताता है कि किस पोर्ट का उपयोग करना है, तो बस bind()। गंभीरता से।

निश्चित रूप से, आप /proc/net/tcp पार्स कर सकते हैं और देख सकते हैं कि बंदरगाह उपयोग में है या नहीं। लेकिन फिर क्या? आपको अभी भी bind() पर कॉल करने की आवश्यकता है कि आप जानते हैं कि आपका पोर्ट नि: शुल्क है, और यह आपको बताएगा कि पोर्ट बंद था या नहीं, और इसलिए /proc/net/tcp के माध्यम से groveling में कोई बिंदु नहीं था और यह सब (धीमी!) स्ट्रिंग उत्पादन कर रहा था और पार्सिंग और अतिरिक्त कर्नेल ट्रिप्स को बहुत अच्छी तरह से अनुकूलित नहीं किया गया है (पढ़ें: bind() की तुलना में सुपर धीमी) डायग्नोस्टिक पथ, केवल जानकारी प्राप्त करने के लिए जो कि इसे पार करने से पहले भी पुराना हो सकता है। तो बस bind() पर कॉल करें और खुश रहें।

4

मैंने इस से संघर्ष किया और थोड़ा कोड बदल दिया।

समाधान serv_addr.sin_port = 0 (ऑटो असाइन पोर्ट) सेट करना है।

नोटbind() और getsockname() लाइनों में sockaddr_in से sockaddr को अशुद्ध डाले देखते हैं। मैंने देखा है कि यह कई जगहों पर किया गया है और मैं एक सुरक्षित समाधान की तलाश में हूं।

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 

// ... snip ... 

int sock = socket(AF_INET, SOCK_STREAM, 0); 
if(sock < 0) { 
    printf("socket error\n"); 
    return; 
} 
printf("Opened %d\n", sock); 

struct sockaddr_in serv_addr; 
bzero((char *) &serv_addr, sizeof(serv_addr)); 
serv_addr.sin_family = AF_INET; 
serv_addr.sin_addr.s_addr = INADDR_ANY; 
serv_addr.sin_port = 0; 
if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 
    if(errno == EADDRINUSE) { 
     printf("the port is not available. already to other process\n"); 
     return; 
    } else { 
     printf("could not bind to process (%d) %s\n", errno, strerror(errno)); 
     return; 
    } 
} 

socklen_t len = sizeof(serv_addr); 
if (getsockname(sock, (struct sockaddr *)&serv_addr, &len) == -1) { 
    perror("getsockname"); 
    return; 
} 

printf("port number %d\n", ntohs(serv_addr.sin_port)); 


if (close (sock) < 0) { 
    printf("did not close: %s\n", strerror(errno)); 
    return; 
} 

कार्यक्रम उत्पादन

Opened 4 
port number 59081 
+0

"' getockname (...) 'पंक्ति में 'sockaddr_in' से' sockaddr' तक एक खतरनाक कलाकार है। " आपको किस तरह से लगता है कि यह खतरनाक है? हो सकता है कि आप वास्तव में सुरक्षित होने के लिए 'sockaddr_storage' का उपयोग करना चाहते हैं। – glglgl

+0

उनके पास समान आकार 16 बाइट हैं। 'Sin_len' और' sin_family' दोनों structs में एक ही स्थान पर स्थित हैं। मुझे बस reinterpret_cast कास्ट पसंद नहीं है। – neoneye

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