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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
|
/*
** server.c -- a stream socket server demo
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "protocol.h"
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[])
{
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR, below
int i, j, rv;
struct addrinfo hints, *ai, *p;
// check and assign arguments
if (argc < 3) {
fprintf(stderr,"usage: <listen port> <file0> [file 1] [file 2] ... \n");
exit(1);
}
const char* port = argv[1];
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// LISTENER: get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, port, &hints, &ai)) != 0) {
fprintf(stderr, "snowcast_server: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "snowcast_server: failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
while(1==1) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1) {
perror("accept");
} else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
printf("selectserver: new connection from %s on "
"socket %d\n.",
inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),
newfd);
// send the welcome message to client
struct Welcome welcome;
welcome.replyType = 2;
welcome.numStations = htons(argc - 2);
if ((send(newfd, &welcome, sizeof(struct Welcome), 0)) == -1)
perror("send");
}
} else {
// handle data from a client
struct Command command;
if ((nbytes = recv(i, (char*)&command, sizeof(struct Command), 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
} else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
} else {
// we got some data from a client
if (command.commandType == 0) {
// hello message with udpPort
printf("udpPort (from Hello) for new connection is %d.\n", ntohs(command.number));
// TALKER: get us a udp socket and bind it
struct addrinfo hintsUdp, *servinfoUdp, *pUdp;
int rvUdp, sockfdUdp, numbytesUdp;
memset(&hintsUdp, 0, sizeof hintsUdp);
hintsUdp.ai_family = AF_INET; // IPv4
hintsUdp.ai_socktype = SOCK_DGRAM; // UDP
if ((rvUdp = getaddrinfo(argv[1], command.number, &hints, &servinfoUdp)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rvUdp));
return 1;
}
// loop through all the results and make a socket
for(p = servinfoUdp; p != NULL; p = p->ai_next) {
if ((sockfdUdp = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to create socket\n");
return 2;
}
if ((numbytesUdp = sendto(sockfdUdp, "test", strlen("test"), 0,
p->ai_addr, p->ai_addrlen)) == -1) {
perror("talker: sendto");
exit(1);
}
freeaddrinfo(servinfoUdp);
printf("talker: sent %d bytes to %d\n", numbytesUdp, sockfdUdp);
// close(sockfdUdp);
}
if (command.commandType == 1) {
// setStation command for the user
printf("TODO: set station to %d\n", ntohs(command.number));
}
else {
// send back in invalid command
struct InvalidCommand invalid;
invalid.replyType = 4;
invalid.replyStringSize = 21;
// make a string with the command.commmandType type in it
invalid.replyString = "Invalid command type";
if ((send(i, &invalid, sizeof(struct InvalidCommand), 0)) == -1)
perror("send");
// drop connection upon invalid command
close(i);
FD_CLR(i, &master);
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
// broadcast the new files over the udp socket list for each use
} // END for(;;)--and you thought it would never end!
return 0;
}
|