Toon posts:

[C] pthreads + socket close()

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik ben het volgende aan het proberen te maken:
Een server die bij het starten een socket() maakt, bind() en laat listen()-en en daarna een thread start die in een oneindige loop nieuwe connections accept() en voor elke connection een thread maakt om te recv()-en. Ik probeer dit zo portable mogelijk te houden dus daarom gebruik ik pthreads.

Omdat ik natuurlijk alles netjes wil opruimen loop ik tegen het volgende aan. accept() kan de thread blocken op het moment dat ik de server wil stoppen (hoogstwaarschijnlijk eigenlijk). Om dit op te lossen dacht ik de socket gewoon te close()-en waardoor de accept() een error geeft en de thread unblockt. Onder windows werkt dit wel, onder freebsd echter niet.. (ik zie nooit de printf("quitting accept-thread\n"))

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
 int *sck_Listen;
 pthread_t thid_Listen;

void *accept_connections(void *ptr)
/*
 * Continue to accept incoming connections
 */
{
        pthread_t tid;
        int *new_fd;
        int sin_size;
        struct sockaddr_in their_addr;

        new_fd = (int *)malloc(sizeof(int));
        sin_size = sizeof(struct sockaddr_in);

        for(;;) /* keep trying to make new connections forever */
        {

                if((*new_fd = accept(*sck_Listen, (struct sockaddr *)&their_addr, &sin_size)) == -1)     /* block thread until new connection is made */
                {
                 printf("quitting accept-thread\n");
                 free(new_fd);
                 new_fd = NULL;
                 pthread_exit(0);
                }
                printf("passed accept()\n");
                if(pthread_create(&tid,NULL,connection,new_fd) != 0);    /* create new thread to handle the new connection */
        }

}


int start_server(int port)
{

        struct sockaddr_in server_addr;

        #ifdef _WIN32                                   /* WSAStartup is required on win32-systems */
                WSADATA wsaData;
                if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) return 1;
        #endif

        sck_Listen = (int *)malloc(sizeof(int));
        *sck_Listen = socket(PF_INET, SOCK_STREAM, 0);   /* create new streaming socket; auto-detect protocol */

        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(port);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);        /* auto-detect local ip-address */
        memset(&(server_addr.sin_zero), '\0', 8);       /* zero the rest of the struct */

        if(bind(*sck_Listen, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) != 0) return 1;   /* Bind socket */

        if(listen(*sck_Listen,BACKLOG) != 0) return 1;   /* start listening on socket */

        if(pthread_create(&thid_Listen,NULL,accept_connections,sck_Listen) != 0) return 1;       /* create thread to accept new connections */

    return 0;   /* server is started */
}

int main(int argc, char* argv[])
{
        if(start_server(80) != 0) printf("Error Starting Server\n");

        printf("Press Any Key...\n");

        while(!fgetc(stdin));

        #ifdef _WIN32
             closesocket(*sck_Listen);
    #else
        if(close(*sck_Listen) != 0) printf("Error Closing socket\n");
    #endif

        pthread_join(thid_Listen,NULL);

        free(sck_Listen);
        sck_Listen = NULL;

        while(!fgetc(stdin));

        return 0;
}


Een ander probleem is dat ik weet dat het geen goede oplossing is om te controleren op een return van -1 van accept() om vervolgens alles op te ruimen en te stoppen. Beter zou zijn om errno te controleren maar waar ik mee zit is: is dat thread-safe? (met andere woorden: kan het niet gebeuren dat door een functie ergens anders in het programma errno gezet wordt voordat ik deze kan controleren?)

raar is overigens dat wanneer ik onder freebsd het programma run deze ook zonder errors gewoon afsluit (hij komt dus voorbij de join die normaal gesproken moet blocken tot de andere thread stopt)

  • neh
  • Registratie: Juni 2001
  • Laatst online: 06-04 22:48

neh

Probeer eens een fflush van stdout na de printf voor de zekerheid.

XT, 640K ram, 20 MB harddisk, MS-DOS 4.0...


Verwijderd

Topicstarter
zoveel zitten zoeken, is het gewoon weer een domme fout (zoals altijd :P)

ik las de output van mijn prog niet goed, er stond namelijk: "Error Starting Server" en dat is ook redelijk logisch als je poort 80 probeerd te binden als niet-root :(

na de poort veranderd te hebben ik 50001 werkt het dus wel.

Weet iemand toevallig hoe het dan nog zit met die errno

[ Voor 11% gewijzigd door Verwijderd op 09-03-2006 19:27 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 05-04 18:00
errno is thread-lokaal (via een rare macro in errno.h, maar daar hoef je je niet van bewust te zijn). Als in de ene thread naar errno geschreven wordt, merk je daar in de andere thread dus niets van. Er is dus geen race condition.

Verder is de socket sluiten en vrij gebruikelijke manier om de accept-lus te doorbreken. Echt netjes is het misschien niet, maar het werkt. Je kunt alternatief ook de main thread een signal te sturen, wat accept-call ook onderbreekt, maar dat zal onder Windows wel niet werken (POSIX compliant my ass).