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
相关产品推荐
相关产品推荐

