【问题标题】:Why my simulation of chat service write wrong content?为什么我模拟的聊天服务写错了内容?
【发布时间】:2016-05-17 07:08:45
【问题描述】:

我正在模拟 C 语言上的聊天服务,我向服务器端发送一条消息,例如“U:用户名,密码;登录用户,但是当我询问谁在线时,它打印了 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);
 }

【问题讨论】:

  • StackOverflow 不是调试服务。请使用调试器帮助缩小问题所在。但首先要确保所有字符串都正确终止,并记住管道(FIFO 是)是一种 流式 通信方法,没有任何设置的消息边界。这意味着接收可能会收到少于或更多的完整消息,并且您需要在流之上实现某种协议以自己引入消息边界(例如,具有开始/停止序列或固定长度的消息大小标题)。

标签: c simulation fifo


【解决方案1】:

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

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

考虑一下:

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

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

在调用add_online 时,缓冲区现在看起来像这样"U\0name\0password\0\0",其中username 指向c 字符串"name" 仍存储在readbuf

当您在链表中创建新节点时,您只需将指针分配给 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;
    }
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    相关资源
    最近更新 更多