首页 > 解决方案 > 当我强制退出服务器(输入 clt + c)时,有什么办法可以做到这一点,客户端也会同时终止?

问题描述

编写两个独立的 C 程序,一个用于 TCP 服务器(处理单个用户的请求),另一个用于客户端。

在服务器端 - 创建一个套接字并侦听某个特定端口以处理客户端请求。

存在一个包含 n 行的默认文件,服务器应该能够处理来自客户端的 READX 和 WRITEX 请求。

  1. 服务器进程应该对从客户端接收到的字符串进行标记,该字符串可能包含以下格式的 READX 或 WRITEX 请求 -
    • READX k- 从文件开头读取第 k 行并返回给客户端。
    • WRITEX msg - 将 msg 字符串附加到服务器上存在的文件末尾并返回“SUCCESS!!” 给客户作为确认。

在客户端——

  1. 客户端进程应该从用户那里获取输入,无论是在服务器端读取还是写入。
  2. 然后它启动与服务器的连接并将查询转发到服务器。
  3. 从服务器接收输出并将其显示给用户。

我几乎处理了所有的错误处理。一切正常。

  1. 我的第一个查询是否强制终止服务器程序(例如通过输入 clt+ c),我希望客户端程序同时终止。我怎样才能做到这一点?
  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);
}

标签: csocketsnetwork-programmingposix

解决方案


我不确定我是否正确阅读了您的代码,但在我看来,如果读取零字节(由客户端或服务器),则不会产生任何消息。 读取(2) 可以返回零:

返回值 如果成功,则返回实际读取的字节数。在读取文件结尾时,返回零。否则,返回 -1 并设置全局变量 errno 以指示错误。

文件结束不被视为错误。这是远程关闭连接的正常指示,当程序终止时会自动发生。

因此,对于任何具有未完成的读取操作(阻塞,等待来自对等方的数据)的进程,read (2) 将在对等方终止时返回零。如果您终止服务器并且客户端正在读取,则客户端将看到读取的字节为零,反之亦然。

您的客户端逻辑对这种情况没有做任何特别的事情。你测试 -1。如果不是 -1,则在(空)缓冲区中测试一堆东西。然后你退出。

显式检查零,并写一条服务器关闭连接的消息。我想你会看到客户端没有“自动终止”。它只是从尽头掉下来。


推荐阅读