Есть ли способ сделать это, когда я принудительно завершаю работу со своего сервера (ввод clt + c), клиент также прерывается одновременно? - PullRequest
0 голосов
/ 10 января 2019

Напишите две отдельные программы на C, одну для TCP-сервера (обрабатывает запрос для один пользователь) и другой для клиента.

На стороне сервера Создает сокет и прослушивает определенный порт для обработки клиента запрос.

Существует файл по умолчанию, имеющий n строк, и сервер должен быть в состоянии обработать READX и WRITEX запрос от клиента.

  1. Процесс сервера должен маркировать строку, полученную от клиента, которая может содержать запрос READX или WRITEX в следующем формате:
    • READX k- прочитать k-ю строку из начала файла и вернуться к клиент.
    • WRITEX msg - добавить строку msg в конец файла, представленного в сервер и верните «УСПЕХ !!» клиенту как подтверждение.

На стороне клиента-

  1. Клиентский процесс должен принимать данные от пользователя, читать или НАПИСАТЬ на стороне сервера.
  2. Затем он инициирует соединение с сервером и перенаправляет запрос на сервер.
  3. Получает выходные данные с сервера и отображает их для пользователя.

Я почти обработал все ошибки. Все работает.

  1. Мой первый запрос, если я принудительно уничтожаю серверную программу (например, путем ввода clt + c), я хочу, чтобы клиентская программа была завершена одновременно. как я могу это сделать?
  2. 2-й запрос: когда я принудительно убиваю клиента, моя серверная программа автоматически завершается. Но я не понимаю, почему это происходит? В общем-то. этого не происходит. Я не нахожу, какая строка моего кода делает это возможным.

Код сервера:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h> 
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<unistd.h>
#define MYPORT "5000"
#define BACKLOG 3
char space[] = "    ";


/*this funtion is counting total no of  line present in input.txt file   
whenever it is called */
 int count_line()
{
FILE *ptr = fopen("input.txt", "r");
char * line = NULL;
int i = 0;
size_t count =0;
if (ptr == NULL)
{
    perror("error: line no 21");
}
while(getline(&line,&count, ptr) > 0)
{
    i++;
}
fclose(ptr);
return i;

}

/* This funtion will  write the message what is given by client at the   
end of the file */
 int write_instruction(char *buffer,int max_length)
{
char final[255];
FILE *fp = fopen("input.txt", "a");

if(fseek(fp, 0, SEEK_END) != 0)
{
    perror("fseek:error");
    exit(EXIT_FAILURE);
}
if (count_line() == 250)
{
    sprintf(final,"%d%s%s",count_line() +1, space, buffer);
}
else
    sprintf(final,"%s%d%s%s","\n",count_line() +1, space, buffer);
fputs(final, fp);
fclose(fp);
return (1);

}

 /* This function will fetch the exact line from input.txt what is instructed by READX in client and will return to client */
void read_instruction(int line_no, int max_length, int server_new_fd)
 {
ssize_t no_read_byte;

/*error checking , if you enter out of bound line no*/
if ((line_no > count_line()) || (line_no <1))
{
    if( ( no_read_byte = write(server_new_fd, "you are entering out of bound line no: TRY AGAIN", strlen("you are entering out of bound line no: TRY AGAIN")+1)) == -1)
    {
        perror("write:error");
        exit(EXIT_FAILURE);
    } 
    return;
}


char *line = NULL, final[max_length];
size_t count =0;

FILE *stream = fopen("input.txt", "r");
if (stream == NULL)
    exit(EXIT_FAILURE);


 for (int i = 0;i < line_no; ++i)
 {
    if(getline(&line, &count, stream) == -1)
    {
        perror("getline: error");

    }
 }

if( ( no_read_byte = write(server_new_fd, line, strlen(line)+1)) == -1)
    {
        perror("write:error");
        exit(EXIT_FAILURE);
    } 
free(line);
fclose(stream);

return ;
}
 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 status, server_sockfd, yes = 1, server_new_fd;
struct addrinfo hints, *ref, *p;
struct sockaddr_storage addr;
struct sockaddr_in m;
struct sockaddr_in6 n;
socklen_t addrlen;
ssize_t no_read_byte;
size_t count = 1024;
char ip_str[INET6_ADDRSTRLEN], buffer[1024], *readx, *writex;

memset(&hints, 0, sizeof hints);/* setting all bits of hints 0;*/

/* AF_UNSPEC mean any address family */
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;

if ( (status = getaddrinfo(NULL, MYPORT, &hints, &ref)) != 0)
{
    fprintf(stderr, "getaddrinfo : %s\n", gai_strerror(status));
    return 2;
}


for ( p = ref; p != NULL; p = p->ai_next)
{
    /*creating socket where passing ai_family , ai_socktype, ai_protocol as domain, type, protocol.
    And chhecking one by one struct addrinfo from list returned by getaddrinfo(), to get first successful socket descriptor.*/
    if ( (server_sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
    {
        perror("socket:error");
        continue;
    }

    if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == -1)
    {
        perror("setsockopt : error");
        exit(1);
    }

    if (bind(server_sockfd, p->ai_addr, p->ai_addrlen) == -1)
    {
        perror("bind : error");
        close(server_sockfd);
        continue;
    }
    /*  I am getting out of this for loop because I already got successful socket fd, and successful binding.
        I don't need to traverse the list anymore.
    */
    break;
}

freeaddrinfo(ref);// I am done with struct addrinfo list.

/* I am listening here.*/
if (listen(server_sockfd, BACKLOG) == -1)
{
    perror("listen : error");
    exit(1);
}

printf("server: waiting for connection...........\n");


addrlen = sizeof(addr); /* If there are any client trying to connect, I accept here its connect request. */
    if( (server_new_fd = accept(server_sockfd, (struct sockaddr *)&addr, &addrlen)) == -1)
    {
        perror("accept: error");
        exit(EXIT_FAILURE);
    }

    inet_ntop(addr.ss_family, get_in_addr((struct sockaddr *)&addr), ip_str, sizeof ip_str);
    printf("Server : %s is connected .\n", ip_str);


int i=0;

close(server_sockfd); /* WE  are here just hanldling one client, so we do need to listen anymore. so we are closing server_sockfd */

while(1)
{
memset(&buffer, 0, 1024); /* Begining of every loop, we shall set '\0' to every byte of buffer */


/*we read here first time for every loop from server_new_fd and save it into buffer*/
if( (no_read_byte = read(server_new_fd, &buffer, count)) == -1)
{
    perror("read failed");
    exit(EXIT_FAILURE);
}

writex = buffer;


/*we are checking error here. when you will give just empty string, that will be detected here*/    
if( (readx = strtok_r( writex, " ", &writex)) == NULL)
{
    if( ( no_read_byte = write(server_new_fd, "you are entering invalid input", strlen("you are entering invalid input")+1) == -1))
    {
        perror("write:error");
        exit(EXIT_FAILURE);
    }
    continue; 

}/* here we are checking for shutdown condition . if you want to shutdown just enter -1*/
else if (strcmp(readx, "-1") == 0)
{
    printf("we are terminating\n");
    if( (no_read_byte = write(server_new_fd,"-1", strlen("-1")+1) ) == -1)
        {
            perror("write: error");
            exit(EXIT_FAILURE);
        }
    exit(EXIT_SUCCESS);
}
/* if you enter just READX or WRITEX , then that will be detected here. */
else if ((atoi(writex) == 0) && (strcmp(readx, "READX") ==0) )
{

    if( ( no_read_byte = write(server_new_fd, "you are entering invalid input", strlen("you are entering invalid input")+1)) == -1)
    {
        perror("write:error");
        exit(EXIT_FAILURE);
    }
    continue;
}
else if ((writex[0] == 0) && (strcmp(readx, "WRITEX") ==0 ))
{
    if( ( no_read_byte = write(server_new_fd, "you are entering invalid input", strlen("you are entering invalid input")+1)) == -1)
    {
        perror("write:error");
        exit(EXIT_FAILURE);
    }
    continue;
}
/* this for READX formatted instruction */
else if( strcmp(readx, "READX") ==0)
{ 
    char * str = strtok_r( writex, " ", &writex);

    if (atoi(writex) != 0)
    {
        /* if you enter like READX 12 34 45, that will be detected here */
        if( ( no_read_byte = write(server_new_fd, "you are entering invalid input", strlen("you are entering invalid input")+1)) == -1)
        {
            perror("write:error");
            exit(EXIT_FAILURE);
        }
        continue; 
    }
    else /* This for correct formatted READX instruction */
    {

        printf("Client is instructing to read and return line.\n");
        read_instruction(atoi(str), 255, server_new_fd);
    }

} /* this for correct WRITEX instruction */
else if (strcmp(readx, "WRITEX") ==0)
{
    printf("Client is instructing to append given string to end of the file.\n");
    if(write_instruction(writex, 255) == 1) 
    {
        if( (no_read_byte = write(server_new_fd,"SUCCESS!!", strlen("SUCCESS!!")+1) ) == -1)
        {
            perror("write: error");
            exit(EXIT_FAILURE);
        }

    }   
}
else /* this for all other invalid error */
{
    if( ( no_read_byte = write(server_new_fd, "you are entering invalid input 1", strlen("you are entering invalid input 1  ")+1)) == -1)
        {
            perror("write:error");
            exit(EXIT_FAILURE);
        }
        continue;
}


}

close(server_new_fd);



}

код клиента:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h> 
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<unistd.h>
#define MYPORT "5000"


void *convert(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[])
{
if (argc != 2)
{
fprintf(stderr, "you are entering wrong number of string\n"   );
    exit(EXIT_FAILURE);
}

struct addrinfo hints, *ref, *p;
int status, client_sockfd, yes = 1;
ssize_t  no_read_byte;
size_t count=1024;
struct sockaddr_storage addr;
char ip_str[INET6_ADDRSTRLEN], buffer[1024];

memset(&hints, 0, sizeof hints);

if ( (status = getaddrinfo(argv[1], MYPORT, &hints, &ref)) != 0)
{
    fprintf(stderr, "getaddrinfo : %s\n", gai_strerror(status));
    return 2;
}


 for (p = ref;p != NULL; p = p->ai_next)
 {
if ( (client_sockfd = socket(p->ai_family, p->ai_socktype,  p->ai_protocol)) == -1)
    {
        perror("client_socket: error");
        continue;
    }



    if ( (connect(client_sockfd, p->ai_addr, p->ai_addrlen)) == -1)
    {
        perror("connect: error");
        close(client_sockfd);
        continue;
    }break;
 }
 if (p == NULL) {
    fprintf(stderr, "client: failed to connect\n");
    exit(EXIT_FAILURE);
}
//inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), ip_str, sizeof ip_str);
inet_ntop(p->ai_family, convert((struct sockaddr *)p->ai_addr), ip_str, sizeof ip_str);
//printf("%s\n",convert((struct sockaddr *)p->ai_addr));
printf("client: connecting to %s\n", ip_str);

freeaddrinfo(ref);
while(1){
printf("please enter the instruction: ");
gets(buffer);

if ((no_read_byte = write(client_sockfd, buffer, strlen(buffer)+1)) == -1)
{
    perror("write:error");
    exit(EXIT_FAILURE);

}
bzero(buffer, 255);
if((no_read_byte = read(client_sockfd, buffer, 255)) == -1)
{

    perror("read:error");

    exit(EXIT_FAILURE);

}
else if (strcmp(buffer, "-1") == 0)
{
    exit(EXIT_SUCCESS);
}
else
    printf("%s\n",buffer );
bzero(buffer, 255);
}

close(client_sockfd);
}

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Я не уверен, что правильно читаю ваш код, но мне кажется, что сообщение не генерируется, если считываются нулевые байты (клиентом или сервером). read (2) может вернуть ноль:

ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ В случае успеха возвращается количество фактически прочитанных байтов. на чтение конца файла, ноль возвращается. В противном случае возвращается -1, а глобальная переменная errno установлена ​​для указания ошибки.

Конец файла не считается ошибкой. Это нормальный признак того, что пульт дистанционного управления закрыл соединение, что автоматически происходит при завершении программы.

Следовательно, для любого процесса с ожидающей операцией чтения (заблокированной, ожидающей данных от однорангового узла) read (2) вернет ноль, когда одноранговый узел завершит работу. Если вы убьете свой сервер, а клиент читает, клиент увидит нулевые байты и наоборот.

Ваша клиентская логика не делает ничего особенного для этого случая. Вы проверяете на -1. Если не -1, вы проверяете кучу вещей в (пустом) буфере. Затем вы выходите.

Проверьте явно на ноль и напишите сообщение, что сервер закрыл соединение. Я думаю, вы увидите, что клиент не "автоматически прекращается". Это просто падает с конца.

0 голосов
/ 10 января 2019

Вы можете использовать обработчик сигнала, который будет определять Ctrl + C, а затем вы можете написать функцию для отправки сигнала выключения клиенту. Ctrl + C отправляет сигнал SIGINT.

#include <signal.h>

void sigintHandler(int sig_num) 
{ 
    signal(SIGINT, sigintHandler); 
    //Code here for Shutting Down Client
    fflush(stdout); 
} 

Вы можете прочитать больше об этом здесь, и можете сделать это для любого сигнала, который генерируется, если вы хотите обработать их тоже

Это должно решить вашу первую проблему

...