गैर-अवरुद्ध सॉकेट्स के साथ क्रॉस-प्लेटफ़ॉर्म https सर्वर। भाग ३

इस लेख में, मैं गैर-अवरुद्ध सॉकेट्स पर एकल-थ्रेडेड https सर्वर में सुधार करना जारी रखता हूं। स्रोत कोड के लिंक वाले पिछले लेख यहां देखे जा सकते हैं:
Ssl समर्थन के साथ सबसे सरल क्रॉस-प्लेटफ़ॉर्म सर्वर
गैर-अवरुद्ध सॉकेट्स के साथ क्रॉस-प्लेटफ़ॉर्म https सर्वर
गैर-अवरुद्ध सॉकेट्स के साथ क्रॉस-प्लेटफ़ॉर्म https सर्वर। भाग २

इस लेख के अंत में सर्वर के स्रोत कोड का एक लिंक होगा जिसे मैंने विज़ुअल स्टूडियो 2012 (विंडोज 8 64 बिट), जी ++ 4.4 (लिनक्स 32 बिट), जी ++ 4.6 (लिनक्स 64 बिट) में परीक्षण किया था। सर्वर किसी भी संख्या में ग्राहकों से कनेक्शन स्वीकार करता है और प्रतिक्रिया में अनुरोध हेडर भेजता है।
लेकिन मैं पिछले कुछ टिप्पणियों के जवाब के साथ लेख शुरू करूँगा।


सबसे पहले, मेरे कोड की असामान्यता के बारे में बहुत अधिक नकारात्मक प्रतिक्रिया मिली, अब से मैंने अपने लेखों को हब "असामान्य प्रोग्रामिंग" में भी डालने का फैसला किया।
दूसरे, मैंने यह तय किया कि अब मैं इस पर "ट्यूटोरियल" का निशान नहीं लगाऊंगा: किसी को मेरे लेखों में कुछ नया मिलेगा, लेकिन किसी को यह शौकिया नहीं मिलेगा। मुझे कोई आपत्ति नहीं है ...

अब मेरी प्रोग्रामिंग शैली के बारे में:
1. मैं हेडर फाइलों में कई कारणों से कोड लिखना जारी रखूंगा:
क) मैं अतिरिक्त इशारों के बिना कोड की पूरी संख्या जानना चाहता हूं और इसलिए यह मेरे लिए अधिक सुविधाजनक है।
बी) किसी भी समय, मैं क्लाइंट या सर्वर को टेम्पलेट को फास्ट करना चाहता हूं, और मैं इसके लिए सभी कोड को फिर से लिखना नहीं चाहूंगा।
जिन लोगों को यकीन है कि चूंकि मैं ऐसा नहीं कर सकता हूं, आप पहले stl सिखा सकते हैं और रचनाकारों को प्रोग्रामिंग को बढ़ावा दे सकते हैं, और फिर server.h फ़ाइल को server.cpp में बदल सकते हैं और सबकुछ ठीक हो जाएगा ...

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

3. मैं एक कारण के लिए memcpy के बजाय std :: copy का उपयोग नहीं करूँगा: std :: copy एक ब्रेक है!

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

अब मुख्य बात के बारे में।
अंत में अनुरोध शीर्ष लेखों को पार्स करने और फ़ाइलों को वितरित करने के लिए पिछले लेख से सर्वर को तैयार करने के लिए, यह एक छोटा जोड़ बनाने के लिए रहता है: एक अनंत लूप के बजाय, विशेष रूप से नेटवर्क घटनाओं के लिए प्रतीक्षा करने के लिए विशेष रूप से डिज़ाइन किए गए फ़ंक्शंस का उपयोग करना शुरू करें।
विंडोज और लिनक्स पर ऐसी कई विशेषताएं हैं, मैं सुझाव देता हूं कि विंडोज पर चयन करें और लिनक्स पर एपोल करें।

एक समस्या है कि एपोल फ़ंक्शन विंडोज पर मौजूद नहीं है। कोड को सभी प्रणालियों के अनुरूप देखने के लिए, आइए सर्वर कोड लिखें जैसे कि विंडोज पर एपोल!

चयन के साथ विंडोज के लिए सरल एपोल कार्यान्वयन
1. Visual Studio प्रोजेक्ट में उसी निर्देशिका से दो खाली फाइलें जोड़ें जहां "server.h" स्थित है। फ़ाइलें: "epoll.h" और "epoll.cpp"।
2. स्थिरांक दस्तावेज़ीकरण से स्थिरांक, संरचना और कार्यों की परिभाषाएँ epoll.h फ़ाइल में स्थानांतरित करें:
#ifndef __linux__ enum EPOLL_EVENTS { EPOLLIN = 0x001, #define EPOLLIN EPOLLIN EPOLLPRI = 0x002, #define EPOLLPRI EPOLLPRI EPOLLOUT = 0x004, #define EPOLLOUT EPOLLOUT EPOLLRDNORM = 0x040, #define EPOLLRDNORM EPOLLRDNORM EPOLLRDBAND = 0x080, #define EPOLLRDBAND EPOLLRDBAND EPOLLWRNORM = 0x100, #define EPOLLWRNORM EPOLLWRNORM EPOLLWRBAND = 0x200, #define EPOLLWRBAND EPOLLWRBAND EPOLLMSG = 0x400, #define EPOLLMSG EPOLLMSG EPOLLERR = 0x008, #define EPOLLERR EPOLLERR EPOLLHUP = 0x010, #define EPOLLHUP EPOLLHUP EPOLLRDHUP = 0x2000, #define EPOLLRDHUP EPOLLRDHUP EPOLLONESHOT = (1 << 30), #define EPOLLONESHOT EPOLLONESHOT EPOLLET = (1 << 31) #define EPOLLET EPOLLET }; /* Valid opcodes ( "op" parameter ) to issue to epoll_ctl(). */ #define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface. */ #define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface. */ #define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure. */ typedef union epoll_data { void *ptr; int fd; unsigned int u32; unsigned __int64 u64; } epoll_data_t; struct epoll_event { unsigned __int64 events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); #endif 


3. epoll.cpp फ़ाइल में हेडर जोड़ें, साथ ही एक वैश्विक चर जिसमें सॉकेट और उनके राज्य संग्रहीत किए जाएंगे:
 #include "epoll.h" #include <map> #ifndef WIN32 #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #else #include <io.h> #include <Winsock2.h> #pragma comment(lib, "ws2_32.lib") #endif std::map<int, epoll_event> g_mapSockets; 


4. पहले फ़ंक्शन के लिए कोड जोड़ें:
 int epoll_create(int size) { return 1; } 


यहां क्या हो रहा है?
जहां तक ​​मैं प्रलेखन से बता सकता हूं: मूल लिनक्स कोड प्रत्येक कॉल पर सॉकेट राज्यों के साथ एक फाइल बनाता है जिसे epoll_create कहा जाता है। स्पष्ट रूप से यह बहु-थ्रेडेड प्रक्रियाओं में आवश्यक है।
हमारे पास एक एकल-थ्रेडेड प्रक्रिया है और हमें सॉकेट्स को संग्रहीत करने के लिए एक से अधिक संरचना की आवश्यकता नहीं है। इसलिए, हमारे साथ epoll_create एक "ठूंठ" है।

5. मेमोरी में सॉकेट को जोड़ना, जोड़ना और हटाना प्राथमिक है:
 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) { switch(op) { case EPOLL_CTL_ADD: case EPOLL_CTL_MOD: g_mapSockets[fd] = *event; return 0; case EPOLL_CTL_DEL: if (g_mapSockets.find(fd) == g_mapSockets.end()) return -1; g_mapSockets.erase(fd); return 0; } return 0; } 


6. अंत में, मुख्य बात: हम चयन के माध्यम से प्रतीक्षा फ़ंक्शन को लागू करते हैं

 int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) { if ((!events) || (!maxevents)) return -1; //      select fd_set readfds, writefds, exceptfds; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); //   int nFDS = 0; for (auto it=g_mapSockets.begin(); it != g_mapSockets.end(); ++it) { if (it->first == -1) continue; if (it->first > nFDS) nFDS = it->first; FD_SET(it->first, &readfds); FD_SET(it->first, &writefds); FD_SET(it->first, &exceptfds); } //   struct timeval tv; tv.tv_sec = timeout/1000; tv.tv_usec = timeout - tv.tv_sec*1000; //  nFDS++; select(nFDS, &readfds, &writefds, &exceptfds, &tv); //     ,     epoll int nRetEvents = 0; for (auto it=g_mapSockets.begin(); (it != g_mapSockets.end() && nRetEvents < maxevents); ++it) { if (it->first == -1) continue; if (!FD_ISSET(it->first, &readfds) && !FD_ISSET(it->first, &writefds) && !FD_ISSET(it->first, &exceptfds)) continue; memcpy(&events[nRetEvents].data, &it->second.data, sizeof(epoll_data)); if (FD_ISSET(it->first, &readfds)) events[nRetEvents].events |= EPOLLIN; if (FD_ISSET(it->first, &writefds)) events[nRetEvents].events |= EPOLLOUT; if (FD_ISSET(it->first, &exceptfds)) events[nRetEvents].events |= EPOLLERR; nRetEvents++; } return nRetEvents; } 


वह सब है। विंडोज के लिए एपोल फ़ंक्शन लागू किया गया है!

सर्वर से एपोल जोड़ना

1. शीर्ष लेख में जोड़ें:
 #ifdef __linux__ #include <sys/epoll.h> #else #include "epoll.h" #endif 


2. CServer वर्ग में लाइनें जोड़ें:
  private: //   struct epoll_event m_ListenEvent; //   vector<struct epoll_event> m_events; int m_epoll; 


3. CServer कंस्ट्रक्टर में, सुनने के कार्य को बदलने के बाद सब कुछ बदल दिया जाता है:
  m_epoll = epoll_create (1); if (m_epoll == -1) { printf("error: epoll_create\n"); return; } m_ListenEvent.data.fd = listen_sd; m_ListenEvent.events = EPOLLIN | EPOLLET; epoll_ctl (m_epoll, EPOLL_CTL_ADD, listen_sd, &m_ListenEvent); while(true) { m_events.resize(m_mapClients.size()+1); int n = epoll_wait (m_epoll, &m_events[0], m_events.size(), 5000); if (n == -1) continue; Callback(n); } 


4. हम पुराने CServer की जगह लेते हैं :: कॉलबैक फ़ंक्शन को एक नए के साथ:
  void Callback(const int nCount) { for (int i = 0; i < nCount; i++) { SOCKET hSocketIn = m_events[i].data.fd; if (m_ListenEvent.data.fd == (int)hSocketIn) { if (!m_events[i].events == EPOLLIN) continue; struct sockaddr_in sa_cli; size_t client_len = sizeof(sa_cli); #ifdef WIN32 const SOCKET sd = accept (hSocketIn, (struct sockaddr*) &sa_cli, (int *)&client_len); #else const SOCKET sd = accept (hSocketIn, (struct sockaddr*) &sa_cli, (socklen_t *)&client_len); #endif if (sd != INVALID_SOCKET) { //      m_mapClients[sd] = shared_ptr<CClient>(new CClient(sd)); auto it = m_mapClients.find(sd); if (it == m_mapClients.end()) continue; //    epoll struct epoll_event ev = it->second->GetEvent(); epoll_ctl (m_epoll, EPOLL_CTL_ADD, it->first, &ev); } continue; } auto it = m_mapClients.find(hSocketIn); //    if (it == m_mapClients.end()) continue; if (!it->second->Continue()) // -   { //   false,     epoll     epoll_ctl (m_epoll, EPOLL_CTL_DEL, it->first, NULL); m_mapClients.erase(it); } } } 


सर्वर वर्ग समाप्त होने के साथ, यह CClient वर्ग से निपटने के लिए बना रहता है।
इसमें निम्न कोड जोड़ें:
  private: //   struct epoll_event m_ClientEvent; public: const struct epoll_event GetEvent() const {return m_ClientEvent;} 


और यहीं पर एपोल सपोर्ट कोड किया जाता है!

यहाँ विजुअल स्टूडियो के लिए प्रोजेक्ट है: l0.3s3s.org
लिनक्स पर संकलन के लिए, epoll.h और epoll.cpp फ़ाइलों की आवश्यकता नहीं है, अर्थात्, सब कुछ हमेशा की तरह है: "फ़ाइलों की प्रतिलिपि बनाएँ: serv.cpp, server.h, ca-cert.pem को एक ही निर्देशिका में टाइप करें और" G ++ -std = c ++ 0x -L / usr / lib -lssl -lcrypto serv.cpp »++

विस्तार

Source: https://habr.com/ru/post/In212101/


All Articles