c ++ - Hvorfor sender datagram ikke, hvis jeg først opretter en TCP-forbindelse?

Indlæg af Hanne Mølgaard Plasc

Problem



Følgende c ++ program skal konvertere hver linje til store bogstaver ved hjælp af socket datagram til at kommunikere mellem to tråde.


Example:
Hello World!<return>
HELLO WORLD!
123abc!<return>
123ABC!
<return>
<end program>


Programmet som skrevet virker for mig, men hvis jeg kommenterer bugfix() funktionsopkaldet i hovedet, venter programmet på ubestemt tid efter den første linje af input.


Example:
Hello World!<return>
<the program wait indefinitely>


Dette sker i Windows 7 med den sidste opdatering den 10/04/2011 ved hjælp af den sidste MinGW32.


#include <iostream>
#include <cstdlib>
#include <cctype>
#include <sys/types.h>
#include <winsock.h>
#include <windows.h>
#include <process.h>

using namespace std;

#define CHECK(exp, cond)  do { typeof(exp) \_check\_value\_ = exp; check(\_check\_value\_ cond, \_check\_value\_, \_\_LINE\_\_, #exp #cond); } while(0)

template <class T>
void check(bool ok, T value, int line, const char* text) {
    if (!ok) {
        cerr << "ERROR(" << line << "):" << text << "
Returned: " << value << endl;
        cerr << "errno=" << errno << endl;
        cerr << "WSAGetLastError()=" << WSAGetLastError() << endl;
        exit(EXIT\_FAILURE);
    }
}

#define DATA\_CAPACITY   1000
#define PORT            23584
#define TEST\_IP         "192.0.32.10"
#define MYSELF          "127.0.0.1"
#define DST\_IP          MYSELF

sockaddr\_in address(u\_long ip, u\_short port) {
    sockaddr\_in addr = { };
    addr.sin\_family = AF\_INET;
    addr.sin\_port = port;
    addr.sin\_addr.s\_addr = ip;
    return addr;
}

void \_\_cdecl client\_thread(void* args) {
    SOCKET s = *(SOCKET*)args;

    sockaddr\_in addr = address(inet\_addr(DST\_IP), htons(PORT));

    char data[DATA\_CAPACITY];
    while (1) {
        cin.getline(data, DATA\_CAPACITY);
        int data\_len = strlen(data);

        CHECK(sendto(s, data, data\_len, 0, (sockaddr*)&addr, sizeof addr), >= 0);
        CHECK(recvfrom(s, data, DATA\_CAPACITY, 0, NULL, NULL), >= 0);

        cout << data << endl;

        if (data\_len == 0)
            break;
    }

    CHECK(closesocket(s), == 0);
}

void \_\_cdecl server\_thread(void* args) {
    SOCKET s = *(SOCKET*)args;
    sockaddr\_in addr = address(INADDR\_ANY, htons(PORT));
    int addr\_size = sizeof addr;
    CHECK(bind(s, (sockaddr*)&addr, sizeof addr), != SOCKET\_ERROR);

    char data[DATA\_CAPACITY];
    while (1) {
        int data\_len = recvfrom(s, data, DATA\_CAPACITY, 0, (sockaddr*)&addr, &addr\_size);
        CHECK(data\_len, >= 0);

        for (int i = 0; i < data\_len; i++)
            if (islower(data[i]))
                data[i] = toupper(data[i]);

        CHECK(sendto(s, data, data\_len, 0, (sockaddr*)&addr, addr\_size), >= 0);

        if (data\_len == 0)
            break;
    }

    CHECK(closesocket(s), == 0);
}

// This function create a TCP connection with www.example.com and the close it
void bugfix() {
    SOCKET s = socket(PF\_INET, SOCK\_STREAM, IPPROTO\_TCP);
    sockaddr\_in addr = address(inet\_addr(TEST\_IP), htons(80));
    connect(s, (sockaddr*)&addr, sizeof addr);
    CHECK(closesocket(s), == 0);
}

int main()
{
    cout << "Convert text to uppercase, an empty line terminate the program" << endl;


    WSADATA wsaData;
    CHECK(WSAStartup(MAKEWORD(2, 2), &wsaData), == 0);

    SOCKET client = socket(PF\_INET, SOCK\_DGRAM, IPPROTO\_UDP);
    SOCKET server = socket(PF\_INET, SOCK\_DGRAM, IPPROTO\_UDP);

    CHECK(client, != INVALID\_SOCKET);
    CHECK(server, != INVALID\_SOCKET);

    // if this function is not called the program doesn't work
    bugfix();

    HANDLE hClient = (HANDLE)\_beginthread(client\_thread, 0, &client);
    HANDLE hServer = (HANDLE)\_beginthread(server\_thread, 0, &server);

    HANDLE h[] = { hClient, hServer };

    WaitForMultipleObjects(sizeof h / sizeof *h, h, TRUE, INFINITE);

    CHECK(WSACleanup(), == 0);

    return EXIT\_SUCCESS;
}

Bedste reference


    int data\_len = strlen(data);


Tony Hoare kaldte sin definition af en NULL peger hans milliard dollar fejltagelse. At have strings nul-termineret skal være Dennnis Ritchie s ti milliarder dollar fejl. Tilføj en.


Dit program er ellers en udførlig måde at opdage, at UDP ikke er en pålidelig protokol. Netværksstakken får lov til at gøre UDP-pakker forsvinde eller ombestille dem. Det er okay, så længe der er en anden protokol på toppen af ​​den, der opdager dette, som TCP. Du flyver uden sådanne bandaider, bugfix () er faktisk ikke en løsning.


Brug TCP, send pakkelængden først, så modtageren vil vide, hvor mange byte der følger, så du er immun mod strømadfærd. Men mere til det punkt er udveksling af data mellem tråde gennem en stikkontakt en virkelig dyre måde at undgå at bruge en matrix med en mutex. Tråd har uhindret adgang til hukommelse i processen, du behøver ikke en interprocess kommunikationsmekanisme for at få dem til at udveksle data.

Andre referencer 1


Jeg ser flere problemer lige fra flagermus.


Jeg bruger normalt ikke IPPROTO\_UDP-flag til at oprette stikket. Bare send 0 for protokollparameteren til stikket.


SOCKET client = socket(PF\_INET, SOCK\_DGRAM, 0);
SOCKET server = socket(PF\_INET, SOCK\_DGRAM, 0);


Vigtigere. Du skal kalde 'bind' på klientens stik på samme måde som du gør serverens stik. Hvis du vil have, at operativsystemet skal vælge en tilfældigt tilgængelig port til dig, kan du bruge 0 som portværdi og IPADDR\_ANY til IP-adressen. Hvis du vil vide, hvad operativsystemet vælges som en lokal port til dig, kan du bruge getsockname. Noget som følgende:


void \_\_cdecl client\_thread(void* args) {
    SOCKET s = *(SOCKET*)args;

    sockaddr\_in addr = address(inet\_addr(DST\_IP), htons(PORT));

    sockaddr\_in localAddrBind = address(INADDR\_ANY, 0);
    sockaddr\_in localAddrActual = {};
    int length = sizeof(localAddrActual);
    int bindRet = bind(s, (sockaddr*)&localAddrBind, sizeof(localAddrBind));

    getsockname(s, (sockaddr*)&localAddrActual, &length);
    printf("Listening on port \%d
", ntohs(localAddrActual.sin\_port));