C/C++ strtok()是线程不安全

最近发生一题,通过使用hwasan发现了一个问题,指示代码使用了非法内存。通过代码和dump一直查不到原因,问题指示的是使用的内存被释放了。函数传入参数后,马上使用按asprinf copy了一份,之后进行处理。而外部内存释放的地方也是改函数执行后,才会被free。log显示asprintf的函数copy的string没有问题,问题点在一个循环中,循环有处理前面asprintf,在某次处理中才出现的问题。这题非常奇怪,请了很多人帮忙看,一直都没有看出问题原因,最后排查了所有可能的问题,最后值落到了strtok函数,看了网上的一些介绍和linux的说明,并且查找到strtok的实现,最终认定问题点就是这个函数了。
strtok()的功能和使用网上有很多,这里就不介绍了。strtok的实现请看下面的。strtok线程不安全,原因就是函数实现使用了一个static的变量导致的。在多线程中,如果两个线程都使用了strtok的话,这个变量的值就会被另一个线程不预期的进行修改。

#include <string.h>
#include <sys/types.h>
 
static char *___strtok = NULL;

char *
strtok(char *s, char const *ct)
{
   
    char *sbegin, *send;
 
    sbegin  = s ? s 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现 TCP 服务器/客户端的多人聊天室,需要按照以下步骤进行: 1. 设计数据结构:需要设计用户信息结构体,保存用户的账号、密码等信息。 2. 编写服务器端代码:服务器端代码需要实现以下功能: - 读取用户信息文件,保存到内存中。 - 等待客户端连接,接受客户端连接请求。 - 接收客户端发送的数据,处理聊天室的消息。 - 将聊天室消息广播给所有连接的客户端。 3. 编写客户端代码:客户端代码需要实现以下功能: - 注册新用户,向服务器发送注册信息。 - 登录聊天室,向服务器发送登录信息。 - 发送聊天消息,向服务器发送聊天消息。 - 接收聊天消息,显示在客户端界面上。 以下是 Windows 下实现 C/C++ TCP 服务器/客户端的多人聊天室的示例代码: 服务器端代码: ```c++ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #define PORT 8888 #define MAX_CLIENTS 30 #define MAX_USERNAME_LEN 20 #define MAX_PASSWORD_LEN 20 #define MAX_MSG_LEN 1024 // 用户信息结构体 typedef struct { char username[MAX_USERNAME_LEN]; char password[MAX_PASSWORD_LEN]; } user_info_t; // 全局变量 SOCKET clients[MAX_CLIENTS]; // 客户端列表 int num_clients = 0; // 当前连接的客户端数量 user_info_t users[MAX_CLIENTS]; // 用户信息 int num_users = 0; // 当前注册的用户数量 // 读取用户信息文件 void read_user_info_file() { FILE* fp = fopen("user_info.txt", "r"); if (fp == NULL) { printf("Failed to open user info file.\n"); exit(1); } while (fscanf(fp, "%s %s", users[num_users].username, users[num_users].password) != EOF) { num_users++; } fclose(fp); } // 处理客户端连接 void handle_client(SOCKET client_socket) { printf("Client connected.\n"); clients[num_clients++] = client_socket; // 接收客户端发送的消息 char msg[MAX_MSG_LEN]; int recv_size; while ((recv_size = recv(client_socket, msg, MAX_MSG_LEN, 0)) > 0) { // 处理注册请求 if (strncmp(msg, "register ", 9) == 0) { // 解析用户名和密码 char* username = strtok(msg + 9, " "); char* password = strtok(NULL, " "); if (username != NULL && password != NULL) { // 检查用户名是否已经存在 int i; for (i = 0; i < num_users; i++) { if (strcmp(users[i].username, username) == 0) { send(client_socket, "Username already exists.", strlen("Username already exists."), 0); break; } } if (i == num_users) { // 添加新用户 strcpy(users[num_users].username, username); strcpy(users[num_users].password, password); num_users++; send(client_socket, "Registration successful.", strlen("Registration successful."), 0); } } } // 处理登录请求 else if (strncmp(msg, "login ", 6) == 0) { // 解析用户名和密码 char* username = strtok(msg + 6, " "); char* password = strtok(NULL, " "); if (username != NULL && password != NULL) { // 检查用户名和密码是否匹配 int i; for (i = 0; i < num_users; i++) { if (strcmp(users[i].username, username) == 0 && strcmp(users[i].password, password) == 0) { send(client_socket, "Login successful.", strlen("Login successful."), 0); break; } } if (i == num_users) { send(client_socket, "Incorrect username or password.", strlen("Incorrect username or password."), 0); } } } // 处理聊天消息 else { // 广播聊天消息给所有客户端 int i; for (i = 0; i < num_clients; i++) { if (clients[i] != client_socket) { send(clients[i], msg, recv_size, 0); } } } } // 客户端断开连接 printf("Client disconnected.\n"); int i; for (i = 0; i < num_clients; i++) { if (clients[i] == client_socket) { while (i < num_clients - 1) { clients[i] = clients[i + 1]; i++; } num_clients--; break; } } closesocket(client_socket); } int main() { // 初始化 Winsock 库 WSADATA wsa; if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { printf("Failed to initialize Winsock.\n"); return 1; } // 创建监听 socket SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0); if (listen_socket == INVALID_SOCKET) { printf("Failed to create listen socket.\n"); return 1; } // 绑定监听 socket sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT); if (bind(listen_socket, (sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) { printf("Failed to bind listen socket.\n"); return 1; } // 开始监听连接请求 listen(listen_socket, 5); printf("Server started.\n"); // 读取用户信息文件 read_user_info_file(); // 处理客户端连接 sockaddr_in client_addr; int client_addr_len = sizeof(client_addr); while (1) { SOCKET client_socket = accept(listen_socket, (sockaddr*)&client_addr, &client_addr_len); if (client_socket == INVALID_SOCKET) { printf("Failed to accept client connection.\n"); break; } handle_client(client_socket); } // 关闭监听 socket closesocket(listen_socket); // 清理 Winsock 库 WSACleanup(); return 0; } ``` 客户端代码: ```c++ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #define PORT 8888 #define MAX_MSG_LEN 1024 // 发送消息 void send_msg(SOCKET socket, const char* msg) { send(socket, msg, strlen(msg), 0); } // 接收消息 void recv_msg(SOCKET socket, char* msg) { int recv_size = recv(socket, msg, MAX_MSG_LEN, 0); msg[recv_size] = '\0'; } int main() { // 初始化 Winsock 库 WSADATA wsa; if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { printf("Failed to initialize Winsock.\n"); return 1; } // 创建 socket SOCKET socket_client = socket(AF_INET, SOCK_STREAM, 0); if (socket_client == INVALID_SOCKET) { printf("Failed to create socket.\n"); return 1; } // 连接服务器 sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); server_addr.sin_port = htons(PORT); if (connect(socket_client, (sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) { printf("Failed to connect to server.\n"); return 1; } // 登录聊天室 char username[20], password[20], msg[MAX_MSG_LEN]; printf("Enter your username:\n"); scanf("%s", username); printf("Enter your password:\n"); scanf("%s", password); sprintf(msg, "login %s %s", username, password); send_msg(socket_client, msg); recv_msg(socket_client, msg); printf("%s\n", msg); // 发送和接收聊天消息 fd_set read_fds; FD_ZERO(&read_fds); FD_SET(socket_client, &read_fds); FD_SET(STDIN_FILENO, &read_fds); while (1) { fd_set tmp_fds = read_fds; int ret = select(0, &tmp_fds, NULL, NULL, NULL); if (ret < 0) { printf("Failed to select.\n"); break; } if (FD_ISSET(socket_client, &tmp_fds)) { // 接收聊天消息 recv_msg(socket_client, msg); printf("%s\n", msg); } if (FD_ISSET(STDIN_FILENO, &tmp_fds)) { // 发送聊天消息 scanf("%s", msg); send_msg(socket_client, msg); } } // 关闭 socket closesocket(socket_client); // 清理 Winsock 库 WSACleanup(); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值