c - Sockets Blocking :( Hvordan går jeg ud?

Indlæg af Hanne Mølgaard Plasc

Problem



Her er server kode jeg tog fra microsoft. Nedenfor er det min vigtigste, der skal køre void important\_code(bool);. Jeg har altid haft dette problem, når jeg bruger rør og stikkontakter på både linux og windows. [14]


Hvordan forlader jeg select(), når jeg vil stoppe min app? Antag, at important\_code altid udføres på samme tråd efter stikkontakten. Hvordan ville jeg gøre det? [15]


Jeg ved, at dette er Windows-kode, men jeg får også dette problem under Linux


bonus test kode: Hvis du kommenterer main2 () i min hovedfunktion og uncomment sløjfen kan du afslutte rent med ctrl + c. Med stikkontakt forhindrer blokering select mig fra at gøre det. Hvordan løser jeg dette?


#pragma comment(lib, "Ws2\_32.lib")
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>

#define DEFAULT\_FAMILY  AF\_UNSPEC
#define DEFAULT\_SOCKTYPE SOCK\_STREAM
#define DEFAULT\_PORT  "1234"
#define BUFFER\_SIZE   23    // length of "WinCE Echo Test Packet"

void Print(TCHAR *pFormat, ...)
{
    va\_list ArgList;
    TCHAR Buffer[256];
    va\_start (ArgList, pFormat);
    (void)StringCchPrintf(Buffer, 256, pFormat, ArgList);

#ifndef UNDER\_CE
    \_putts(Buffer);
#else
    printf("\%s",Buffer);
#endif

    va\_end(ArgList);
}

int main2 ()
{
    SOCKET sock, SockServ[FD\_SETSIZE];
    int nFamily = DEFAULT\_FAMILY;
    int nSockType = DEFAULT\_SOCKTYPE;
    char *szPort = DEFAULT\_PORT;
    SOCKADDR\_STORAGE ssRemoteAddr;
    int i, nNumSocks, cbRemoteAddrSize, cbXfer, cbTotalRecvd;
    WSADATA wsaData;
    ADDRINFO Hints, *AddrInfo = NULL, *AI;
    fd\_set fdSockSet;
    char pBuf[BUFFER\_SIZE];
    char szRemoteAddrString[128];

    if(WSAStartup(MAKEWORD(2,2), &wsaData))
    {
        // WSAStartup failed
        return 1;
    }

    sock = INVALID\_SOCKET;

    for(i = 0; i < FD\_SETSIZE; i++)
        SockServ[i] = INVALID\_SOCKET;

    //
    // Get a list of available addresses to serve on
    //

    memset(&Hints, 0, sizeof(Hints));
    Hints.ai\_family = nFamily;
    Hints.ai\_socktype = nSockType;
    Hints.ai\_flags = AI\_NUMERICHOST | AI\_PASSIVE;

    if(getaddrinfo(NULL, szPort, &Hints, &AddrInfo))
    {
        Print(TEXT("ERROR: getaddrinfo failed with error \%d
"), WSAGetLastError());
        goto Cleanup;
    }

    //
    // Create a list of serving sockets, one for each address
    //

    i = 0;
    for(AI = AddrInfo; AI != NULL; AI = AI->ai\_next) 
    {
        if (i == FD\_SETSIZE) 
        {
            // getaddrinfo returned more addresses than we could use
            break;
        }

        if((AI->ai\_family == PF\_INET) || (AI->ai\_family == PF\_INET6)) // only want PF\_INET or PF\_INET6
        {
            SockServ[i] = socket(AI->ai\_family, AI->ai\_socktype, AI->ai\_protocol);
            if (SockServ[i] != INVALID\_SOCKET)
            {
                if (bind(SockServ[i], AI->ai\_addr, AI->ai\_addrlen) == SOCKET\_ERROR)
                    closesocket(SockServ[i]);
                else 
                {
                    if(nSockType == SOCK\_STREAM)
                    {
                        if (listen(SockServ[i], 5) == SOCKET\_ERROR)
                        {
                            closesocket(SockServ[i]);
                            continue;
                        }
                    }

                    Print( 
                        TEXT("Socket 0x\%08x ready for connection with \%hs family, \%hs type, on port \%hs
"), 
                        SockServ[i], 
                        (AI->ai\_family == AF\_INET) ? "AF\_INET" : ((AI->ai\_family == AF\_INET6) ? "AF\_INET6" : "UNKNOWN"),
                        (AI->ai\_socktype == SOCK\_STREAM) ? "TCP" : ((AI->ai\_socktype == SOCK\_DGRAM) ? "UDP" : "UNKNOWN"),
                        szPort);
                    i++;
                }
            }
        }
    }

    freeaddrinfo(AddrInfo);

    if (i == 0) 
    {
        Print(TEXT("ERROR: Unable to serve on any address. Error = \%d
"), WSAGetLastError());
        goto Cleanup;
    }

    //
    // Wait for incomming data/connections
    //

    nNumSocks = i;

    FD\_ZERO(&fdSockSet);

    for (i = 0; i < nNumSocks; i++)    // want to check all available sockets
        FD\_SET(SockServ[i], &fdSockSet);

    if (select(nNumSocks, &fdSockSet, 0, 0, NULL) == SOCKET\_ERROR)
    {
        Print(TEXT("ERROR: select() failed with error = \%d
"), WSAGetLastError());
        goto Cleanup;
    }

    for (i = 0; i < nNumSocks; i++)    // check which socket is ready to process
    {
        if (FD\_ISSET(SockServ[i], &fdSockSet))    // proceed for connected socket
        {
            FD\_CLR(SockServ[i], &fdSockSet);
            if(nSockType == SOCK\_STREAM)
            {
                cbRemoteAddrSize = sizeof(ssRemoteAddr);
                sock = accept(SockServ[i], (SOCKADDR*)&ssRemoteAddr, &cbRemoteAddrSize);
                if(sock == INVALID\_SOCKET) 
                {
                    Print(TEXT("ERROR: accept() failed with error = \%d
"), WSAGetLastError());
                    goto Cleanup;
                }

                Print(TEXT("Accepted TCP connection from socket 0x\%08x
"), sock);
            }
            else
            {
                sock = SockServ[i];
                Print(TEXT("UDP data available on socket 0x\%08x
"), sock);
            }
            break;        // Only need one socket
        }
    }

    //
    // Receive data from a client
    //

    cbTotalRecvd = 0;
    do
    {
        cbRemoteAddrSize = sizeof(ssRemoteAddr);
        cbXfer = recvfrom(sock, pBuf + cbTotalRecvd, sizeof(pBuf) - cbTotalRecvd, 0,
            (SOCKADDR *)&ssRemoteAddr, &cbRemoteAddrSize);
        cbTotalRecvd += cbXfer;
    } while(cbXfer > 0 && cbTotalRecvd < sizeof(pBuf));

    if(cbXfer == SOCKET\_ERROR)
    {
        Print(TEXT("ERROR: Couldn't receive the data! Error = \%d
"), WSAGetLastError());
        goto Cleanup;
    }
    else if(cbXfer == 0)
    {
        Print(TEXT("ERROR: Didn't get all the expected data from the client!
"));
        goto Cleanup;
    }

    if(nSockType == SOCK\_STREAM)
    {
        cbRemoteAddrSize = sizeof(ssRemoteAddr);
        getpeername(sock, (SOCKADDR *)&ssRemoteAddr, &cbRemoteAddrSize);
    }

    if (getnameinfo((SOCKADDR *)&ssRemoteAddr, cbRemoteAddrSize,
        szRemoteAddrString, sizeof(szRemoteAddrString), NULL, 0, NI\_NUMERICHOST) != 0)
        strcpy(szRemoteAddrString, "");

    Print(TEXT("SUCCESS - Received \%d bytes from client \%hs
"), cbTotalRecvd, szRemoteAddrString);

    //
    // Echo the data back to the client
    //

    cbXfer = 0;
    cbXfer = sendto(sock, pBuf, cbTotalRecvd, 0, (SOCKADDR *)&ssRemoteAddr, cbRemoteAddrSize);

    if(cbXfer != cbTotalRecvd)
        Print(TEXT("ERROR: Couldn't send the data! error = \%d
"), WSAGetLastError());
    else
        Print(TEXT("SUCCESS - Echo'd \%d bytes back to the client
"), cbXfer);

Cleanup:

    for(i = 0; i < nNumSocks && SockServ[i] != INVALID\_SOCKET; i++)
        closesocket(SockServ[i]);

    if(sock != INVALID\_SOCKET)
    {
        shutdown(sock, SD\_BOTH);
        closesocket(sock);
    }

    WSACleanup();

    return 0;
}



#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

volatile bool terminate\_app=false;
void terminate (int param)
{
    printf("Terminating program...
");
    terminate\_app=true;
}

void important\_code(bool v)
{
    printf("Important code here \%d
", v);
}

int main ()
{
    try{
        void (*prev\_fn)(int);

        prev\_fn = signal (SIGINT,terminate);

        main2();
        //while(terminate\_app==false)
        {
        }
    }
    catch(...){
        important\_code(1);
    }
    important\_code(0);
    return 0;
}

Bedste reference


På POSIX select() vender tilbage på timeout, eller når en af ​​beskrivelserne er klar, eller muligvis med retur med errno indstillet til EINTR, hvis det afbrydes af et signal.


Så du kunne bruge EINTR adfærd. Installer kun en signalhåndterer med funktionen sigaction() uden SA\_RESTART (genstart systemopkald afbrudt af signaler) flag. (Opførelsen af ​​signal() adskiller sig mellem systemer, og på nogle systemer kan den ændres via #defines).


Eller du kunne bruge selvrøret-tricket: skriv til et rør for at signalere at du vil afslutte. Hvis din select() vælger læsedeskriptoren for det pågældende rør, vil den returnere.

Andre referencer 1


Hvorfor gøre tingene så komplicerede med rør? Sikkert udgangen betyder ikke noget, om det tager nogle sekunder. Bare sæt en tid ud af (sig) 3 sekunder og kontroller, om en udgang er påkrævet efter at have lavet select.


Vil det være noget, hvis det tager et par sekunder at starte afslutningen?