You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GMS客户端/服务器聊天程序中atoi致字符串值异常求助

GMS聊天程序atoi调用后member_name乱码问题

我们开发GMS客户端/服务器聊天程序时,遇到异常:调用atoi函数后,member_name的值丢失变为乱码;注释掉atoi语句后,member_name能保持原有值。

主逻辑相关代码

unserialize_tcp(buffer,&type, &grpname, &member_name, &received_addr, &received_port);
printf("1%s %s %s\n", grpname, member_name, received_addr);
addr = atoi(received_addr);
client.sin_addr.s_addr = addr;
client.sin_port = received_port;
printf("2%s %s \n", grpname, member_name);

执行输出结果

Succesfully created RECEIVER WORKER and SOCKET WORKER 

Available options
     type 'exit' to close gms  
BEFORE
AFTER
BEFORE
New Client socket 4 stored at index 0
packet 0_omada_onoma_16777343_26385
1omada onoma 16777343
2omada @���x 
3omada @���x 
4omada @���x 
Group omada just got a new member @���x 
We didn't notify every member of the group  about the new member

unserialize_tcp函数代码

void unserialize_tcp(char* message, type_t *type, char** grpname, char** member_name, char** received_addr, int* received_port) {

    char msg[TCP_PACKET] = {'\0'};
    char *saveptr;
    char *ptr = message;
    char *delim = "_";
    int i;
    for (i = 0; ptr[i]; ptr[i] == '_' ? i++ : *ptr++);
    if (i != 4) {
        printf("Message corrupted\n");
        return;
    }
    strcpy(msg, message);
    *type = atoi(strtok_r(msg, delim, &saveptr));
    *grpname = strtok_r(NULL, delim, &saveptr);
    *member_name = strtok_r(NULL, delim, &saveptr);
    *received_addr = strtok_r(NULL, delim, &saveptr);
    *received_port = atoi(strtok_r(NULL, delim, &saveptr));
}

gms_receive_worker线程代码

void *gms_receive_worker(void *args) {
  int i;
  char buffer[TCP_PACKET];
  char *grpname = (char*)calloc(16, sizeof(char));
  char* member_name = (char*)calloc(16, sizeof(char));
  type_t type ;
  char* received_addr = (char*)calloc(LENGTH_MSG, sizeof(char));
  char *msg;
  msg = (char*) malloc(sizeof(char)*TCP_PACKET);
  int received_port, addr;
  
  struct sockaddr_in client;
  while(!flag) {
    for (i=0; i< MAX_CLIENTS; i++) {
      memset(buffer, '\0',sizeof(buffer));
      if (recv(client_sockets[i], (char*)buffer, TCP_PACKET, MSG_WAITALL) < 0 ) {
            continue;
      }
      
      if(strlen(buffer) > 0) {
        printf("packet %s\n", buffer);
        unserialize_tcp(buffer,&type, &grpname, &member_name, &received_addr, &received_port);
        printf("1%s %s %s\n", grpname, member_name, received_addr);
        addr = atoi(received_addr);
        client.sin_addr.s_addr = addr;
        client.sin_port = received_port;
         printf("2%s %s \n", grpname, member_name);
        if (type == EXIT_TCP_SOCKET) {
          printf(BOLDRED"Client exited and the socket %d just closed\n"RESET, client_sockets[i]);
          client_sockets[i] = 0;
          continue;
        }
        else if (type == GROUP_JOIN) {
          printf("3%s %s \n", grpname, member_name);
          if (search_gms_list(list, grpname, member_name) != NULL) {
            msg = serialize_tcp(GROUP_REJECT_MEMBER, grpname, member_name, received_addr, received_port);
            if(send(client_sockets[i], (const char *)msg, TCP_PACKET, MSG_NOSIGNAL) < 0 ) {
                printf(BOLDRED"Failed inside group join\n"RESET);
                exit(1);
            }
            continue;
          }
           printf("4%s %s \n", grpname, member_name);
          printf(BOLDRED"Group %s just got a new member %s \n"RESET, grpname, member_name);
          
          add_gms_list(list, grpname, member_name, client, client_sockets[i]);
          msg = serialize_tcp(GROUP_SOMEONE_JOINED, grpname, member_name, received_addr, received_port);
            
          if (gms_sender_worker(grpname,member_name, msg) == 1) {
            printf(CYAN"We notified every member of the group %s about the new member\n"RESET, grpname);
            continue;
          }
          else{
            printf(CYAN"We didn't notify every member of the group %s about the new member\n"RESET, grpname);
            continue;
          }
            
        }
        else if (type == GROUP_LEAVE ) {
          if (search_gms_list(list, grpname, member_name) == NULL) {
            msg = serialize_tcp(LEAVE_FAILED, grpname, member_name, received_addr, received_port);
            if(send(client_sockets[i], (const char *)msg, TCP_PACKET, MSG_NOSIGNAL) < 0 ) {
                printf(BOLDRED"Failed inside group join\n"RESET);
                exit(1);
            }
            continue;
          }
          printf(BOLDRED"Group %s lost the member %s \n"RESET, grpname, member_name);

          delete_gms_list(list, grpname, member_name, client);
          msg = serialize_tcp(GROUP_SOMEONE_LEFT, grpname, member_name, received_addr, received_port);
            
          if (gms_sender_worker(grpname,member_name, msg) == 1) {
            printf(CYAN"We notified every member of the group %s about the member that left\n"RESET, grpname);
            continue;
          }
          else{
            printf(CYAN"We didn't notify every member of the group %s about the member that left\n"RESET, grpname);
            continue;
          }
        }
        else{
          printf(BOLDRED"You are not supposed to see this \n"RESET);
        }
      }
    }
  }
  free(grpname);
  free(member_name);
  free(received_addr);
  free(msg);
  

  pthread_exit(NULL);
}

问题原因与解决思路

核心原因

unserialize_tcp函数中,msg是栈上的局部字符数组,函数返回后这块栈内存会被释放。但strtok_r返回的指针直接指向msg数组内的位置,你把这些指针赋值给grpname、member_name、received_addr后,这些变量实际指向了已经失效的栈内存。

后续调用atoi(received_addr)时,atoi的函数调用会修改栈上内容(比如保存寄存器、局部变量),刚好覆盖了原来msg数组所在的栈区域,导致member_name指向的内存变成垃圾数据,输出乱码。

解决步骤

需要把strtok_r分割出的字符串复制到预先分配好的堆内存中(也就是你在gms_receive_worker里用calloc分配的grpname、member_name、received_addr),而不是直接赋值指针。修改unserialize_tcp函数如下:

void unserialize_tcp(char* message, type_t *type, char** grpname, char** member_name, char** received_addr, int* received_port) {

    char msg[TCP_PACKET] = {'\0'};
    char *saveptr;
    char *ptr = message;
    char *delim = "_";
    int i;
    for (i = 0; ptr[i]; ptr[i] == '_' ? i++ : *ptr++);
    if (i != 4) {
        printf("Message corrupted\n");
        return;
    }
    strcpy(msg, message);
    *type = atoi(strtok_r(msg, delim, &saveptr));
    
    // 复制分割结果到预先分配的内存
    char *temp_grp = strtok_r(NULL, delim, &saveptr);
    strncpy(*grpname, temp_grp, 15); // 15是因为calloc了16字节,留1字节给'\0'
    (*grpname)[15] = '\0';
    
    char *temp_member = strtok_r(NULL, delim, &saveptr);
    strncpy(*member_name, temp_member, 15);
    (*member_name)[15] = '\0';
    
    char *temp_addr = strtok_r(NULL, delim, &saveptr);
    strncpy(*received_addr, temp_addr, LENGTH_MSG - 1);
    (*received_addr)[LENGTH_MSG - 1] = '\0';
    
    *received_port = atoi(strtok_r(NULL, delim, &saveptr));
}

这样修改后,grpname、member_name、received_addr指向的是堆上的有效内存,不会因为栈内存被覆盖而变成乱码。

内容的提问来源于stack exchange,提问作者zero exp programmer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 02:59:52