aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsotech117 <michael_foiani@brown.edu>2023-09-15 01:10:57 -0400
committersotech117 <michael_foiani@brown.edu>2023-09-15 01:10:57 -0400
commitcbfafaabbd846e625796f275b2e843376385cc36 (patch)
tree3f3f59aa8ce02e3b11a097d0b6ef651bbb3db2b0
parent003c34172ffa9c8256a3ec4db913c53c825c6c9f (diff)
work on server handling multiple clients with a fd set
-rw-r--r--Makefile2
-rw-r--r--broadcast.c67
-rw-r--r--client.c33
-rw-r--r--protocol.h35
-rw-r--r--server.c2
-rw-r--r--snowcast-server.c0
-rwxr-xr-xsnowcast_controlbin13864 -> 34461 bytes
-rwxr-xr-xsnowcast_serverbin14176 -> 34476 bytes
-rw-r--r--snowcast_server.c195
9 files changed, 324 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 23d8e32..be4e159 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ default: all
all: server client
server: server.c
- $(CC) $(CFLAGS) -o snowcast_server server.c
+ $(CC) $(CFLAGS) -o snowcast_server snowcast_server.c
client: client.c
$(CC) $(CFLAGS) -o snowcast_control client.c \ No newline at end of file
diff --git a/broadcast.c b/broadcast.c
new file mode 100644
index 0000000..b10e633
--- /dev/null
+++ b/broadcast.c
@@ -0,0 +1,67 @@
+/*
+** broadcaster.c -- a datagram "client" like talker.c, except
+** this one can broadcast
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#define SERVERPORT 4950 // the port users will be connecting to
+
+int main(int argc, char *argv[])
+{
+ int sockfd;
+ struct sockaddr_in their_addr; // connector's address information
+ struct hostent *he;
+ int numbytes;
+ int broadcast = 1;
+ //char broadcast = '1'; // if that doesn't work, try this
+
+ if (argc != 3) {
+ fprintf(stderr,"usage: broadcaster hostname message\n");
+ exit(1);
+ }
+
+ if ((he=gethostbyname(argv[1])) == NULL) { // get the host info
+ perror("gethostbyname");
+ exit(1);
+ }
+
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ perror("socket");
+ exit(1);
+ }
+
+ // this call is what allows broadcast packets to be sent:
+ if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast,
+ sizeof broadcast) == -1) {
+ perror("setsockopt (SO_BROADCAST)");
+ exit(1);
+ }
+
+ their_addr.sin_family = AF_INET; // host byte order
+ their_addr.sin_port = htons(SERVERPORT); // short, network byte order
+ their_addr.sin_addr = *((struct in_addr *)he->h_addr);
+ memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
+
+ if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]), 0,
+ (struct sockaddr *)&their_addr, sizeof their_addr)) == -1) {
+ perror("sendto");
+ exit(1);
+ }
+
+ printf("sent %d bytes to %s\n", numbytes,
+ inet_ntoa(their_addr.sin_addr));
+
+ close(sockfd);
+
+ return 0;
+} \ No newline at end of file
diff --git a/client.c b/client.c
index 6458227..49d2cdf 100644
--- a/client.c
+++ b/client.c
@@ -11,6 +11,7 @@
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
+#include <ctype.h>
#include <arpa/inet.h>
@@ -18,6 +19,10 @@
#define MAXDATASIZE 100 // max number of bytes we can get at once
+#define MAX_READ_SIZE 1024
+#define LINE_MAX 1024
+
+
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
@@ -83,7 +88,7 @@ int main(int argc, char *argv[])
struct Welcome msg;
// recv the message, check for errors too
- if ((recvbytes = recv(sockfd, (char*)&msg, sizeof(struct snowcast_message), 0)) == -1) {
+ if ((recvbytes = recv(sockfd, (char*)&msg, sizeof(struct Welcome), 0)) == -1) {
perror("recv");
exit(1);
}
@@ -95,12 +100,34 @@ int main(int argc, char *argv[])
// convert updPort to an int
int udpPortInt = atoi(udpPort);
hello.udpPort = htons(udpPortInt);
-
if ((numbytessent = send(sockfd, &hello, sizeof(struct Hello), 0)) == -1) {
perror("send");
exit(1);
}
- close(sockfd);
+
+ char input[LINE_MAX];
+ printf("Enter a number to change to it's station. Click q to end stream.\n");
+ while (1==1) {
+ char *line = fgets(input, LINE_MAX, stdin);
+
+ if (line == NULL) {
+ continue;
+ } else if (strncmp("q\n", input, LINE_MAX) == 0) {
+ printf("Exiting.\n");
+ break;
+ } else {
+ // convert input to an int
+ int inputInt = (uint16_t)atoi(input);
+
+ struct SetStation setStation;
+ setStation.commandType = 1;
+ setStation.stationNumber = htons(inputInt);
+ if ((numbytessent = send(sockfd, &setStation, sizeof(struct SetStation), 0)) == -1) {
+ perror("send");
+ exit(1);
+ }
+ }
+ }
return 0;
} \ No newline at end of file
diff --git a/protocol.h b/protocol.h
index 0cdc998..b038901 100644
--- a/protocol.h
+++ b/protocol.h
@@ -1,16 +1,39 @@
#include <stdint.h> // Provides uint8_t, int8_t, etc.
-struct snowcast_message {
- uint8_t type;
- uint16_t number;
+// client to server messages (commands)
+
+struct Command {
+ uint8_t commandType;
+ u_int16_t number;
+} __attribute__((packed));
+
+struct Hello {
+ uint8_t commandType;
+ uint16_t udpPort;
+} __attribute__((packed));
+struct SetStation {
+ uint8_t commandType;
+ uint16_t stationNumber;
} __attribute__((packed));
+// server to client message (replies)
struct Welcome {
uint8_t replyType;
uint16_t numStations;
} __attribute__((packed));
-struct Hello {
- uint8_t commandType;
- uint16_t udpPort;
+struct Reply {
+ uint8_t replyType;
+ uint8_t stringSize;
+ char *string;
+} __attribute__((packed));
+struct Announce {
+ uint8_t replyType;
+ uint8_t songnameSize;
+ char *songname;
} __attribute__((packed));
+struct InvalidCommand {
+ uint8_t replyType;
+ uint8_t replyStringSize;
+ char *replyString;
+} __attribute__((packed)); \ No newline at end of file
diff --git a/server.c b/server.c
index 475535c..be9f603 100644
--- a/server.c
+++ b/server.c
@@ -118,6 +118,8 @@ int main(int argc, char *argv[])
printf("server: waiting for connections...\n");
+
+
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
diff --git a/snowcast-server.c b/snowcast-server.c
deleted file mode 100644
index e69de29..0000000
--- a/snowcast-server.c
+++ /dev/null
diff --git a/snowcast_control b/snowcast_control
index 5e208f3..d6aaff0 100755
--- a/snowcast_control
+++ b/snowcast_control
Binary files differ
diff --git a/snowcast_server b/snowcast_server
index 75713b7..1f61e9d 100755
--- a/snowcast_server
+++ b/snowcast_server
Binary files differ
diff --git a/snowcast_server.c b/snowcast_server.c
new file mode 100644
index 0000000..f09bfcc
--- /dev/null
+++ b/snowcast_server.c
@@ -0,0 +1,195 @@
+/*
+** 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);
+
+ // 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, "selectserver: %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 == 1) {
+ // update the station for the user
+ printf("TODO: set station to %d\n", ntohs(command.number));
+ }
+ else {
+ // send back ianinvalid 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");
+ }
+ // for(j = 0; j <= fdmax; j++) {
+ // // send to everyone!
+ // if (FD_ISSET(j, &master)) {
+ // // except the listener and ourselves
+ // if (j != listener && j != i) {
+ // if (send(j, buf, nbytes, 0) == -1) {
+ // perror("send");
+ // }
+ // }
+ // }
+ // }
+ }
+ } // END handle data from client
+ } // END got new incoming connection
+ } // END looping through file descriptors
+ } // END for(;;)--and you thought it would never end!
+
+ return 0;
+} \ No newline at end of file