(C)在多线程客户端和服务器中使用互斥锁

时间:2015-12-05 02:30:08

标签: c multithreading server pthreads mutex

我需要帮助让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。谢谢!

1 个答案:

答案 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。这样,如果锁定获取失败,您可以告诉远程客户端服务器正在为另一个客户端提供服务。