~iaz/+junk/book-GNP

« back to all changes in this revision

Viewing changes to examples/selectserver.c

  • Committer: ivan.a.zorin at gmail
  • Date: 2011-08-20 10:58:21 UTC
  • Revision ID: ivan.a.zorin@gmail.com-20110820105821-qf3pzwgifu2b6yih
book source code examples: Guide to Network Programming

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
** selectserver.c -- a cheezy multiperson chat server
 
3
*/
 
4
 
 
5
#include <stdio.h>
 
6
#include <stdlib.h>
 
7
#include <string.h>
 
8
#include <unistd.h>
 
9
#include <sys/types.h>
 
10
#include <sys/socket.h>
 
11
#include <netinet/in.h>
 
12
#include <arpa/inet.h>
 
13
#include <netdb.h>
 
14
 
 
15
#define PORT "9034"   // port we're listening on
 
16
 
 
17
// get sockaddr, IPv4 or IPv6:
 
18
void *get_in_addr(struct sockaddr *sa)
 
19
{
 
20
        if (sa->sa_family == AF_INET) {
 
21
                return &(((struct sockaddr_in*)sa)->sin_addr);
 
22
        }
 
23
 
 
24
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
 
25
}
 
26
 
 
27
int main(void)
 
28
{
 
29
    fd_set master;    // master file descriptor list
 
30
    fd_set read_fds;  // temp file descriptor list for select()
 
31
    int fdmax;        // maximum file descriptor number
 
32
 
 
33
    int listener;     // listening socket descriptor
 
34
    int newfd;        // newly accept()ed socket descriptor
 
35
    struct sockaddr_storage remoteaddr; // client address
 
36
    socklen_t addrlen;
 
37
 
 
38
    char buf[256];    // buffer for client data
 
39
    int nbytes;
 
40
 
 
41
        char remoteIP[INET6_ADDRSTRLEN];
 
42
 
 
43
    int yes=1;        // for setsockopt() SO_REUSEADDR, below
 
44
    int i, j, rv;
 
45
 
 
46
        struct addrinfo hints, *ai, *p;
 
47
 
 
48
    FD_ZERO(&master);    // clear the master and temp sets
 
49
    FD_ZERO(&read_fds);
 
50
 
 
51
        // get us a socket and bind it
 
52
        memset(&hints, 0, sizeof hints);
 
53
        hints.ai_family = AF_UNSPEC;
 
54
        hints.ai_socktype = SOCK_STREAM;
 
55
        hints.ai_flags = AI_PASSIVE;
 
56
        if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
 
57
                fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
 
58
                exit(1);
 
59
        }
 
60
        
 
61
        for(p = ai; p != NULL; p = p->ai_next) {
 
62
        listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
 
63
                if (listener < 0) { 
 
64
                        continue;
 
65
                }
 
66
                
 
67
                // lose the pesky "address already in use" error message
 
68
                setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
 
69
 
 
70
                if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
 
71
                        close(listener);
 
72
                        continue;
 
73
                }
 
74
 
 
75
                break;
 
76
        }
 
77
 
 
78
        // if we got here, it means we didn't get bound
 
79
        if (p == NULL) {
 
80
                fprintf(stderr, "selectserver: failed to bind\n");
 
81
                exit(2);
 
82
        }
 
83
 
 
84
        freeaddrinfo(ai); // all done with this
 
85
 
 
86
    // listen
 
87
    if (listen(listener, 10) == -1) {
 
88
        perror("listen");
 
89
        exit(3);
 
90
    }
 
91
 
 
92
    // add the listener to the master set
 
93
    FD_SET(listener, &master);
 
94
 
 
95
    // keep track of the biggest file descriptor
 
96
    fdmax = listener; // so far, it's this one
 
97
 
 
98
    // main loop
 
99
    for(;;) {
 
100
        read_fds = master; // copy it
 
101
        if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
 
102
            perror("select");
 
103
            exit(4);
 
104
        }
 
105
 
 
106
        // run through the existing connections looking for data to read
 
107
        for(i = 0; i <= fdmax; i++) {
 
108
            if (FD_ISSET(i, &read_fds)) { // we got one!!
 
109
                if (i == listener) {
 
110
                    // handle new connections
 
111
                    addrlen = sizeof remoteaddr;
 
112
                                        newfd = accept(listener,
 
113
                                                (struct sockaddr *)&remoteaddr,
 
114
                                                &addrlen);
 
115
 
 
116
                                        if (newfd == -1) {
 
117
                        perror("accept");
 
118
                    } else {
 
119
                        FD_SET(newfd, &master); // add to master set
 
120
                        if (newfd > fdmax) {    // keep track of the max
 
121
                            fdmax = newfd;
 
122
                        }
 
123
                        printf("selectserver: new connection from %s on "
 
124
                            "socket %d\n",
 
125
                                                        inet_ntop(remoteaddr.ss_family,
 
126
                                                                get_in_addr((struct sockaddr*)&remoteaddr),
 
127
                                                                remoteIP, INET6_ADDRSTRLEN),
 
128
                                                        newfd);
 
129
                    }
 
130
                } else {
 
131
                    // handle data from a client
 
132
                    if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
 
133
                        // got error or connection closed by client
 
134
                        if (nbytes == 0) {
 
135
                            // connection closed
 
136
                            printf("selectserver: socket %d hung up\n", i);
 
137
                        } else {
 
138
                            perror("recv");
 
139
                        }
 
140
                        close(i); // bye!
 
141
                        FD_CLR(i, &master); // remove from master set
 
142
                    } else {
 
143
                        // we got some data from a client
 
144
                        for(j = 0; j <= fdmax; j++) {
 
145
                            // send to everyone!
 
146
                            if (FD_ISSET(j, &master)) {
 
147
                                // except the listener and ourselves
 
148
                                if (j != listener && j != i) {
 
149
                                    if (send(j, buf, nbytes, 0) == -1) {
 
150
                                        perror("send");
 
151
                                    }
 
152
                                }
 
153
                            }
 
154
                        }
 
155
                    }
 
156
                } // END handle data from client
 
157
            } // END got new incoming connection
 
158
        } // END looping through file descriptors
 
159
    } // END for(;;)--and you thought it would never end!
 
160
    
 
161
    return 0;
 
162
}
 
163