/* TTT.c * * I consulted the following pages to increase my understanding of servers that * use select and to guide my thoughts of how I should construct my own server * using the select function. * http://www.binarytides.com/multiple-socket-connections-fdset-select-linux/ * http://www.lowtek.com/sockets/select.html * * Utilizes code from P. Kearns - 1987, 2009 * * Adam Carpenter - 2017 */ #include "ttt.h" #include #include #include #include #include #include #include #include #include int checkEndGame(char *board) { int i; int tie = TRUE; // Check rows for win. if (board[0] + board[1] + board[2] == 'X' + 'X' + 'X') return X_WIN; else if (board[0] + board[1] + board[2] == 'O' + 'O' + 'O') return O_WIN; if (board[3] + board[4] + board[5] == 'X' + 'X' + 'X') return X_WIN; else if (board[3] + board[4] + board[5] == 'O' + 'O' + 'O') return O_WIN; if (board[6] + board[7] + board[8] == 'X' + 'X' + 'X') return X_WIN; else if (board[6] + board[7] + board[8] == 'O' + 'O' + 'O') return O_WIN; // Check columns for win. if (board[0] + board[3] + board[6] == 'X' + 'X' + 'X') return X_WIN; else if (board[0] + board[3] + board[6] == 'O' + 'O' + 'O') return O_WIN; if (board[1] + board[4] + board[7] == 'X' + 'X' + 'X') return X_WIN; else if (board[1] + board[4] + board[7] == 'O' + 'O' + 'O') return O_WIN; if (board[2] + board[5] + board[8] == 'X' + 'X' + 'X') return X_WIN; else if (board[2] + board[5] + board[8] == 'O' + 'O' + 'O') return O_WIN; // Check diagonals for win. if (board[0] + board[4] + board[8] == 'X' + 'X' + 'X') return X_WIN; else if (board[0] + board[4] + board[8] == 'O' + 'O' + 'O') return O_WIN; if (board[2] + board[4] + board[6] == 'X' + 'X' + 'X') return X_WIN; else if (board[2] + board[4] + board[6] == 'O' + 'O' + 'O') return O_WIN; // Check for tie win. for (i = 0; i < 9; i++) { if (board[i] != 'X' && board[i] != 'O') tie = FALSE; } if (tie) { return TIE_GAME; } return 0; } int main() { int i; char message[MESSAGE_BUFF]; char hostname[MAX_HOSTNAME]; int streamPort; int querySock; int listenSock; int newSock; int connSockX; int connSockO; int queuedPlayers[MAX_CLIENTS]; socklen_t length; struct sockaddr_in *serverAddr, peerAddr; struct addrinfo hints, *addrlist; int hits; fd_set writeSockets; fd_set readSockets; char gameBoard[] = "123456789"; char handleX[MESSAGE_BUFF]; char handleO[MESSAGE_BUFF]; char gameTurn = 'X'; int numPlayers = 0; int gameOn = FALSE; int winner = 0; int qPort = 0; socklen_t qLength; struct sockaddr_in *qServerAddr, from; struct addrinfo qHints, *qAddrlist; socklen_t fsize; struct timeval timeout; // **** Create SOCK_DGRAM socket to send datagrams to clients **** // Create qServerAddr for dgram. memset(&qHints, 0, sizeof(qHints)); qHints.ai_family = AF_INET; qHints.ai_socktype = SOCK_DGRAM; qHints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; qHints.ai_protocol = 0; qHints.ai_canonname = NULL; qHints.ai_addr = NULL; qHints.ai_next = NULL; // Do getaddrinfo on qHints. if (getaddrinfo(NULL, "0", &qHints, &qAddrlist) != 0) { fprintf(stderr, "TTT: couldn't getaddrinfo for dgrams.\n"); exit(1); } // Set server address. qServerAddr = (struct sockaddr_in *) qAddrlist->ai_addr; // Create socket. if ((querySock = socket(qAddrlist->ai_family, qAddrlist->ai_socktype, 0)) < 0) { fprintf(stderr, "TTT: couldn't create socket for dgrams.\n"); exit(1); } // Bind socket. if (bind(querySock, (struct sockaddr *) qServerAddr, sizeof(struct sockaddr_in)) < 0) { fprintf(stderr, "TTT: couldn't bind socket for dgrams.\n"); exit(1); } // Get the socket name for the querySock. qLength = sizeof(struct sockaddr_in); if (getsockname(querySock, (struct sockaddr *)qServerAddr, &qLength) < 0) { fprintf(stderr, "TTT: couldn't get sock name for dgrams.\n"); exit(1); } qPort = ntohs(qServerAddr->sin_port); timeout.tv_sec = 10; timeout.tv_usec = 0; setsockopt(querySock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &timeout, sizeof(timeout)); // ************************************************************* // **** Create SOCK_STREAM socket to listen for connections **** // Create serverAddr for stream vc. memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; hints.ai_protocol = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; if (getaddrinfo(NULL, "0", &hints, &addrlist) != 0) { fprintf(stderr, "TTT: couldn't getaddrinfo.\n"); exit(1); } // Set server address. serverAddr = (struct sockaddr_in *) addrlist->ai_addr; // Create socket. if ((listenSock = socket(addrlist->ai_family, addrlist->ai_socktype, 0)) < 0) { fprintf(stderr, "TTT: couldn't create socket.\n"); exit(1); } // Bind socket. if (bind(listenSock, (struct sockaddr *) serverAddr, sizeof(struct sockaddr_in)) < 0) { fprintf(stderr, "TTT: couldn't bind socket.\n"); exit(1); } length = sizeof(struct sockaddr_in); if (getsockname(listenSock, (struct sockaddr *)serverAddr, &length) < 0) { fprintf(stderr, "TTT: couldn't get sock name.\n"); exit(1); } // Write ip and port numbers to game connection file. FILE *addrFile; if (!(addrFile = fopen("prof.falken", "w"))) { fprintf(stderr, "TTT: couldn't open file!\n"); exit(1); } if (gethostname(hostname, MAX_HOSTNAME) < 0) { fprintf(stderr, "TTT: failed to get hostname.\n"); exit(1); } streamPort = ntohs(serverAddr->sin_port); fprintf(addrFile, "%s\n", hostname); fprintf(addrFile, "%d\n", streamPort); fprintf(addrFile, "%d\n", qPort); fclose(addrFile); freeaddrinfo(addrlist); listen(listenSock, 6); length = sizeof(peerAddr); handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; for (i = 0; i < MAX_CLIENTS; i++) { queuedPlayers[i] = 0; } // **** Main server loop **** for (;;) { // Set up select sockets. FD_ZERO(&readSockets); FD_SET(listenSock, &readSockets); FD_ZERO(&writeSockets); FD_SET(querySock, &readSockets); if (connSockX != 0) { FD_SET(connSockX, &writeSockets); FD_SET(connSockX, &readSockets); } if (connSockO != 0) { FD_SET(connSockO, &writeSockets); FD_SET(connSockO, &readSockets); } for (i = 0; i < MAX_CLIENTS; i++) { if (queuedPlayers[i] > 0) { if (queuedPlayers[i] != 0) { FD_SET(queuedPlayers[i], &writeSockets); } } } // Select sockets that need attention. if ((hits = select(FD_SETSIZE, &readSockets, &writeSockets, NULL, NULL)) < 0) { fprintf(stderr, "TTT: select failed.\n"); exit(1); } // Handle query requests. if (FD_ISSET(querySock, &readSockets)) { fsize = sizeof(from); recvfrom(querySock, message, MESSAGE_BUFF, 0, (struct sockaddr *) &from, &fsize); if (gameOn == TRUE) { strcpy(message, "Game in session between "); strcat(message, handleX); strcat(message, " and "); strcat(message, handleO); strcat(message, "\n"); } else { strcpy(message, "No game in session...\n"); if (queuedPlayers[0] != 0) { strcat(message, "Players waiting to play:\n"); strcat(message, handleX); strcat(message, "\n"); } else { strcat(message, "No players waiting to play.\n"); } } strcat(message, "\0"); sendto(querySock, message, MESSAGE_BUFF, 0, (struct sockaddr *) &from, sizeof(struct sockaddr_in)); } // Handle new incoming connections. if (FD_ISSET(listenSock, &readSockets)) { if ((newSock = accept(listenSock, (struct sockaddr *) &peerAddr, &length)) < 0) { fprintf(stderr, "TTT: couldn't accept socket.\n"); exit(1); } // Queue up player. for (i = 0; i < MAX_CLIENTS; i++) { if (queuedPlayers[i] == 0 && newSock != 0) { queuedPlayers[i] = newSock; newSock = 0; } } newSock = 0; } // **** Game logic **** if (numPlayers == 0) { // Get first opponent. if (queuedPlayers[0] != 0) { // Pull most recent client to be X connSockX = queuedPlayers[0]; for (i = 1; i < MAX_CLIENTS; i++) { queuedPlayers[i - 1] = queuedPlayers[i]; } queuedPlayers[MAX_CLIENTS] = 0; if (sendMessage(connSockX, HANDLE) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } if (getMessage(connSockX, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } else { strcpy(handleX, message); numPlayers++; } } } else if (numPlayers == 1) { // Get second opponent. if (queuedPlayers[0] != 0) { // Pull most recent client to be O connSockO = queuedPlayers[0]; for (i = 1; i < MAX_CLIENTS; i++) { queuedPlayers[i - 1] = queuedPlayers[i]; } queuedPlayers[MAX_CLIENTS] = 0; if (sendMessage(connSockO, HANDLE) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } if (getMessage(connSockO, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } else { strcpy(handleO, message); numPlayers++; } } } else if (numPlayers == 2 && gameOn == FALSE) { // Set up a new game. if (FD_ISSET(connSockX, &writeSockets) && FD_ISSET(connSockO, &writeSockets)) { // If both clients are ready, inform them of opponents and sides. strcpy(message, "p X O "); strcat(message, handleO); if (sendMessage(connSockX, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } strcpy(message, "p O X "); strcat(message, handleX); if (sendMessage(connSockO, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } strcpy(gameBoard, "123456789"); gameTurn = 'X'; gameOn = TRUE; } } else if (numPlayers == 2 && gameOn == TRUE) { // Continue turn-taking. if (gameTurn == 'X' && FD_ISSET(connSockX, &writeSockets)) { strcpy(message, "b "); strcat(message, gameBoard); if (sendMessage(connSockO, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // send board to O if (sendMessage(connSockO, WAIT) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // tell O to wait for X to play if (sendMessage(connSockX, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // send board to X if (sendMessage(connSockX, MOVE) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // tell X to make a move if (getMessage(connSockX, gameBoard)) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // get move from X gameTurn = 'O'; // Check for endgame. winner = checkEndGame(gameBoard); if (winner) { strcpy(message, "b "); strcat(message, gameBoard); if (sendMessage(connSockX, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } if (sendMessage(connSockO, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } message[0] = ENDGAME; message[1] = winner; message[2] = '\0'; if (sendMessage(connSockX, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } if (sendMessage(connSockO, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } } else if (gameTurn == 'O' && FD_ISSET(connSockO, &writeSockets)) { strcpy(message, "b "); strcat(message, gameBoard); if (sendMessage(connSockX, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // send board to X if (sendMessage(connSockX, WAIT) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // tell X to wait for O to play if (sendMessage(connSockO, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // send board to O if (sendMessage(connSockO, MOVE) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // tell O to make a move if (getMessage(connSockO, gameBoard) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } // get move from O gameTurn = 'X'; // Check for endgame. winner = checkEndGame(gameBoard); if (winner) { strcpy(message, "b "); strcat(message, gameBoard); if (sendMessage(connSockX, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } if (sendMessage(connSockO, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } message[0] = ENDGAME; message[1] = winner; message[2] = '\0'; if (sendMessage(connSockX, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } if (sendMessage(connSockO, message) == -1) { numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } numPlayers = 0; handleX[0] = '\0'; handleO[0] = '\0'; connSockX = 0; connSockO = 0; gameOn = FALSE; } } } } exit(0); }