#include "hw3.h" /******************************************************************************** Server code based on professor Yap and Ting-jen Yen code. *********************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PRINTF //printf // this is the type of the function that is going to implement the protocol typedef int (*CONNECTION_HANDLER)(int); LineFinder *lineFinder = NULL; // the LineFinder. It contains a Country structure int server_sock, con_sock; int srv_port = 50000; struct sockaddr_in source; /* Close all sockets before the forced quit caused by signal */ void signal_server_handler(int n) { close(con_sock); close(server_sock); exit(0); } /* When the state of a child process is changed, (for example,it is terminated, or ended by call exit()) the server should call wait() to get the state. This will prevent the child process from becoming a zombie process when it has finished. */ void child_end(int n) { wait(NULL); } // send a string to a socket already created void sendToSocket(int connectionSocket, char *toSend){ char buffToSend[8192]; sprintf(buffToSend, "%s\r\n", toSend); PRINTF("Sending %s", buffToSend); write(connectionSocket, buffToSend, strlen(buffToSend)); } // send a float to a socket already created void sendToSocket(int connectionSocket, float toSend){ char buffToSend[8192]; sprintf(buffToSend, "%f\r\n", toSend); PRINTF("Sending %s", buffToSend); write(connectionSocket, buffToSend, strlen(buffToSend)); } // reads from a socket and into a buffer - returns number of bytes read. // try to repeat until read something from the client, unless the connection is broken. int receiveSocket(int con_sock, char* buffer, int size){ int lg; do { /* get response from the server. If there is error and it is EBADF, it means the connection might be closed, otherwise, try to read it again until we read it successfully. */ lg = read(con_sock, buffer, size); if (lg == -1 && errno == EBADF){ close(con_sock); return(0); } } while (lg <= 0); return(lg); } /********************************************************************************* This routine determines the bounding box and send it back to the client in the form of 2 points (4 floats). I will always wait for an answer (ACK) unless is the end of the transmission (END). *********************************************************************************/ void sendBoundingBox(int con_sock){ char buff[8192]; float xmin = 10.10; float ymin = 30.30; float xmax = -20.20; float ymax = 40.40; /******* Get the boundaries *******/ Box bounds = lineFinder->getBoundaries(); Point2 pmin, pmax; pmax = bounds.max(); pmin = bounds.min(); xmin = pmin.x; ymin = pmin.y; xmax = pmax.x; ymax = pmax.y; /******* Send the boundaries *******/ sprintf(buff, "%f", xmin); sendToSocket(con_sock, buff); // write everything to the network. receiveSocket(con_sock, buff, 5); // wait for acknowledge sprintf(buff, "%f", ymin); sendToSocket(con_sock, buff); // write everything to the network. receiveSocket(con_sock, buff, 5); // wait for acknowledge sprintf(buff, "%f", xmax); sendToSocket(con_sock, buff); // write everything to the network. receiveSocket(con_sock, buff, 5); // wait for acknowledge sprintf(buff, "%f", ymax); sendToSocket(con_sock, buff); // write everything to the network. receiveSocket(con_sock, buff, 5); // wait for acknowledge sendToSocket(con_sock, "END"); // Sending end of transmission (END) close(con_sock); } /********************************************************************************* This function will take care of sending the lines to the client *********************************************************************************/ void sendLines(int con_sock){ char buff[8192]; int lg, flag, is_float, result_i; char *c, *p; char req[8192]; double result_f; double xmin, ymin, xmax, ymax; // the protocol says, read 4 floats from clients the floats are strings ended in \r\n // these are the corners of the window (xmin, ymin, xmax, ymax) // then, send back all the lines. Starting each line, send the word LINE. // end the transmission with END // First, the server receive the coordinates of the bounding box for(int i = 0; i < 4; i++){ lg = receiveSocket(con_sock, req, 8192); // Check the ending characters of the protocol if (req[lg - 1] != '\n' || req[lg - 2] != '\r') { sendToSocket(con_sock, "Error: Unknown input"); break; } req[lg - 2] = '\0'; // Get rid of the trailing new line character and carriage return character. c = strtok(req, " "); // Get the first part of the line only. (Separated by space.) // Empty line if (c == NULL) { sendToSocket(con_sock, "Error: Unknown input"); break; } // A float-point number result_f = atof(c); switch(i){ case 0: xmin = result_f; break; case 1: ymin = result_f; break; case 2: xmax = result_f; break; case 3: ymax = result_f; break; } // Send some ACKNOWLEDGE sendToSocket(con_sock, "ACK"); } // End receiving coordinates. /******* Set the boundaries to search *******/ Point2 pmin(xmin, ymin); Point2 pmax(xmax, ymax); Box box(pmin, pmax); LineList *ll = lineFinder->findLines(box); // get the lines in the bounding box if (ll == NULL || ll->size() == 0){ sendToSocket(con_sock, "END"); close(con_sock); return; } /******* Send the lines to the client *******/ for (LineList::iterator ii = ll->begin(); ii != ll->end(); ii++) { Line *l = *ii; sendToSocket(con_sock, "LINE"); // starting new line receiveSocket(con_sock, buff, 5); // wait for acknowledgement sprintf(buff,""); // clean the buffer for (Point2List::iterator jj = l->points.begin(); jj != l->points.end(); jj++) { sprintf(buff, "%s %f %f", buff, (*jj).x, (*jj).y); // Put next point on the buffer } sendToSocket(con_sock, buff); // Send this coordinate to the client receiveSocket(con_sock, buff, 5); // wait for acknowledge } sendToSocket(con_sock, "END"); close(con_sock); } /********************************************************************************* The protocol is simple and clear. It could have be even easier but I wanted to make it real clear. The server will always wait for connection. After the connection is established, it will wait for one of two commands: either BOX or LINES. If the server receives LINES, it will expect to receive the the bounding box next (four floats: xmin, ymin, xmax, ymax). The protocol says that after each transmision, wait for an acknowledgement (ACK). The next step will be sending the lines out. The server will send a command LINE before each new line and the command POINT to signal that there is a new point of the line. After the transmission is completed, it sends the command END. The server will always wait for an acknowledge except when sending END. If the server receives BOX, it will transmit to the client the four coordinates of the bounding box. These are the limits of the box. *********************************************************************************/ int handle_req(int con_sock) { int lg; char *c, *p; char req[8192]; // The protocol is modified. We will read from the client what is it looking for // either Box boundaries (BOX) or set of lines (LINES) lg = receiveSocket(con_sock, req, 8192); // Check the ending characters of the protocol if (req[lg - 1] != '\n' || req[lg - 2] != '\r') { sendToSocket(con_sock, "Error: Unknown input"); return(1); } req[lg - 2] = '\0'; // Get rid of the trailing new line character and carriage return character. c = strtok(req, " "); // Get the first part of the line only. (Separated by space.) // Empty line if (c == NULL) { sendToSocket(con_sock, "Error: Unknown input"); return(1); } // if we receive "END" end the loop if (strncmp(req, "BOX", 3) == 0){ // Send some ACKNOWLEDGE sendToSocket(con_sock, "ACK"); // send the bounding box sendBoundingBox(con_sock); } else { // Send some ACKNOWLEDGE sendToSocket(con_sock, "ACK"); // Send the lines sendLines(con_sock); } return(0); } /********************************************************************** This function initialize the server connection. It uses a global variable "srv_port" for the port. **********************************************************************/ void init_servconnection(int paramPort) { struct sockaddr_in server; // STEP (A). Creating socket server_sock = socket(PF_INET, SOCK_STREAM, 0); // it is misconception to use "AF_INET" instead of "PF_INET" if (server_sock < 0) { perror("socket"); exit(1); } server.sin_family = PF_INET; //server.sin_port = htons(srv_port); server.sin_port = htons(paramPort); server.sin_addr.s_addr = INADDR_ANY; // STEP (B). Bind the specified port to this socket if (bind(server_sock, (struct sockaddr *) & server, sizeof(server)) < 0){ perror("bind socket"); exit(1); } // listen for connection */ if (listen(server_sock, 5) < 0) { perror("listen"); exit(1); } } /************************************************************************* This is a general function that will attend any connection and fork a new process to handle it. It receives a function as a parameter. That parameter function is the one that is going to handle the request. I did in this way(as a callback) so I can have any function to handle the request. ***************************************************************************/ //int attenteconnection(void) int attenteconnection(CONNECTION_HANDLER theHandler) { int lg, pid; lg = sizeof(struct sockaddr_in); // repeat until accept a successful connect do con_sock = accept(server_sock, (struct sockaddr *) & source, (socklen_t *) &lg); while (con_sock <= 0); // Fork a child process to serve the client if ((pid = fork()) != 0) { close(con_sock); // parent process can close the connect and quit } else { //handle_req(con_sock); // The child process will handle the client, and the quit when finished. (*theHandler)(con_sock); exit(1); } return 0; } extern int errno; /********************************************************************** This is the Server function per say. It receives the port as parameter. **********************************************************************/ void initServer(int thePort){ signal(SIGCHLD, child_end); signal(SIGTERM, signal_server_handler); signal(SIGQUIT, signal_server_handler); signal(SIGINT, signal_server_handler); // Setup the socket and the port init_servconnection(thePort); // Wait for connection do { //attenteconnection(); attenteconnection(&handle_req); } while (1); } LineFinder *initLineFinder(int argc, char *argv[]) { StringList ls; int count = 0; for (int i = 1; i < argc; i++) { ls.push_back(string(argv[i])); count++; } return new SimpleLineFinder(loadCountry(ls)); } /************************************************************************ The port was hard-coded because I don't know how we want to handle the port and the other parameters. ************************************************************************/ int main(int argc,char **argv) { if (argc < 3) { cerr << "usage: " << argv[0] << " PORT FILE..." << endl; exit(1); } int port = atoi(argv[1]); lineFinder = initLineFinder(argc - 1, argv + 1); // load the country structure cerr << "listening on port " << port << endl; initServer(port); }