2010-07-24 11 views
29

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

एक प्रक्रिया डेटा (अनौपचारिक, बाइनरी) प्राप्त करती है और डाटाग्राम प्रोटोकॉल (यानी AFDPET के साथ यूडीपी के समान) का उपयोग करके स्थानीय डेटा AF_UNIX सॉकेट के माध्यम से इस डेटा को वितरित करेगी। इस प्रक्रिया से स्थानीय यूनिक्स सॉकेट में भेजे गए डेटा को एक ही सॉकेट पर सुनने वाले एकाधिक ग्राहकों द्वारा प्राप्त किया जाएगा। रिसीवर की संख्या अलग-अलग हो सकती है। साथ errno रिपोर्टिंग ENOTCONN ("परिवहन endpoint जुड़ा हुआ नहीं है

struct sockaddr_un ipcFile; 
memset(&ipcFile, 0, sizeof(ipcFile)); 
ipcFile.sun_family = AF_UNIX; 
strcpy(ipcFile.sun_path, filename.c_str()); 

int socket = socket(AF_UNIX, SOCK_DGRAM, 0); 
bind(socket, (struct sockaddr *) &ipcFile, sizeof(ipcFile)); 
... 
// buf contains the data, buflen contains the number of bytes 
int bytes = write(socket, buf, buflen); 
... 
close(socket); 
unlink(ipcFile.sun_path); 

यह लिखने रिटर्न -1:

इस निम्नलिखित कोड प्राप्त करने के लिए एक सॉकेट बनाने और इसे (सर्वर प्रक्रिया) को डेटा भेजने के लिए किया जाता है ")। मुझे लगता है कि ऐसा इसलिए है क्योंकि वर्तमान में कोई स्थानीय प्रक्रिया इस स्थानीय सॉकेट को नहीं सुन रही है?

फिर, मैंने इस सॉकेट से कनेक्ट होने वाले क्लाइंट को बनाने का प्रयास किया।

struct sockaddr_un ipcFile; 
memset(&ipcFile, 0, sizeof(ipcFile)); 
ipcFile.sun_family = AF_UNIX; 
strcpy(ipcFile.sun_path, filename.c_str()); 

int socket = socket(AF_UNIX, SOCK_DGRAM, 0); 
bind(socket, (struct sockaddr *) &ipcFile, sizeof(ipcFile)); 
... 
char buf[1024]; 
int bytes = read(socket, buf, sizeof(buf)); 
... 
close(socket); 

यहां, बाइंड विफल रहता है ("पता पहले से उपयोग में है")। तो, क्या मुझे कुछ सॉकेट विकल्प सेट करने की ज़रूरत है, या क्या यह आम तौर पर गलत दृष्टिकोण है?

किसी भी टिप्पणी/समाधान के लिए अग्रिम धन्यवाद!

+0

चेक भी ग्राहक और सी के रूप में सर्वर के रूप में [यहां] (http://stackoverflow.com/a/43421610/4626775) –

उत्तर

-3

आपको यूनिक्स-डोमेन की बजाय आईपी मल्टीकास्टिंग में देखना चाहिए। वर्तमान में आप कहीं भी लिखने की कोशिश कर रहे हैं। और यदि आप एक ग्राहक से जुड़ते हैं तो आप केवल उस ग्राहक को लिखेंगे।

यह सामान इस तरह से काम नहीं करता है जैसा आपको लगता है कि ऐसा करता है।

+0

मल्टीकास्टिंग प्रक्रियाओं के लिए php? –

+0

ज़रूर, क्यों नहीं? अन्यथा उसे प्रत्येक संदेश को एन बार भेजना पड़ता है, और ग्राहकों को एक ही समय में संदेश नहीं मिलते हैं, जो निष्पक्षता मुद्दों को उठाता है। – EJP

+0

मैं क्षमा चाहता हूं; मैंने कभी यूनिक्स सॉकेट के लिए मल्टीकास्ट पते नहीं देखा है। क्या आपके पास एक कामकाजी उदाहरण है, या क्या मैंने आपकी पोस्ट को गलत समझा? –

-1

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

यदि आप एकाधिक ग्राहकों को कुछ देना चाहते हैं, तो आप एक सर्वर बनाते हैं जो कनेक्शन की प्रतीक्षा करता है और फिर सभी क्लाइंट कनेक्ट हो सकते हैं और यह उन्हें जानकारी देता है। आप प्रोग्राम को बहु-थ्रेडेड या फोर्किंग प्रक्रियाओं द्वारा समवर्ती कनेक्शन स्वीकार कर सकते हैं। सर्वर एकाधिक सॉकेट से जुड़े कई सॉकेट-आधारित कनेक्शन स्थापित करता है, बजाय एक सॉकेट होने के बजाय कई क्लाइंट कनेक्ट होते हैं।

7

अपने त्रुटि का आसन्न कारण है कि write() पता नहीं है जहां को डेटा भेजने करना चाहते हैं। bind() सॉकेट के आपके पक्ष का नाम सेट करता है - यानी। जहां डेटा से आ रहा है। सॉकेट के गंतव्य पक्ष को सेट करने के लिए, आप या तो connect() का उपयोग कर सकते हैं; या आप write() के बजाय sendto() का उपयोग कर सकते हैं।

अन्य त्रुटि ("पता पहले से उपयोग में है") क्योंकि केवल एक प्रक्रिया bind() पते पर हो सकती है।

आपको इसे ध्यान में रखने के लिए अपना दृष्टिकोण बदलना होगा। आपके सर्वर को bind() के साथ सेट एक प्रसिद्ध पते पर सुनने की आवश्यकता होगी। डेटाग्राम प्राप्त करने में रुचि रखने के लिए आपके ग्राहकों को इस पते पर सर्वर को एक संदेश भेजने की आवश्यकता होगी। सर्वर recvfrom() का उपयोग कर ग्राहकों से पंजीकरण संदेशों को प्राप्त करेगा, और प्रत्येक ग्राहक द्वारा उपयोग किए गए पते को रिकॉर्ड करेगा।जब यह एक संदेश भेजना चाहता है, तो उसे बदले में प्रत्येक संदेश को भेजने के लिए sendto() का उपयोग करके, उन सभी ग्राहकों पर लूप करना होगा।

वैकल्पिक रूप से, आप यूनिक्स डोमेन सॉकेट के बजाय स्थानीय आईपी मल्टीकास्ट का उपयोग कर सकते हैं (यूनिक्स डोमेन सॉकेट मल्टीकास्ट का समर्थन नहीं करते हैं)।

-4

आप नीचे दिए गए कोड के साथ बाँध त्रुटि को हल कर सकते हैं:

int use = yesno; 
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&use, sizeof(int)); 

यूडीपी प्रोटोकॉल के साथ, आप connect() आह्वान चाहिए आप write() या send() उपयोग करना चाहते हैं, अन्यथा आप sendto() बजाय का उपयोग करना चाहिए।

अपनी आवश्यकताओं को प्राप्त करने के लिए निम्न छद्म कोड मदद की हो सकती है:

sockfd = socket(AF_INET, SOCK_DGRAM, 0) 
set RESUSEADDR with setsockopt 
bind() 
while (1) { 
    recvfrom() 
    sendto() 
} 
+0

ओपी ने AF_UNIX के लिए एक समाधान के बारे में पूछा AFFCET – Flow

36

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

कारण "आप पहले से ही उपयोग में पता" देख रहे हैं क्योंकि आप क्लाइंट को सर्वर के समान पते से जुड़ने के लिए कह रहे हैं। bind() बाहरी पहचान का दावा करने के बारे में है। दो सॉकेट आमतौर पर एक ही नाम नहीं हो सकता है।

आंकड़ारेख सॉकेट के साथ

, विशेष रूप से यूनिक्स डोमेन आंकड़ारेख सॉकेट, ग्राहक अपने खुद समाप्ति बिंदु को bind(), तो connect()सर्वर के समाप्ति बिंदु को करना पड़ता है। इस बिंदु अपने सॉकेट पूरी तरह से सेटअप होना चाहिए पर

char * server_filename = "/tmp/socket-server"; 
char * client_filename = "/tmp/socket-client"; 

struct sockaddr_un server_addr; 
struct sockaddr_un client_addr; 
memset(&server_addr, 0, sizeof(server_addr)); 
server_addr.sun_family = AF_UNIX; 
strncpy(server_addr.sun_path, server_filename, 104); // XXX: should be limited to about 104 characters, system dependent 

memset(&client_addr, 0, sizeof(client_addr)); 
client_addr.sun_family = AF_UNIX; 
strncpy(client_addr.sun_path, client_filename, 104); 

// get socket 
int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); 

// bind client to client_filename 
bind(sockfd, (struct sockaddr *) &client_addr, sizeof(client_addr)); 

// connect client to server_filename 
connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)); 

... 
char buf[1024]; 
int bytes = read(sockfd, buf, sizeof(buf)); 
... 
close(sockfd); 

: यहाँ अपने ग्राहक कोड, थोड़ा में फेंक दिया कुछ अन्य उपहार के साथ, संशोधित है। मुझे लगता है कि सैद्धांतिक रूप से आप read()/write() का उपयोग कर सकते हैं, लेकिन आमतौर पर मैं डेटाग्राम सॉकेट के लिए send()/recv() का उपयोग करता हूं।

आम तौर पर आप इनमें से प्रत्येक कॉल के बाद त्रुटि जांचना चाहते हैं और बाद में perror() जारी करना चाहते हैं। जब चीजें गलत होती हैं तो इससे आपको बहुत मदद मिलेगी। सामान्य रूप से, इस तरह के पैटर्न का उपयोग करें:

if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { 
    perror("socket failed"); 
} 

यह किसी भी सी सिस्टम कॉल के लिए बहुत अधिक है।

स्टीवन का "यूनिक्स नेटवर्क प्रोग्रामिंग" इसके लिए सबसे अच्छा संदर्भ है। तीसरे संस्करण में, धारा 15.4, पेज 415-419 कुछ उदाहरण दिखाते हैं और कई चेतावनियों को सूचीबद्ध करते हैं।

वैसे,

मैं इस अनुमान के संदर्भ में है क्योंकि प्राप्त करने का कोई प्रक्रिया वर्तमान में इस स्थानीय सॉकेट सुन रहा है, सही है?

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

+0

मैं इस उत्तर का उपयोग करके डॉट्स को जोड़ने में सक्षम था, @caf से एक और अंतिम 2 स्रोत फ़ाइलों http://www.thomasstover.com/uds.html पर नोट (नोट कि उस कोड में कुछ मामूली बग हैं)। प्रश्न में सर्वर कोड क्लाइंट एड्रेस (एसएस) प्राप्त किए बिना काम नहीं करेगा, जिसे मैंने कैफ के जवाब को पढ़ते समय महसूस किया था। – hBrent

+1

'यूडीपी सॉकेट सामान्य रूप से शिकायत नहीं करेगा' से शुरू होने वाले वाक्यों और 'यह यूनिक्स डोमेन डेटाग्राम सॉकेट को यूडीपी से काफी बेहतर बनाता है' दोनों गलत हैं। अधिकतर यदि यूनिक्स-डोमेन डेटाग्राम सॉकेट के बारे में आपने जो कुछ कहा है, वह सब आईपी-डोमेन यूडीपी सॉकेट के समान नहीं होता है: विशेष रूप से, उन्हें 'लिखने()' का उपयोग करने के लिए जोड़ा जाना चाहिए, और वे 'लिखने' में ब्लॉक करते हैं() 'या 'भेजें()' जबकि भेजें बफर भरा हुआ है। गलत जानकारी के लिए -1। – EJP

+0

@EJP: मुझे लगता है कि आपने मेरे कुछ जवाब गलत समझा। मेरा मतलब यह नहीं था कि 'लिखना() 'के दौरान सॉकेट को अनकनेक्ट किया जा सकता था, और मैंने पूर्ण प्रेषण बफर (केवल एक पूर्ण प्राप्त बफर) के बारे में कुछ भी नहीं बताया। मैंने नीचे वर्णित अनुच्छेद को स्थानांतरित कर दिया क्योंकि यह एक सहायक प्रश्न पर चर्चा करता है, जो भ्रम का हिस्सा हो सकता है। – adamlamar

1

तो सवाल (मैं यह समझ के रूप में), प्रसारण के बारे में होने तो unix(4) - UNIX-domain protocol family के अनुसार, यह प्रसारण इरादा यूनिक्स डोमेन सॉकेट के साथ उपलब्ध नहीं है:

यूनिक्स Ns -domain प्रोटोकॉल परिवार का समर्थन नहीं करता आने वाले संदेशों पर प्रसारण पते या "वाइल्डकार्ड" मिलान का कोई भी प्रकार । सभी पते पूर्ण हैं- या अन्य यूनिक्स एनएस -डोमेन सॉकेट के सापेक्ष-पथनाम।

मल्टीकास्ट एक विकल्प हो सकता है, लेकिन मुझे लगता है कि यह POSIX के साथ उपलब्ध नहीं है, हालांकि Linux supports UNIX Domain Socket multicast

यह भी देखें: Introducing multicast Unix sockets

-1

यह सर्वर या क्लाइंट के कारण अनलिंक/बाध्य() फ़ाइल सहयोगी को हटाने से पहले मर जाएगा। इस बाइंड पथ का उपयोग कर क्लाइंट/सर्वर में से कोई भी, सर्वर को फिर से चलाने का प्रयास करें।

समाधान: जब आप फिर से बांधना चाहते हैं तो बस जांचें कि फ़ाइल पहले से ही संबद्ध है और फिर उस फ़ाइल को अनलिंक करें। कैसे कदम करें: पहले इस फ़ाइल तक पहुंच (2) तक पहुंच की जांच करें; यदि हां तो अनलिंक (2) इसे। बाइंड() कॉल से पहले कोड की शांति डाल दें, स्थिति स्वतंत्र है।

if(!access(filename.c_str())) 
    unlink(filename.c_str()); 
अधिक संदर्भ के लिए

पढ़ यूनिक्स (7)

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