我需要帮助让mutex
以我想要的方式工作。我正在制作一个带有服务器和多个客户端的简单银行系统。
服务器有两个线程。一个线程侦听连接。当客户端连接到服务器时,将创建第二个线程。
客户端有3个线程。有另外两个主线程。第二个线程只接收来自服务器的消息并输出它们。第三个线程只接受输入并将它们发送到服务器。
我尝试在客户会话登录后使用mutex
。例如,如果一个客户端使用其帐户登录服务器,我想使用mutex
锁定该帐户会话。因此,如果任何其他客户端尝试登录到同一帐户,则必须等待已登录的客户端。
上述方案与我的代码完美配合。我目前的问题是多个帐户。让我们说client1登录他们的帐户并开始做事。然后client2尝试登录到与client1不同的帐户。由于来自client1帐户lock
的{{1}},它不会让client2进入。
我想这样做,如果client1登录到他们的帐户,在client1完成之前,其他任何客户端都无法登录到client1的帐户。但是,其他客户端应该能够登录到其他帐户并对其进行单独锁定。
这是我的服务器代码(curr_acc()中的mutex
):
lock
这是我的服务器的精简版本,但它应该足以理解我正在做的事情。如果你想要更多,请告诉我。
我的客户端向服务器发送#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <unistd.h>
#include <pthread.h>
// Mutex var
pthread_mutex_t lock;
int main(int argc, char *argv[])
{
pthread_mutex_init(&lock, NULL);
server_listen();
pthread_mutex_destroy(&lock);
return 0;
}
int server_listen()
{
int socket_desc, client_sock, c, *new_sock;
int portno = 8888;
struct sockaddr_in server, client;
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(portno);
bind(socket_desc, (struct sockaddr *) &server, sizeof(server))
listen(socket_desc, 5);
c = sizeof(struct sockaddr_in);
// Accept connection
while((client_sock = accept(socket_desc, (struct sockaddr *) &client, (socklen_t *) &c)))
{
// Create new thread and sock
pthread_t sniffer_thread;
new_sock = malloc(1);
*new_sock = client_sock;
pthread_create(&sniffer_thread, NULL, handle_client, (void *) new_sock)
}
}
void *handle_client(void *new_sock)
{
// Convert void
int sock = *((int *) new_sock);
// Prepare to receive and send
int read_size, index;
char *msg = malloc(10);
while(recv(sock, msg, 10, 0) > 0)
{
if(strcmp(msg, "open") == 0)
new_acc(sock);
else if(strcmp(msg, "start") == 0)
curr_acc(sock);
else
write(sock, "Invalid input", 25);
memset(msg, 0, sizeof(msg));
}
free(new_sock);
free(msg);
// Exit the thread
pthread_exit(0);
}
void curr_acc(int sock)
{
int lock_ret;
// Get account name here and check if it's in array of accounts.
// Then try below
// Try and lock the thread then go inside
lock_ret = pthread_mutex_trylock(&lock);
if(!lock_ret)
{
// Show customer menu
write(sock, "\n Welcome!", 20);
cus_menu(sock);
// Unlock the thread
lock_ret = pthread_mutex_unlock(&lock);
}
else
{
printf("Cannot open account");
write(sock, "Cannot open account. Try again or wait...", 50);
}
}
指针以检查输入。如果您需要查看客户端代码,请告诉我。但如上所述,它非常直接。
任何帮助将不胜感激。我真的想更好地理解char
。谢谢!
答案 0 :(得分:2)
什么银行?我有点担心你的一些代码可能会绕过我的钱。例如:
char *msg = malloc(10);
while(recv(sock, msg, 10, 0) > 0)
{
if(strcmp(msg, "open") == 0)
明显的安全漏洞 - 在执行msg
之前,您无法保证strcmp
为空终止。
这一行:
write(sock, "Invalid input", 25);
是完全错的。客户端应该如何解释该行发送的11个垃圾字符?如果从客户端获得无效协议,则只需终止连接。
哦,不......
new_sock = malloc(1);
*new_sock = client_sock;
您的client_sock值是写在我的银行余额之上,因为您应该说:new_sock = malloc(sizeof(socket_t))
说真的,如果您的代码在银行中运行并管理客户数据,请在部署之前由经验丰富的开发人员和安全专家进行审核。
现在,回答你原来的问题。
有很多方法可以做到这一点。数据库更适合这种情况,但我怀疑你需要的是客户端帐户和相应的互斥锁之间的映射,以及套接字和帐户之间的映射。
我实际上必须起飞,但我会在今晚晚些时候为您写完一个正确的答案,解决您管理同步访问的原始问题。继续......
<强>更新强>
好的,现在回答你原来的问题。
首先,套接字线程无限制地阻塞依赖于另一个客户端行为的互斥锁,这不是一个好主意。我可以写一个rouge客户端不断地为同一个客户端ID建立连接和请求事务。这将导致您的服务器不断启动在同一个互斥锁上阻塞的线程。更好的方法是考虑“注销”旧的客户端连接,以便新连接可以继续。或者,如果您想要非常健壮,可以考虑允许同一帐户的多个客户端登录 - 然后拥有支持线程安全事务操作的数据库(或对象模型)。
现在,如果您想为每个客户帐户使用互斥锁,我建议使用此模式。首先定义这个结构:
struct ClientAccount
{
int account_number; // could be any type, I chose "int" for brevity
pthread_mutex_t mutex; // the mutex that guards access to this client account
};
在我们的简单示例中,我将说明ClientAccount实例总是被指针引用的声明,并且一旦创建了ClientAccount实例,它就永远不会消失。这将使以下示例更简单。
然后有一个ClientAccount实例的全局表。可以是数组或链表,但最好是可以增长的哈希表。但无论如何,这个包含所有ClientAccount实例的表都有自己的互斥锁。保持简单,它看起来像这样:
struct ClientAccountTable
{
ClientAccount* accounts[MAX_NUMBER_OF_ACCOUNTS]; // an array of pointers - not an array of instances!
int accounts_size; // number of items in accounts
};
然后是具有全局互斥的全局实例;
ClientAccountTable g_account_table;
pthread_mutex_t g_table_mutex; // guards access to g_account_table;
您必须在此表上定义操作,但每个人都会在对表执行操作之前获取表的互斥锁。例如:
ClientAccount* find_or_create_account(int account_number)
{
ClientAccount* account = NULL;
// acquire the global lock before accessing the global table
pthread_mutex_lock(&g_table_mutex);
for (int i = 0; i < g_account_table; i++)
{
if (account_number == g_account_table.accounts[i].account_number)
{
account = g_account_table.accounts[i];
}
}
if (account == NULL)
{
// not found in the table, so create a new one
pAccount = malloc(sizeof(ClientAccount));
g_account_table.accounts[g_account_table.accounts_size] = account;
g_account_table.accounts_size++;
}
// release the lock
pthread_mutex_unlock(&g_table_mutex);
return account;
}
然后,在客户端识别出要使用的帐户后,服务器上的每个线程都可以执行此操作:
void* handle_client(void* )
{
// socket code to read client's account number
ClientAccount* account = find_or_create_account(account_number);
// lock the mutex for this client account
pthread_mutex_lock(&account->mutex);
// not shown: socket code and the code to do transactions for this account
// unlock the account's mutex after this client session is done
pthread_mutex_unlock(&account->mutex);
}
您还可以尝试使用pthread_mutex_trylock
代替pthread_mutex_lock
。这样,如果锁定获取失败,您可以告诉远程客户端服务器正在为另一个客户端提供服务。