Винсок бойынша оқулық Джонни

Егер сіз әдейі Винсок менің сабаққа келді, онда сіз де мен сияқты қызықты Зерттеуші ретінде Интернет арқылы өзара іс-қимыл, өз өтініштері суретті табуың мүмкін. Немесе, мүмкін, біреу бірдей қызықты болашағы тапты, және сіз шындыққа осы көзқарасы тарту миссиясымен тапсырылды. Кез-келген жағдайда, Винсокжелі қызметі, және бұл оқулық сіз жай ғана желілік жеке пайдалануға арналған бағдарламалау, немесе арасындағы нәрсе аясын зерттеу арқылы бизнестің Сіздің мақсаттарға жету көмектеседі.

Яғни, біз қамтуы алатындарыңыз қандай:

  • Розетканы жасау: желілік функцияларды шағын әскерін ескере отырып, біз кіріс байланыстары үшін шыдамдылықпен күтеді бағдарламасын құруға болады? (Иә, біз аламыз.)
  • Сіздің жеке қосылымдарды жасаңыз: тағы бірнеше ерекшеліктері ескере отырып, біз табысты тыңдауға серверге қосылады бағдарламасын жасауға болады? (Иә).
  • Жіберу және қабылдау: біз екі бағдарламалар арасында деректер алмасу үшін, оны қалай пайдаланатынын белсенді қосылымды жетті кейін? (Сіз оны сұраса – () және қабылдау () жібереді.)
  • Non-блоктау және асинхронды Sockets: қалай біз түрлі желілік сызбаны жүзеге асыру арқылы біздің код тиімділігін арттыру болады? (Біз терезенің рәсімі жеткілікті емес vozimsya.)
  • Ең оқулықтар мен әдебиеттер тізімі: Бұл сабақта асатын қандай ресурстар бар? 3 сен менің оқулық дайджест кейін сіз Әрине 🙂 туралы, (қысқа уақыт қажет екенін атап өтті.
  • Пікірлер мен сын: мұнда, сабақ бағалау сұрақтар қоюға немесе Пікір қалдыру мүмкіндігі бар.Егер сіз қосымша табысты өзінің алғашқы байланысын орнатады, онда әсерлі нүктесін жету үшін дайын болуы мүмкін, ал, код ұғымдардың хабардар болуы. Егер шұғыл қажеттіліктерін қанағаттандыру үшін бекер деректер көзінің айла болдырмау үшін көріңіз, бірақ оның орнына сіздің қолдану талаптарды анықтайды, содан кейін жақсы шешім, меніңше, қандай іске асыру. бағдарламалық қамтамасыз етуді әзірлеу жөніндегі менің Zen кеңесінің жеткілікті желілік бағдарламалау алуға мүмкіндік, яғни …

    Коды бойынша сабақтар толық тізімін жүктеп алу үшін тартынба.
    скачать весь список уроков по коду
    кез келген коды осы оқулықта ұсынылған Есіңізде болсын, wsock32.lib немесе осы сияқты бірдеңе әдетте, Winsock кітапхана байланысты болуы тиіс. Егер IDE жылы оқулықта көрсетілген кодты дәл сондай пайдаланған кезде (т.б. Dev-C ++, Microsoft VC ++, C ++ Builder, және.) Сонымен қатар, қателерді болдырмау үшін WinMain () бар Windows жобасын жасау үшін таңдаңыз.


    Тыңдау ұясын (тыңдау Socket) жасау

    Машинаның тыс қызмет көрсететін Бағдарламалар серверлер деп аталады. Серверлік қосымшалар тыңдау розеткадан біреуін немесе бірнешеуін баптандыру үшін қалай тапсырыс берушінің тыңдау. клиент осы ұялардың біріне қосылғанда, сервер Winsock бастап хабарлама алған, байланысты қабылдаңыз, және жаңа тапсырыс берушінің және хабарларды жіберуге және қиылыс бастайды. Мүмкін серверлер бірнеше клиенттерге өңдеуге, ол ең қарапайым әдісі, ол әрбір клиент қосылу үшін жаңа жіп уылдырық отыр. Бұл сервер моделі жиі уақытша тоқтатылды және кіріс деректердің жаңа қосылыстар және басқа да іс-шараларға желілік күтеміз розеткалар, бұғаттау пайдаланады. Алдымен біз бұғаттау розетканы бастау үшін қажет болады, ол үшін кейбір құрылымдарды, анықтау мүмкіндік береді:

     

  • WSADATA: Бұл құрылым біздің кодын талап Winsock үшін сұрау операциялық жүйе нұсқасы үшін пайдаланылады. өтініш тиісті Winsock DLL баптандыру үшін WSAStartup () деп атайды.
  • SOCKET: объект (шын мәнінде, ол сондай-ақ тараптардың кезінде өсек белгілі — winsock.h жылы, u_int, қол қойылмаған бүтін ретінде анықталады) сабы розетканы сақтау үшін пайдаланылған қолданбалар.
  • SOCKADDR_IN: өтініш розетканы жұмыс істеу көрсету үшін осы құрылымды пайдаланады. SOCKADDR_IN IP-мекен-жайы және порт нөмірі үшін өрістерді қамтиды:
    struct sockaddr_in
    
    {
    
      short sin_family;         // тип протокола
    
      u_short sin_port;         // порт-номер сокета
    
      struct in_addr sin_addr;  // IP-адрес
    
      char sin_zero[8];         // не используется
    
    };

    Бірінші өрісі хаттама түрі, әдетте, AF_INET (TCP / IP) болып табылады. Тыңдау розетка, онда ол орналасқан бойынша машинаның желілік мекен-жайы байланысты емес болғандықтан, Winsock автоматты түрде құру кезінде тыңдау розеткадан үшін IP-мекен-жайы мен порт нөмірін тағайындайды.

    Біз жоғарыда құрылымдардың бірінші тыңдау серверін және желілік функцияларды шағын әскер жасақтауымыз болады:

    #include <windows.h>
    
    #include <winsock.h>
    
    #include <stdio.h>
    
    
    #define NETWORK_ERROR -1
    
    #define NETWORK_OK     0
    
    
    void ReportError(int, const char *);
    
    
    
    
    int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShow)
    
    {
    
            WORD sockVersion;
    
            WSADATA wsaData;
    
            int nret;
    
    
            sockVersion = MAKEWORD(1, 1);                 // нам бы Winsock версии 1.1
    
    
    
            // Мы начинаем с инициализации Winsock
    
            WSAStartup(sockVersion, &wsaData);
    
    
    
            // Затем создаем прослушивающий сокет
    
            SOCKET listeningSocket;
    
    
    
            listeningSocket = socket(AF_INET,             // идем через TCP/IP
    
                                    SOCK_STREAM,          // Это сокет, ориентированный на поток
    
                                    IPPROTO_TCP);         // Используйте TCP вместо UDP
    
    
            if (listeningSocket == INVALID_SOCKET)
    
            {
    
                   nret = WSAGetLastError();             // Получите более подробную ошибку
    
                   ReportError(nret, "socket()");        // Сообщите об ошибке с помощью нашей пользовательской функции
    
    
                   WSACleanup();                         // Закрываем Winsock
    
                   return NETWORK_ERROR;                 // Верните значение ошибки
    
            }
    
    
    
            // используйте SOCKADDR_IN структуру для заполнения адресной информации
    
            SOCKADDR_IN serverInfo;
    
    
            serverInfo.sin_family = AF_INET;
    
            serverInfo.sin_addr.s_addr = INADDR_ANY;      // Так как этот сокет прослушивает подключения,
    
                                                         // любой локальный адрес будет делать
    
            serverInfo.sin_port = htons(8888);            // Преобразование целого 8888 на порядок network-byte
    
                                                         // и вставляете в поле порта
    
    
    
            // Привяжите сокет к нашему адресу локального сервера
    
            nret = bind(listeningSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));
    
    
            if (nret == SOCKET_ERROR)
    
            {
    
                   nret = WSAGetLastError();
    
                   ReportError(nret, "bind()");
    
    
                   WSACleanup();
    
                   return NETWORK_ERROR;
    
            }
    
    
    
    
            // Make the socket listen
    
            nret = listen(listeningSocket, 10);           // Up to 10 connections may wait at any
    
                                                         // one time to be accept()'ed
    
    
            if (nret == SOCKET_ERROR)
    
            {
    
                   nret = WSAGetLastError();
    
                   ReportError(nret, "listen()");
    
    
                   WSACleanup();
    
                   return NETWORK_ERROR;
    
            }
    
    
    
    
            // ждите клиента
    
            SOCKET theClient;
    
    
    
            theClient = accept(listeningSocket,
    
                              NULL,                       // При желании, адрес SOCKADDR_IN структурируется
    
                              NULL);                      // При желании, адрес переменной содержит
    
                                                         // sizeof ( struct SOCKADDR_IN )
    
    
            if (theClient == INVALID_SOCKET)
    
            {
    
    
                   nret = WSAGetLastError();
    
                   ReportError(nret, "accept()");
    
    
                   WSACleanup();
    
                   return NETWORK_ERROR;
    
            }
    
    
    
            // Отправляйтe и получайте от клиента, и, наконец,
    
            closesocket(theClient);
    
            closesocket(listeningSocket);
    
    
    
            // закрывайте Winsock
    
            WSACleanup();
    
            return NETWORK_OK;
    
    }
    
    
    
    
    void ReportError(int errorCode, const char *whichFunc)
    
    {
    
       char errorMsg[92];                                // Декларируйте буфер для удержания
    
                                                         // сообщение об ошибке генерируется
    
    
       ZeroMemory(errorMsg, 92);                         // автоматически завершится и обнулится строка
    
    
       // Следующая строка копирует фразу, whichFunc строку, и интегрирует код ошибки в буфер
    
       sprintf(errorMsg, "Call to %s returned error %d!", (char *)whichFunc, errorCode);
    
    
       MessageBox(NULL, errorMsg, "socketIndication", MB_OK);

    Сіз бірден код аңғаруға болады жалғыз нәрсе қате тексеру лауазымына күш сомасы болып табылады. қате орын кезде, коды WSAGetLastError () пайдалана отырып, тиісті қате кодын қабылдайды және желідегі нәтиже сақтайды. қате коды) ReportError (атты таңдамалы функциясы сәтсіз функцияның атын көрсете отырып, жолдың бірге жіберіледі. Онда, қате туралы хабар көрсетіледі және пайдаланушы стандартты WinAPI бөлігі болып табылады MessageBox () қоңырау, пайдаланып құрылған. Мысалы, толық ақаулық желісі болар еді, (WSANOTINITIALISED ретінде анықталады) қате коды бар 10093 үшін) (тыңдау, сәтсіз аяқталды «) (тыңдауға Call қате 10093 оралды». Сіз үнемді әзірлеуші, сіз код көзқараспен және WSAStartup табысты қоңырау () әлі жасалған жоқ, өйткені қате пайда болды, бұл табылған.

    Александр Павлов туралы ондаған стандартты розетка қатенің сипаттау қамтуы,) (осы қате туралы хабарды ұзартылды. Оның соңғы нұсқасын, сіз бұдан былай код, яғни, іздеу үшін қажет болады, және сіздің бағдарлама сіздің бөлігінде өте аз күш неғұрлым ыңғайлы болып.

    Сондай-ақ, NETWORK_ERROR және NETWORK_OK анықталған. Сіздің жеке желі функцияларын қайтару мәні тексеру кезінде пайдалы болуы мүмкін. Егер функциясы осы мәндердің бірін қайтарады болса, қоңырау функциясы кез келген қателерді анықтау үшін, теңдік қарапайым тест орындауға болады, егер: (myNetworkingFunction () == NETWORK_ERROR) {…}. Қоңырау шалушы содан тиісінше WSAGetLastError (үшін арнайы код алуға) және қатені өңдеуге болады. сіз бірден неге сіздің бағдарлама polamat білетін болады, өйткені Сайып келгенде, жақсы қате өңдеу схемасы қазір іске асыру сізге дамыту уақыт күн немесе апта көп үнемдеуге мүмкіндік береді.

    Сонымен қатар, тапсырыс берушінің жаңа қосылым қайтаруға (қабылдауға) орнына қосымша уақыт немесе (жылдамдық сыни хост цикл әсіресе ойын серверлерінде проблема болуы мүмкін) функциясы қоңырау талап әдістерін пайдаланудан гөрі, сервер тұтынушы туралы ақпаратты алуға мүмкіндік береді. Осы функцияны пайдалануға SOCKADDR индексі құйылған sockaddr_in құрылымында мекенжайына өту, яғни (LPSOCKADDR) & aSockaddrInStructure. Сонымен қатар, SOCKADDR құрылымында sizeof INT құнына бүтін айнымалы жиынтығы мәлімдей, және үшінші параметр ретінде бүтін мекенжайын өтеді. мекен-жайы туралы ақпарат қоңырау кейін қайтарылуы тиіс болса, ұзындығы параметр болуы тиіс.


    jdarnold осы параметр бойынша MSDN құжаттаманы үшінші сену емес бізге былай деп ескертеді: «MSDN құжат сіз осы ғана қосымша шығыс параметрі екенін addrlen өтіңіз қажеті жоқ деп есептейді, бірақ олар дұрыс емес Кіріс қанша байт SOCKADDR буфер және шығыс [дейді. Winsock]] көптеген [Winsock толтырады қолданылады. Егер сіз Len ретінде нөлге өтіңіз болса, [Winsock] буферін әсер етпейді «.


    Ол қосылу үшін тек бір пайдаланушы үшін күтеді етіп, серверден mnogozavisit, содан кейін бірден өшірілген, бірақ бұл негізгі жобалау болып табылады. жай нәрселерді тазалау үшін, WSAStartup () қоңырау сіз (бұл жағдайда 1.1) жүктеу және WSADATA құрылымын шешу үшін келетін нұсқасы көрсетеді Сөзді кіреді. Келесі, біз басқа компьютерлерге қосу үшін қалай қараймыз.

    Сіздің жеке байланыс жасау

    басқа біреуге қосылу үшін сокет жасау бөлігі HOSTENT құрылымын қоспағанда, бірдей функцияларды пайдаланады:

     

  • HOSTENT: компьютерлік және порт қосылатын сокет айтып үшін пайдаланылады құрылымы. Бұл құрылымдар әдетте hostent құрылымына ғана көрсеткіштер болып табылады HOSTENT IP айнымалы ретінде пайда болады. Егер сіз Windows коды болғандықтан, сіз predshestvovshy LP түрі іс жүзінде екенін көрсетеді деректер кез-келген түрі «базасының» түрі (мысалы, LPCSTR сондай-ақ CHAR * ретінде белгілі, С желісі сілтегіш) көрсетеді екенін түсіну prinitspe.Сондықтан, кодексіне тікелей алуға мүмкіндік:
    #include <windows.h>
    
    #include <winsock.h>
    
    #include <stdio.h>
    
    
    #define NETWORK_ERROR -1
    
    #define NETWORK_OK     0
    
    
    void ReportError(int, const char *);
    
    
    
    
    int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmd, int nShow)
    
    {
    
            WORD sockVersion;
    
            WSADATA wsaData;
    
            int nret;
    
    
    
            sockVersion = MAKEWORD(1, 1);
    
    
    
            // Инициализация Winsock, как и раньше
    
            WSAStartup(sockVersion, &wsaData);
    
    
    
            // Хранит информацию о сервере
    
            LPHOSTENT hostEntry;
    
            hostEntry = gethostbyname("www.yahoo.com");   // Указание сервера по его имени;
    
                                                         // другой вариант: gethostbyaddr()
    
            if (!hostEntry)
    
            {
                   nret = WSAGetLastError();
    
                   ReportError(nret, "gethostbyname()"); // Сообщает об ошибке, как и раньше
    
    
                   WSACleanup();
    
                   return NETWORK_ERROR;
    
            }
    
    
    
            // Создает сокет
    
            SOCKET theSocket;
    
    
            theSocket = socket(AF_INET,                   // через TCP/IP
                              SOCK_STREAM,                // Это поток, ориентированный на сокет
    
                              IPPROTO_TCP);               // используйте TCP вместо UDP
    
    
            if (theSocket == INVALID_SOCKET)
    
            {
                   nret = WSAGetLastError();
    
                   ReportError(nret, "socket()");
    
    
                   WSACleanup();
    
                   return NETWORK_ERROR;
    
            }

     

     

            // заполните SOCKADDR_IN структуру адресной информацией
    
            SOCKADDR_IN serverInfo;
    
    
            serverInfo.sin_family = AF_INET;
    
    
            // На данный момент, мы успешно извлекли жизненно важную информацию о сервере,
    
            // в том числе его имя хоста, псевдонимы и IP-адреса. Подождите; как мог один
            // Компьютер иметь несколько адресов, и что именно следующая строка делает?
    
            // Читайте объяснение ниже.
    
    
            serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);
    
    
    
            serverInfo.sin_port = htons(80);              // Перейдите в сетевой порядок байт и
    
                                                                                 // введите в поле порта

     

            // подсоединитесь к серверу
    
            nret = connect(theSocket,
    
                         (LPSOCKADDR)&serverInfo,
    
                          sizeof(struct sockaddr));
    
    
            if (nret == SOCKET_ERROR)
    
            {
    
                   nret = WSAGetLastError();
    
                   ReportError(nret, "connect()");
    
    
    
                   WSACleanup();
    
                   return NETWORK_ERROR;
    
            }
    
    
            // успешно подсоединено!
    
    
            // Отправьте / получите, а затем очистите:
    
            closesocket(theSocket);
    
            WSACleanup();
    }
    
    
    void ReportError(int errorCode, const char *whichFunc)
    
    {
    
       char errorMsg[92];                                // Декларируйте буфер для удержания
    
                                                         // сообщение об ошибке генерируется
    
    
       ZeroMemory(errorMsg, 92);                         // Автоматически NULL-завершает строку
       // Следующая строка копирует фразу, которая Func строку, и интегрирует код ошибки в буфер
    
       sprintf(errorMsg, "Call to %s returned error %d!", (char *)whichFunc, errorCode);
    
    
    
       MessageBox(NULL, errorMsg, "socketIndication", MB_OK);
    
    }

    Төмендегідей тізіміне ең қиын желісі болып табылады:


    serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);


    Салыстырмалы бірден — жасырын солардың бірі болып табылады — бірнеше операцияларды орындайды, өйткені. ның қадамдық оны бір-бірінен алайық:

    Мүше HOSTENT h_addr_list құрылымы, әдетте, жолдар немесе CHAR жиымы болып ** h_addr_list, * келіңіздер операндоға ретінде анықталады. Тізімдегі сервердің мекен-жайы белгілі барлық анықтау және көшіру үшін gethostbyname (). Дегенмен, бірнеше мекен-жайы тұжырымдамасы түбегейлі мағынасы қандай? Шын мәнінде, ол жасайды. Компьютеріңіз, шын мәнінде, көптеген ортақ желі мекенжайларын бар. Сіздің Интернет адресі 205.182.67.96 болуы мүмкін, сіздің жергілікті желі мекенжайы 10.0.0.2 болуы мүмкін, және Microsoft Windows іске қосылған барлық компьютерлер, әрине, «қысқа» мекен-жайы 127.0.0.1, компьютерлік жергілікті желіге өзі қараңыз үшін пайдаланылады. сол тұжырымдамасы Интернет мекен-жайы немесе IP мекен-жайы саласындағы қолданылады, сондықтан тізімі қажет, бірақ бір мекенжайын сақтау үшін емес, орын. артықшылықты мекен-жайы, яғни, қолда бар ең жақсы мекен-жайы әрқашан бірінші тізім элементіне көшіріледі, содан кейін екінші артықшылықты немесе басқа мекенжайлары отыр ескеріңіз.

    * HostEntry-> h_addr_list нені білдіреді? Сіз разыменовывания оператор (*) тізімінде бірдей мекен-жайы қол жеткізу үшін қолданылады деп ойлаймын мүмкін. Алайда, белгілі бір индексі қамтамасыз етуге жағдайы жоқ, разыменовывания операторы автоматты түрде, бірінші артықшылықты мекенжайын көрсетеді. Бұл арнайы бөлім сервер кем дегенде бір мекен-жайы болуы керек, өйткені, бар кепілдік * hostEntry-> h_addr_list [0], тең.

    Келесі, * разыменовывания оператор in_addr * немесе LPIN_ADDR отқа тасталатын операнд қайтарады. Соңында, тағы бір кідіріс операция тек бір мекен-жайы ұстап алады көрсеткіш, сілтеме құрылымын in_addr қайтару үшін жүзеге асырылады. нәтижесінде алынған құрылымы, содан кейін serverInfo.in_addr in_addr тағайындалады. Кейінгі қосылу () серверіне қосылымдар қалыптастыруда параметр ретінде URL мекен-жайын алады.

    Серверінің IP-мекен-жайы белгілі болса, жарамды HOSTENT (gethostbyname айырмашылығы () алдыңғы листингіне пайдаланылатын)) (gethostbyaddr пайдалану арқылы алуға болады:

    LPHOSTENT hostEntry;
    
    in_addr iaHost;
    
    
    iaHost.s_addr = inet_addr("204.52.135.52");
    
    
    hostEntry = gethostbyaddr((const char *)&iaHost, sizeof(struct in_addr), AF_INET);
    
    
    if (!hostEntry)
    
    {
    
            // Обращайтесь соответственно
    
    }

    Бұл жағдайда, inet_addr () in_addr құрылымында тікелей IP-мекен-жайын көрсете отырып жолды көшіру үшін пайдаланылады. Кейіннен, құрылымы) талаптар gethostbyaddr (сәйкес тұрақты мекен-жайы операнд * алады. Екі әдістері ішінара мекенжай ақпарат Winsock толық жазба ретінде мекенжай қаулы сервер қайтару деп аталады.

    Бірнеше қосымша ноталары: Интернетте беттердің аударма осы порты арқылы келіп, өйткені Port 80 жай пайдаланылады. Егер сіз белгілі бір файлды сұратуға және қайтару нәрсе алуға көріңіз веб-серверге жолды жібере болса, сіз өте қарапайым веб-браузерді еді. Әрине, жол толық HTTP команданы болуға тиіс. Бұл біз тыңдап, басқа компьютерлер жалғанатынын тамаша, сондай-ақ жіберуге және алуға мүмкіндік байланысын қамтиды.

    Жіберу және алу

    Диспетчерлік жүзеге асырылады, ыңғайлы жеткілікті, жіберуге кезінде () функциясы:

    int send(
    
      SOCKET s,
    
      const char * FAR buf,
    
      int len,
    
      int flags
    
    );
    
    

    Жалпы алғанда, сіз буфер келсе бәрін көшіруге еді, және басқа да соңына дейін деректерді жіберу үшін қосылған розеткаға бойынша жіберу () функциясын қолданыңыз:

    char buffer[256];              // Декларирование буфера в стеке
    
    char *buffer = new char[256];  // или в куче
    
    
    ZeroMemory(buffer, 256);
    
    strcpy(buffer, "Pretend this is important data.");
    
    
    nret = send(theSocket,
    
                buffer,
    
                strlen(buffer),    // Заметим, что это указывает длину строки; не
    
                                   // размер всего буфера
    
                0);                // В большинстве случаев равна нулю, но смотрите MSDN для получения дополнительных параметров
    
    
    delete [] buffer;              // Тогда и только тогда, когда была использована декларация динамической памяти
    
    
    if (nret == SOCKET_ERROR)
    
    {
    
            // Get a specific code
    
            // Handle accordingly
    
            return NETWORK_ERROR;
    
    } else {
    
            // nret содержит кол-во отправленных байт
    
    }
    
    
    Прием это тот же процесс, наоборот:
    
    
    
    char buffer[256];              // На стеке
    
    char *buffer = new char[256];  // или в динамической памяти
    
    
    nret = recv(theSocket,
    
                buffer,
    
                256,               // Полный размер буфера
    
                0);
    
    
    delete [] buffer;              // Манипулирование буфером, а затем удалите, если и только если
    
                                   // буфер выделяется на динамической памяти
    
    
    if (nret == SOCKET_ERROR)
    
    {
    
            // Получите специальный код
    
            // Обращайтесь соответственно
    
            return NETWORK_ERROR;
    
    } else {
    
            // nret contains the number of bytes received
    
    }

    «(Қабылдау) жіберу / қабылдау» дейді Microsoft Outlook бағдарламасында құралдар тақтасындағы батырмасы бар екенін атап өту қызықты қандай болып табылады. Содан кейін, «алу» көп рет) ғана батырмасы дұрыс көрінеді, немесе ол программист кіріс қабылдау (бір әдеті екеніне көз жеткізу үшін «қабылдау» дейін төмендетілді? (Тараптардың шағын әңгіме қайтадан, жақсы) өз теориясын қыршын қалыптастырады.

    өз Winsock бағдарламасын жазу кезінде Мен сәл проблемаларымен бетпе онда сол. Тек қабылдау () пайдалана отырып, сіз алуға болады дәл қанша деректер білесіз кезде керемет (бірінші байт командасы болуы мүмкін ойында, мысалы, және келесі байт, т.б. параметр болып табылады), бірақ сен не білмейді, Егер сіз жасаймыз? деректер жолдың (Java клиенттер C-серверлер сөйлесіп ортақ проблема) арқылы тоқтатылуы алуға деген болса, онда сіз бұрын барлық таңбаны басып алу үшін Readline () функциясын жаза алады. Міне, мен пайдаланылған қандай:

    char * readLine()
    
    {
    
       vector theVector;
    
       char buffer;
    
       int bytesReceived;
    
    
       while (true)
    
       {
    
          bytesReceived = recv(theSocket, &buffer, 1, 0);
    
          if (bytesReceived <= 0)
    
             return NULL;
    
    
          if (buffer == '\n')
    
          {
    
             char *pChar = new char[theVector.size() + 1];
    
             memset(pChar, 0, theVector.size() + 1);
    
    
             for (int f = 0; f < theVector.size(); f++)
    
                pChar[f] = theVector[f];
    
    
             return pChar;
    
          } else {
    
             theVector.push_back(buffer);
    
          }
    
       }
    
    }

    Оны сақтау ғарыш желісі ұзындығы байланысты, автоматты түрде ұлғайтылуы мүмкін вектор, орнына алаптың пайдаланылады. қабылдау () (алынған байт көрсетілген нөлден аз) қатені қайтарады болса, жым-жылас қайтарылады. Осы мүмкіндік болғандықтан, проблема функциясы жол пайдалануға Readline () функцияларын оралды қамтамасыз етуге тиіс. цикл ішіндегі бір сипаты жоқ Қызыл жол векторына қосылады, егер желі алынған, және. Осы жолдың сипаты болса, векторының мазмұны C көшіріледі, және жолды қайтарады. жол векторының және memset (артық бір сипаты) жарияланды – iruetsya және қайтару желісі автоматты түрде жойылады, сондықтан нөлге ұмтылады. нөл мәні бар сызық соңы әдетте, жақсы тәжірибе болып табылады бағдарламалау, ерекше қателерді болдырмайды және.

    Сондай-ақ, қайтару (бос салмаңыз) және оңай рәміздер өзгерту қабілеті қолдауымен осы Бойко жақсарды нұсқасы:

    // Код первоначально написан Нором. Модифицированный немного для
    
    // поддержки MessageBox() API, сделать более читабельной логику,
    
    // выровнять расстояние, и добавлять комментарии. Опубликовано с разрешения.
    
    
    #define backKey '\b'                                 // Чтобы отключить backspace-ы, #define backKey NULL
    
    #define newLine '\n'
    
    #define endStr  '\0'
    
    
    char *readLine(SOCKET s)
    
    {
    
            vector theVector;
    
            char buffer;
    
            char *pChar;
    
            int bytesReceived;
    
    
            while (true)
    
            {
    
                   bytesReceived = recv(s, &buffer, 1, 0);
    
    
                   if (bytesReceived <= 0)
    
                   {
    
                           MessageBox(NULL, "recv() returned nothing.", "socketIndication", MB_OK);
    
                           return NULL;
    
                   }
    
    
    
                   switch (buffer)
    
                   {
    
                           case backKey:                  // регулировка backspace
    
                                   if (theVector.size() > 0)
    
                                          theVector.pop_back();
    
                                   break;
    
                           case endStr:                   // Если конец строкового символьного операнда достигнут,
    
                           case newLine:                  // или если конец линейного символьного операнда достигнут,
    
                                   pChar = new char[theVector.size() + 1];
    
                                   memset(pChar, 0, theVector.size() + 1);
    
    
                                   for (int f = 0; f < theVector.size(); f++)
    
                                          pChar[f] = theVector[f];
    
                                   return pChar;
    
                                   break;
    
                           default:                       // любой обычный операнд
    
                                   theVector.push_back(buffer);
    
                                   break;
    
                   }
    
            }
    
    }

    Non-блоктау және асинхронды Sockets

    Пайдаланушы қосатын дейін біз розеткалар бұғаттау туралы айтқан Осы уақытқа дейін мұндай қабылдауға () ретінде, онда функциясы қоңырау белгісіз уақытқа күтеді. ол ештеңе істеу айтты емес, кез келген уақытта розетка (кейінірек үшін бір нәрсе болады деп көрсетіп) табысты нәтижесінде немесе қате немесе ештеңе, не дереу қайтарады емес бұғаттау. осы түрін қолдану кемшілігі қолмен нәтижесі сіз қоңырау әрбір функциясы алынған көру үшін, егер сокет сұрауға мәжбүр болады. Сіз оқу, жазу немесе қайтарылған қате дайын қайсысын көру үшін, () таңдаңыз функциясы үшін қосқыштарының жиынтығын өте алады.

    асинхронды розеткалар пайдалану және дереу оралды, бірақ сіз белгілі бір оқиға орын алды, қашан сендердің терезенің рәсімінің жіберу үшін хабарлама көрсетуге болады ерекшеліктері. Мысалы, сіз розетка SOCKET_GOTMSG ол нәрсе өзгерткенде хабарлама жіберген етіп істеуге болады. Әдетте бұл сіз кейінірек қажетсіз проблемалар тудырады болдырмау үшін розеткаға хабар алуым, (ауқымдылығы, бірақ қажетті) қателерді тексеру үшін өте ақылды екен. Біріншіден, біз ның асинхронды сокет жасау үшін пайдаланылатын кейбір функцияларды анықтау көрейік

    INT WSAAsyncSelect (Socket с, HWND HWND, INT wMsg, ұзақ Левент пайдаланып)
    Бұл функция оған асинхронды хабарламада және буыны ретінде розетканы анықтау үшін пайдаланылады. Бұл розетка, сіз жұмыс істеп жатқан кіммен. HWND сокет оқиғаны пайда болған кезде хабарлама аласыз терезенің дескриптор болып табылады. wMsg сіз (мысал жоғарыдан SOCKET_GOTMSG розетка байланысы болып табылады) Егер терезенің рәсімінің жібергіңіз келетін хабарды болып табылады. IEvent параметр сокет көрсетеді және хабар жіберу үшін қандай жағдайларда бір немесе бірнеше жалаулар алады. осы жалаудың Кейбір:

  • FD_READ: Розетка негізгі деректер алуға дайын
  • FD_WRITE: Розетка негізгі деректер жіберуге дайын
  • FD_ACCEPT: серверлерінде пайдаланылатын, осы хабарлама пайдаланушы байланысты екенін білдіреді
  • FD_CONNECT: клиент қосымшаларда қолданылады, бұл хабар розетка қосылған хабарлайды
  • FD_CLOSE: Socket ғана жабылды
  • WSAGETSELECTERROR (LPARAM LPARAM)
    Розетка қате қайтарылады, егер Ол анықтайды. Техникалық, ол жұмыс істемейді, және макро (сіз үшін шын мәнінде осы шағын фактісі тараптардың шағын әңгіме жасай аласыз).
  • WSAGETSELECTEVENT (LPARAM LPARAM)
    winsock2.h анықталған тағы бір пайдалы макрос розетка жасалған нақ көруге пайдаланылады WSAGETSELECTEVENT () болып табылады.
    Осылайша, асинхронды розетканы орнату көрейік:

    // Мы начнем с создания флага, который будет использовать ОС Windows чтоб обращаться к нам, когда что-то случается
    
    #define THERE_WAS_A_SOCKET_EVENT      WM_USER + 100  // WM_USER это база для пользовательских сообщений

     

    // Где-то в нашем коде инициализации после CreateWindow (), вызываем WSAAsyncSelect ()
    
    WSAAsyncSelect ( theSocket, hwnd, THERE_WAS_A_SOCKET_EVENT, FD_READ | FD_WRITE | FD_CONNECT | ... );
    
    
    // Это означает: Windows, пожалуйста, свяжитесь со мной, используя THERE_WAS_A_SOCKET_EVENT флаг который я
    
    // определил ранее, в момент когда есть данные для чтения (FD_READ), или когда я свободен для передачи данных
    
    // (FD_WRITE), или когда я успешно подключен к кому-то еще (FD_CONNECT), или когда...итд.

     

     

     

    // В нашей процедуре окна (функция которая обрабатывает все сообщения, которые посылает Windows для вашего приложения)
    
    LRESULT WINAPI TheWindowProcedure ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
    
    {

     

            switch ( msg )
    
            {
    
    
                   case THERE_WAS_A_SOCKET_EVENT:
    
                           if ( WSAGETSELECTERROR ( lParam ) )
    
                           {       // если обнаружилась ошибка,
    
                                   закройте сокет ( theSocket );
    
                                   WSACleanup ();                        // Закройте Winsock
    
                                   return NETWORK_ERROR;
    
                           }
    
                           switch ( WSAGETSELECTEVENT ( lParam ) )
    
                           {       // что конкретно случилось?
    
                                   case FD_READ:
    
                                          // примите разрыв
    
                                          данных;
    
                                   case FD_WRITE:
    
                                          // пропишите
    
                                          разрыв данных;
    
                                   case FD_CONNECT:
    
                                          // просто подсоединитесь к
    
                                          Серверному разрыву;
    
                                   case ...                              // Те же установки для других флагов
    
                                          и разрывов;
    
                           }
    
                           break;
    
    
    
                   // другие заявления случаев с логикой, которая обрабатывает другие сообщения Windows
    
            }
    
    }

     

    Сіз әрбір оқиғаға арналған ұя _GOT MSG FD_READ ретінде әрбір іс-шара үшін бір хабар анықтау мүмкін емес екенін ескеріңіз, содан кейін FD_CONNECT үшін SOCKET_CONNECT. Бұл (WSAAsyncSelect өйткені қайталап қоңыраулар табылады) әрбір туын орнату үшін іс-қимыл WSAAsyncSelect () соңғы қоңырау болдырмайды.

    Толығырақ оқу құралдары және сілтемелер

    Мен 2000 жылдың желтоқсан айында осы оқулық жазған, содан кейін қонақтар мен жақсартулар тұрақты ағынын көрген сәттен бастап жеті немесе сол жыл. Мен жаза ұнады ретінде сіз оны оқып ләззат үміттенеміз: Johnny Оқулық Winsock пайдалану үшін рахмет. Жоғарыда сіз Winsock арқылы қол жеткізуге болады мүмкіндіктердің қысқаша шолу болып табылады, және т.б. мәселе ерекшелігі туралы ойша бұл Мен болған әлдеқайда жақсы жасадық:

    (Қосымша ақпарат алу үшін кітаптың мұқабасындағы курсорды қойыңыз.)

                          

     

  • Winsock арқылы программистер үшін FAQ
  • C ++ тілінде MadWizard Winsock желілік Оқулық (компиляция, сондай-ақ қол жетімді)
  • Онлайн ойын бағдарламалау Series 7 бөліктері flipcode ұсындыМен «желілік бағдарламалау ол қарағанда оңай болып көрінеді.» Деп, Томас Bleeker (MadWizard) келісесіз Мен отладчика бірге, ол, сендерге осы мүмкіндіктердің пайдалану маңыздылығын әсер мүмкін емес, сондықтан сіз не болып жатқанын көруге болады. Сіз сіз бұл дұрыс емес, егер сайып келгенде, сіз оны дұрыс неге білуге, заттар қалай жұмыс әлдеқайда жақсы идея бар, содан кейін оң оны алу ләззат болады. басқа сөзбен айтқанда, қателік жасаудан, зерттеу жолы.Қалай жақсартуға болады?

    түсіндіруге қажет нәрсе бар? Сіз білесіз келеді факт Winsock жылы тақырыптар ма оқулық қамту, құлап? Бұл оқулық бағдарламалық қамтамасыз ету әзірлеуші ​​ретінде сіздің қажеттіліктеріне пайдалы болды? Бұл қызық? Бойко жазбаша? тым оңайлатылған? немесе жай ғана дұрыс?

    ҮШІН КӨР http://johnnie.jerrata.com/winsocktutorial/#sthash.bKpVO7OC.dpuf

     

Leave a Reply

Your email address will not be published. Required fields are marked *