Работа сервера без ожидания чтения клиентской команды при программировании сокета C - PullRequest
0 голосов
/ 29 марта 2020

У меня есть простой сервер и клиент, написанный на C. Они хорошо общаются до самого конца моей программы, где сервер, кажется, пропускает метод «read» и просто продолжает, он напечатает пустую строку в printf («% s», playAgain);

Вот конец кода

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

#define BACKLOG 10

char invalidPortNumber[] = "Please specify a port number between 2000 and 65535";

char intro[] = "Welcome to the prisoners dilemma";
char playGame[] = "Will you stay silent or betray the other prisoner?\nType S for silent or B for betray";
char option1[] = "The other prisoner betrayed you\nYou each get 2 years in prison";
char option2[] = "The other prisioner betrayed you\nYou get 3 years in prison, the other prisioner is set free";
char option3[] = "The other prisioner stayed silent\nYou are set free, the other prisioner gets 3 years in prison";
char option4[] = "The other prisioner stayed silent\nYou both get 1 year on a lesser charge";

int main(int argc, char *argv[]) {

     if (argc < 2) {
         printf("Run with port number as the arguement\n");
         exit(1);
     }
     int port = atoi(argv[1]);
     if (port<2000 || port>65535){
         printf("%s\n", invalidPortNumber);
         exit(2);
     }

    //Struct to store information for IPv4 address
    struct sockaddr_in serverAddress;

    //Create socket for IPv4, reliable stream (TCP), default protocol
    int serverSocket = socket(PF_INET, SOCK_STREAM, 0);

    //Specify that IPv4 family addresses will be used
    serverAddress.sin_family = AF_INET;
    //Set the port number
    serverAddress.sin_port = htons(port);
    //Bind to all local interfaces for IP
    serverAddress.sin_addr.s_addr = INADDR_ANY;

    //Bind the created socket to the IP address specified in the sockaddr_in struct
    bind(serverSocket, (struct sockaddr *) &serverAddress, sizeof(serverAddress));

    //Listen for connections, allowing backlog of up to BACKLOG connection requests
    listen(serverSocket, BACKLOG);
    int play = 0;
    while(1) {

        //Struct to store info of connecting clients
        struct sockaddr_in clientAddress;
        socklen_t clientAddrSize = sizeof(clientAddress);

        //Create a socket for the connection between the client and server
        int connectionSocket = accept(serverSocket, (struct sockaddr *) &clientAddress, &clientAddrSize);
        //Input buffer to store client's request
        do{
        char input[800];
        memset(input, '\0', sizeof(input));

        //Have intro to the game
        write(connectionSocket, intro, sizeof(intro) - 1);

        //Read client's input


        read(connectionSocket, input, sizeof(input)-1);
        if(strcmp(input,"Y\n")==0||strcmp(input,"y\n")==0){
            write(connectionSocket, playGame, sizeof(playGame) - 1);
        }
        else if(strcmp(input,"N\n")==0||strcmp(input,"n\n")==0){
            write(connectionSocket, "Okay, connection closed", sizeof("Okay, connection closed") - 1);
            close(connectionSocket);
            return 0;
        }

        //read clients choice
        char clientChoice[2];
        read(connectionSocket, clientChoice, sizeof(clientChoice)-1);


        srand(time(NULL));
        int random = rand();
        if( random % 2 ==0 ){

            char serverChoice[2] = "S";
            if(strcmp(clientChoice, "S")==0){
                write(connectionSocket, option4, sizeof(option4) - 1);
            }
            else if(strcmp(clientChoice, "B")==0){
                write(connectionSocket, option3, sizeof(option3) - 1);
            }

        }
        else {

            char serverChoice[2] = "B";
            if(strcmp(clientChoice, "S")==0){
                write(connectionSocket, option2, sizeof(option2) - 1);
            }
            else if(strcmp(clientChoice, "B")==0){
                write(connectionSocket, option1, sizeof(option1) - 1);
            }

        }


        char playAgain[5];
        read(connectionSocket, playAgain, sizeof(playAgain)-1);
        printf("%s",playAgain);
        if(strcmp(playAgain, "Play")==0){
            printf("Playing again");
            play=1;
        }
        }while(play==1);
    }

    //Close the server socket and terminate the program if the loop ever ends
    close(serverSocket);
    return 0;

}

Это сервер.

А теперь вот конец клиента

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

#define BACKLOG 10

char invalidPortNumber[] = "Please specify a port number between 2000 and 65535";

char intro[] = "Welcome to the prisoners dilemma";


int main(int argc, char *argv[]) {
    char buffer[512];
    char IPAddress[15];
    int n;
     if (argc < 2) {
         printf("Run with host IP and port number as the arguement\n");
         exit(1);
     }
     int port = atoi(argv[1]);
     if (port<2000 || port>65535){
         printf("%s\n", invalidPortNumber);
         exit(2);
     }

    //Struct to store information for IPv4 address
    struct sockaddr_in serverAddress;

    //Create socket for IPv4, reliable stream (TCP), default protocol
    int serverSocket = socket(PF_INET, SOCK_STREAM, 0);

    //Specify that IPv4 family addresses will be used
    serverAddress.sin_family = AF_INET;
    //Set the port number
    serverAddress.sin_port = htons(port);
    //Bind to all local interfaces for IP
    serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1");

    //Bind the created socket to the IP address specified in the sockaddr_in struct
    int play=0;

    if(connect(serverSocket, (struct sockaddr *) &serverAddress, sizeof(serverAddress))<0){
        printf("Couldn't connect, make sure the server is running and port number is correct \n");
        return 1;
    }
    //read intro from server

    do{
    bzero(buffer,512);
    n = read(serverSocket,buffer,511);
    printf("%s\n",buffer);
    //ask user if they'd like to play
    int validCommand=1;
    do{
    printf("Would you like to play? (Y/N) ");
    bzero(buffer,512);
    fgets(buffer,511,stdin);
    if(strcmp(buffer, "Y\n")==0||strcmp(buffer, "N\n")==0){
        validCommand=0;
    }
    else{
        printf("Invalid command \n");
    }
    }while(validCommand==1);
    //write whether user wants to play to server
    n = write(serverSocket,buffer,strlen(buffer));
    if (n < 0) 
         error("ERROR writing to socket");
    bzero(buffer,512);
    //read response from server
    n = read(serverSocket,buffer,511);
    if (n < 0) 
         error("ERROR reading from socket");
    printf("%s\n",buffer);
    if(strcmp(buffer, "Okay, connection closed")==0){
        close(serverSocket);
        return 0;
    }



    do{
    bzero(buffer,512);
    printf("Make your choice (B/S) ");
    fgets(buffer,511,stdin);
    if(strcmp(buffer, "B\n")==0||strcmp(buffer, "S\n")==0){
        validCommand=0;
    }
    else{
        printf("Invalid command \n");
        validCommand=1;
    }
    }while(validCommand==1);
    //write the users choice to the server
    n = write(serverSocket,buffer,strlen(buffer));
    if (n < 0) 
         error("ERROR writing to socket");
    bzero(buffer,512);
    n = read(serverSocket,buffer,511);
    if (n < 0) 
         error("ERROR reading from socket");
    printf("%s\n",buffer);

    do{
    bzero(buffer,512);
    printf("Would you like to play again? (Play/Quit) ");
    fgets(buffer,511,stdin);
    if(strcmp(buffer, "Play\n")==0||strcmp(buffer, "Quit\n")==0){
        validCommand=0;
    }
    else{
        printf("Invalid command \n");
        validCommand=1;
    }
    }while(validCommand==1);
    //write the users choice to the server
    if(strcmp(buffer, "Quit\n")==0){
        printf("Closing Connection to server");
        close(serverSocket);
        return 0;
    }
    if(strcmp(buffer, "Play\n")==0){
        printf("Playing again");
        play=1;
        n = write(serverSocket,buffer,strlen(buffer)-1);
        if (n < 0) 
         error("ERROR writing to socket");
    }



    }while(play==1);


}

И клиент, и сервер работать для выбора B / S, клиент отправляет, а сервер отвечает. Я понятия не имею, что может быть не так, но сервер, кажется, не ждет окончательной команды клиентов

1 Ответ

1 голос
/ 02 апреля 2020

Во-первых, я думаю, что основная проблема c, с которой вы сталкиваетесь, заключается в распространенном заблуждении, что 1 запись соответствует 1 чтению автоматически. Это не так.

Упомянутая вами проблема вызвана несинхронизацией операций чтения и записи c. Вы должны убедиться, что вы читаете ту же сумму, что отправляете каждый раз. Сервер не «продолжает работу, не ожидая чтения клиентской команды»; он только что прочитал и проигнорировал это.

Например, когда клиент делает

write(serverSocket, buffer, strlen(buffer))

, сервер будет сбит с толку. Если вы сначала не отправите размер строки, сервер не сможет узнать, когда прекратить чтение. Это особенно верно, поскольку вы не отправляете NUL-терминатор. Этой конкретной проблемы c можно избежать, выполнив дополнительную обработку на стороне клиента. Проверив входные данные по "Y" и "N" на клиенте, вы можете упростить обмен данными до простой отправки однобайтового логического значения. Это уменьшает сложность вашего кода и объем обмена данными между сервером и клиентом. Если вам нужны примеры того, как вы могли бы начать улучшать это, или у вас есть вопросы, просто задайте их в комментариях.

Дополнительные примечания:
1) Вам не нужно отправлять вступление через сокет; это уже на стороне клиента.
2) Булевы переменные, такие как validCommand, обычно 0 для false и 1 для true. Похоже, вы перевернули это в своем коде. Это не так по сути, просто сбивает с толку читать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...