为什么我的模拟聊天服务写错了内容?

Why my simulation of chat service write wrong content?

我正在用 C 语言模拟聊天服务,我向服务器端发送一条消息,例如“U:username,password; 以登录用户,但是当我询问谁在线时,它打印了 n 次(设 n 为数字)在线用户数)提问的用户姓名!什么不是预期的行为。有人可以告诉我为什么吗?

server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <linux/stat.h>

#define FIFO_FILE "MYFIFO"

typedef struct online {
 char* user;
 struct online* next;
} online;

typedef struct users{
 char* name;
 char* password;
} users;

online* begin=NULL;
online* end=NULL;
FILE *fp;
void start_server();

void add_online(char* user);

void realocate_end();

void remove_online(char* user);

char* user_access(char* s);



void who_online();

void free_all();

int find_username(char* user);

void add_user_regist(char* user,char* password);

users* find_user_regist(char* user, char* password);

int main(int argc, char *argv[])
{
 if(argc>1){
  char password[50];
  char password2[50];

  printf("Password? ");
  scanf("%s",password);
  printf("Repita a password? ");
  scanf("%s",password2);

  if(strcmp(password,password2)!=0){
   printf("Passwords diferentes!\n");
   exit(1);
  }

  if(find_username(argv[1])){
   printf("Utilizador ja existe\n");
   exit(1);
  }
  add_user_regist(argv[1],password);
  printf("Utilizador %s adicionado.\n",argv[1]);
  exit(0);
 }


 char readbuf[250];

 /* Create the FIFO if it does not exist */
 umask(0);
 mknod(FIFO_FILE, S_IFIFO|0666, 0);
 printf("Servidor iniciado.\nEm modo de espera de mensagens\n");

 while(1)
 {
  fp = fopen(FIFO_FILE, "r");
  fgets(readbuf, 250, fp);
  fclose(fp);
  if(readbuf[0]=='U')
   user_access(readbuf);
  if(readbuf[0]=='W')
   who_online();
  printf("Received string: %s\n",readbuf);


 }
return(0);
}



void realocate_end(){
 online* tmp=begin;
 while(tmp!=NULL){
  if(tmp->next==NULL)
   end=tmp;
  tmp=tmp->next;
 }
}

void remove_online(char* user){
 online* tmp=begin;
 char path[100];
 sprintf(path,"/tmp/chatroom_%s",user);
 unlink(path);
 while(tmp!=NULL){
  if(tmp->user==user && tmp->user == end->user){
   free(end);
   end=NULL;
   realocate_end();
  }
  else if(tmp->user==user && tmp->user == begin->user ){
   begin=begin->next;
   free(tmp);
   break;

  }
  else if(tmp->next->user ==user){
   online* tmp2 = tmp->next->next;
   free(tmp->next);
   tmp->next = tmp2;
   realocate_end();
   break;
  }
  tmp=tmp->next;
 }
}

void add_online(char* user){
 online* tmp = (online*)malloc(sizeof(online));
 tmp->user = user;
 tmp->next = NULL;


 if(begin==NULL){
  begin = tmp;
  end=tmp;
 }
 else{
  end->next=tmp;
  end=end->next;
 }
}

void who_online(){
 online* tmp = begin;
 while(tmp!=NULL){
  printf("%s\n",tmp->user);
  tmp=tmp->next;
 }
}

void free_all(){
 while(begin!=NULL){
  online* tmp = begin->next;
  free(begin);
  begin = tmp;
 }
}

int find_username(char* user){
 FILE* fp;
 char line[50];
 fp=fopen("./regist", "r");
 while(fscanf(fp,"%s",line)!=EOF){
  char* name=strtok(line,",");
  if(strcmp(name,user)==0){
   fclose(fp);
   return 1;
  }
 }
 return 0;
}

char* user_access(char* s){
 int i=0 ,begin=2;
 strtok(s,":");
 char* username=strtok(NULL,",");
 char* password=strtok(NULL,";");

 add_online(username);


 } 

 users* find_user_regist(char* user, char* password){
  FILE* fp;
  char line[50];

  users* tmp = (users*) malloc(sizeof(users));

  fp=fopen("./regist", "r");
  while(fscanf(fp,"%s",line)!=EOF){
   tmp->name =strtok(line,",");
   tmp->password = strtok(NULL,";");
   if(strcmp(tmp->name,user)==0 && strcmp(tmp->password,password)==0 ){
    fclose(fp);
    return tmp;
   }


  }
  fclose(fp);
  return NULL;
 }

 void add_user_regist(char* user,char* password){
  FILE* fp;
  fp=fopen("./regist", "a");
  if(fp==NULL)
   fp=fopen("./regist", "w");
  fprintf(fp,"%s,%s;\n",user,password);
  fclose(fp);
 }

client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <termios.h>
#include <linux/stat.h>


#define FIFO_FILE "MYFIFO"


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

    char user[50];
    char password[50];

    if(argc==1){
     printf("Não escolheu utilizador\n");
     exit(0);
    }

    sprintf(user,"%s",argv[1]);
    printf("Password:\n");
    scanf("%s",password);
    getchar();

    char mensagem[250];

    if((fp = fopen(FIFO_FILE, "w")) == NULL) {
     perror("fopen");
     exit(1);
    }
    char path [100] ;

    strcpy(path,".");
    strcat(path,user);
    umask(0);
    mknod(path, S_IFIFO|0666, 0);

    sprintf(mensagem,"U:%s,%s;",user,password);
    fputs(mensagem, fp);
    fclose(fp);

    while(1){

     printf("**Menu**\n1) Listar utilizadores online\n2) Mandar SMS a um utilizador\n3) Logout\n\n");

     char opcao;
     scanf("%c",&opcao);
     char lixo=getchar();

     if(opcao!='1' && opcao!='2' && opcao!='3')
       continue;
     if(opcao=='1'){
      if((fp = fopen(FIFO_FILE, "w")) == NULL) {
       perror("fopen");
       exit(1);
     }
     sprintf(mensagem,"W:%s;",user);
     fputs(mensagem, fp);
     fclose(fp);
     }
     else if(opcao=='2'){

     if((fp = fopen(FIFO_FILE, "w")) == NULL) {
      perror("fopen");
      exit(1);
     }

     memset(mensagem,0,sizeof(mensagem));
     scanf("%s",mensagem);
     char lixo=getchar();
     fputs(mensagem, fp);
     fclose(fp);
     }
    else if(opcao=='3'){
     break;
    }

   }



  return(0);
 }

这可能是因为您忘记分配新内存来存储每个用户的用户名。您现在执行此操作的方式是为指向每个用户的字符串的指针分配新内存 - 而不是实际的字符串。

如果您通过函数调用遵循存储在结构中的值的路径,您将看到每条记录都指向 readbuf 数组

考虑一下:

char readbuf[250] = "U:name,password;"
user_access(readbuf);

在user_access中char *s指向字符串的开始,即U。第二次调用 strtok returns 指向用户名开头的指针 n 并用空终止符替换 ,

在调用 add_online 时,缓冲区现在看起来像这样 "U[=19=]name[=19=]password[=19=][=19=]" username 指向仍然存储在 "name" 中的 c 字符串 "name" =12=]

当你在链表中创建一个新节点时,你只需将指针分配给 readbuf 中的那个 c 字符串,这样用户名就会根据缓冲区中的内容而改变。由于 W 命令只有一个字符,最后发送到服务器的用户名将在缓冲区中完好无损。

要解决此问题,您只需为每个用户名分配新内存并复制字符串即可。

void add_online(char* user){
    online* tmp = (online*) malloc(sizeof(online));
    tmp->user = (char*) malloc(strlen(user)*sizeof(char));
    strcpy(tmp->user, user);
    tmp->next = NULL;


    if (begin==NULL){
        begin = tmp;
        end=tmp;
    }
    else {
        end->next=tmp;
        end=end->next;
    }
}

同样,在释放结构时,您应该释放为用户名分配的内存