Monitor stdin and the socket using select to avoid blocking on either one. Update the client so it monitors just two file descriptors: the socket with the server and stdin. Whenever a message is received on either, your program should read it and pass it to the correct output stream.

while (1) {
        num_read = read(STDIN_FILENO, buf, BUF_SIZE);
        if (num_read == 0) {
            break;
        }
        buf[num_read] = '\0';

        /*
         * We should really send "\r\n" too, so the server can identify partial
         * reads, but you are not required to handle partial reads in this lab.
         */
        if (write(sock_fd, buf, num_read) != num_read) {
            perror("client: write");
            close(sock_fd);
            exit(1);
        }

        num_read = read(sock_fd, buf, sizeof(buf) - 1);
        if (num_read == 0) {
            break;
        }
        buf[num_read] = '\0';
        printf("[Server] %s", buf);
    }