【问题标题】:Threads in a client/server program客户端/服务器程序中的线程
【发布时间】:2010-05-22 13:23:43
【问题描述】:

对于大学,我正在开发一个本地中继聊天。我必须编写一个聊天服务器和客户端,它们只能在同一台计算机上的不同终端窗口上使用线程和 FIFO 发送消息。

FIFO 部分我没有问题,线程部分是让我有些头疼的部分。

服务器有一个线程用于接收来自 FIFO(由所有客户端使用)的命令,另一个线程用于连接的每个客户端。

对于每个连接的客户端,我需要知道某些信息。首先,我使用全局变量,只要只有一个客户端连接就可以工作,这很像聊天,可以单独聊天。

所以,理想情况下,我会有一些数据,例如:

-昵称
-名称
-电子邮件
-等等...

每个连接的客户端。但是,我不知道该怎么做。

我可以创建一个 client_data[MAX_NUMBER_OF_THREADS] 其中 client_data 是一个结构,其中包含我需要访问的所有内容,但这需要在服务器和客户端之间的每次通信中询问数组 client_data 中的客户端 ID这似乎不太实用

我也可以在创建线程后立即实例化 client_data,但它只能在该块中可用,这也不是很实用。

如您所见,我需要一些指导。非常感谢任何评论、代码或任何相关信息的链接。

【问题讨论】:

    标签: c multithreading client-server


    【解决方案1】:

    我不知道您使用的是什么语言,但这里有一些基本的想法:

    • 在一个线程中启动您的服务器(可能是您的主线程)。
    • 服务器的 while 循环将阻止接受套接字连接。
    • 当套接字连接被接受时,它应该产生一个新线程来处理连接。
    • 在新线程中开始与客户端通信。

    一个简单的套接字服务器循环如下所示(在 Java 中):

    while(true){
        ClientWorker w;
        try{
          //server.accept returns a client connection
          w = new ClientWorker(server.accept(), textArea);
          Thread t = new Thread(w);
          t.start();
        } catch (IOException e) {
          // log the exception or something...  
        }
      }
    

    如果您想知道它的作用 - ClientWorker 可用 here。在 C# 中,如果您正在创建 new Thread,请不要忘记将其 IsBackground 属性设置为 true,以便在您的应用关闭时线程关闭,即没有挂起的线程。

    记住:接受套接字连接或从套接字接收数据通常是一个阻塞调用,这意味着您的线程将阻塞,直到有人连接到套接字或数据通过插座。

    在 C# 中:

    1. 聊天客户端:http://www.geekpedia.com/tutorial239_Csharp-Chat-Part-1---Building-the-Chat-Client.html
    2. 聊天服务器:http://www.geekpedia.com/tutorial240_Csharp-Chat-Part-2---Building-the-Chat-Server.html
    3. 基本客户端/服务器:http://www.dreamincode.net/forums/topic/33396-basic-clientserver-chat-application-in-c%23/

    在 Java 中:

    1. 聊天客户端/服务器:http://pirate.shu.edu/~wachsmut/Teaching/CSAS2214/Virtual/Lectures/chat-client-server.html
    2. Nakov 聊天客户端/服务器:http://inetjava.sourceforge.net/lectures/part1_sockets/InetJava-1.9-Chat-Client-Server-Example.html

    在 C++ 中

    1. 代码项目:http://www.codeproject.com/KB/cpp/chat_client_server.aspx
    2. 另一个代码项目 TCP/IP 聊天客户端/服务器:http://www.codeproject.com/KB/IP/ipchat.aspx

    更新

    不用做全局变量,只需为客户账户定义一个struct,并为每个用户声明一个账户变量……下面是定义账户信息的方法:

    struct account {
       char nickname[32];
       char first_name[32];
       char last_name[32];
       char e_mail[32];
       char password[32];
    };
    

    当客户端发送消息时,它应该具有标准格式:FROM|TO|CONTENT

    struct message{
       char nickname_from[32];
       char nickname_to[32]; // optional
       char msg_content[256];
    };
    

    将每个message 放在fifo [队列] 上,您将获得识别发送者所需的所有信息。

    【讨论】:

    • 感谢您的回复,但我必须在没有套接字的情况下实现这一点。只是fifos和线程。这是一个本地中继聊天。
    • @nunos,这与阻塞 i/o 的概念相同:不是阻塞套接字,而是阻塞从您正在阅读的任何地方的读取。您仍然需要为每个“已连接”的客户端创建一个新线程,并建立一个与客户端通信的专用 i/o 通道。
    • Thread-per-client 显然不能扩展,所以如果你想要一个非常健壮的服务器,你可以使用非阻塞 IO 和 select() 来监控来自单个线程的多个套接字。但是对于一些简单的事情,每个客户端线程就可以了。
    【解决方案2】:

    这是一些实际上可能几乎可以运行的伪代码。请注意,我没有释放我分配的内容,也没有检查错误,我只是想演示如何将结构传递给线程并使用简单的互斥锁。

    有一点需要注意,线程的函数指针指定了一个void * 参数,它可以是字面上的任何 种类型。在线程中,我们假设将线程参数转换为我们定义的类型是安全的。如果您要传递多种可能的类型,则必须小心:)

    我不太确定您的程序的结构,或者您如何处理线程,但这里有一个与方法无关的简短示例,说明如何将数据传递给它们:

    typedef struct client {
        char *firstname;
        char *lastname;
        char *email;
        char *nickname
    } client_t;
    
    
    pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
    
    void *client_thread(void *threadarg)
    {
        client_t *client = (client_t *) threadarg;
    
        pthread_mutex_lock(&client_lock);
        /* read and write to client structure */
        pthread_mutex_unlock(&client_lock);
    
        /* do some other stuff */
        pthread_exit(NULL);
    }
    
    int main(void)
    {
        client_t *client;
        pthread_t cthread;
    
        client = (client_t *)malloc(sizeof(struct client));
        if (client == NULL)
          return 1;
    
        client->firstname = strdup("Joe Public");
        /* set the rest of the data */
    
        pthread_create(&cthread, NULL, (void *)client_thread, (void *)client);
        /* join / detach / etc */
    
        /* Free all the structure members and the structure itself */
    
       return 0;
    }
    

    我很确定这就是你要问的?

    【讨论】:

    • 你和 Lirik 的回应让我对如何处理这个问题有了一些了解。我现在有了开始实现线程的想法。谢谢。