多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread


文章目录

    • 1.介绍
    • 2.功能
      • 2.1 注册
      • 2.2 聊天
      • 2.3 注销
      • 2.4 离线聊天
      • 2.5 频道-群组聊天
    • 3. 实现展示

1.介绍 多客户端聊天服务器,可以在一个channel聊天。
当用户想要加入聊天室时,服务器会询问用户名,并且仅当用户名是唯一时才接受连接。然后,它将消息从一个客户端广播到连接的所有其他客户端。还通知任何客户端的进入/退出
2.功能 完整的聊天应用程序可以大致分为以下五个功能。每个功能都涉及客户端部分或服务器部分或两者的组合。服务器和客户端中的四个功能及其各自的部分将在以下部分中进行说明。
2.1 注册
对于注册功能,服务器必须接受来自客户端的注册或订阅请求。服务器需要在客户端之前启动。服务器维护一个包含所有客户端的 IP 地址、端口号和昵称的表。此功能涉及客户端和服务器模式。
客户端模式: ? 客户端必须使用服务器的IP 地址和端口号与服务器通信[假设所有客户端默认都知道服务器信息]。
$ ChatApp :为服务器和客户端启动程序(例如:ChatApp -c 用于客户端,ChatApp -s 用于服务器)。
服务器模式有一个参数:它的监听端口。
客户端模式应该有四个参数:客户端名称、服务器 IP 地址、服务器的监听端口号和客户端的监听端口号。
$ ChatApp -s : 启动服务器进程
$ ChatApp -c : 启动客户端与服务器的通信。
客户端名称就像此聊天客户端的用户名。服务器 IP 地址应以十进制格式给出,端口号应为 1024-65535 范围内的整数值。例如,如果服务器IP为198.123.75.45,服务器端口为1024,客户端监听的端口号为2000,则命令为:
$ ChatApp -c client-name 198.123.75.45 1024 2000。
如果带参数以正确的格式,应该显示类似“>>>”的提示。应用程序还应该能够执行基本的错误检查,其中 IP 地址是有效数字,并且分配的端口在范围内。否则,应显示适当的错误消息。
  • 在服务器上成功注册的客户端也应该向客户端显示状态信息:
    $>> [Welcome, You are registered.]
  • 每个客户端也应该维护一个包含所有其他客户端信息的本地表(名称、IP、端口号、在线状态)。当服务器发送所有其他客户的信息时,每个客户都应该更新(覆盖)其本地表(在接下来的章节中进一步详细说明)。
  • 当表被成功更新时,客户端应该显示信息:
    $ >>> [[Client table updated] 应该有两种方法来 "断开/关闭 "作为一个客户端。
  • silent leave。一旦客户端断开/关闭,服务器将不会被通知。你可以预期,客户端通过Silent leave退出后,不会再使用相同的信息进行注册。要退出或关闭,客户端使用
    $ >>> ctrl + c 或简单地关闭客户端正在运行的SSH窗口(这两个动作都需要实现,而且系统不应该崩溃)。
  • 通知离开。取消客户端的注册,取消注册的动作将被通知到服务器。服务器表中的客户端状态应该被改为离线。更详细的信息在2.3中涉及。
    服务器模式:
  • 服务器进程应该维护一个表来保存所有客户端的名称、IP 地址和端口号。
  • 当客户端发送注册请求时,应将客户端信息(名称、IP 地址、端口号、在线状态)添加到表中。
  • 服务器应向所有在线客户端广播完整的活动客户端表,以便它们可以更新其本地信息。每当服务器更新其表时,都会发生这种情况。
2.2 聊天
一旦客户端设置完毕并在服务器上注册,下一步就是实现实际的聊天功能。 客户端应该直接相互沟通,而不能使用服务器来转发聊天信息。因为它不涉及服务器,所以只有客户端部分的聊天功能。
客户端。
  • 一个客户端应该用其本地表的信息与另一个客户端通信(包括与自己通信)。
    客户端应该支持以下发送消息的命令
    $ >>> send : 这个命令应该使客户端从其本地表中查找接收客户端的IP地址和端口号,并将消息发送到相应的客户端(消息长度应该是可变的)。
  • 发送消息的客户端必须等待一个ack,同样,接收消息的客户端在收到消息后也必须发送一个ack。
  • 如果发送至另一个客户端的消息的ack超时(500毫秒),这意味着接收端的客户端是离线的,因此消息必须被发送到服务器上。服务器必须保存这些信息,并在以后当他们重新上线和重新注册时向适当的客户显示这些信息(详情见离线聊天部分)。 适当的状态信息也需要在每种情况下显示:
    \ $>>> [Message received by .]
    $ >>> [No ACK from , message sent to server.]
2.3 注销
这是一个记账功能,用于跟踪活跃的客户。这个功能涉及客户和服务器两部分。
服务器。
  • 当服务器收到来自客户端的取消注册请求时,它必须在表中把相应的客户端的状态改为脱机(不要关闭或退出客户端把它的状态改为脱机)。
  • 然后,它必须将更新的表广播给所有活跃的(在线)客户。
  • 然后,服务器必须向要求取消注册的客户发送一个ack。
客户端。
当一个客户要下线时,它必须向服务器发送一个取消注册的请求,宣布它要下线了。 - 客户端必须在500毫秒内等待服务器的应答。如果它没有收到确认,客户端应该重试5次。如果它五次都失败了,客户端应该显示消息。
$>> [Server not responding]
$>> [Exiting] 并退出。
  • 所有其他活动的客户端,当他们从服务器收到表时,应该更新他们各自的本地表(只是覆盖现有的表)。
    $ >>> dereg : 这是客户端向服务器发出的取消注册的请求,以便下线。 请注意:你不应该在取消注册后关闭这个客户端的SSH窗口。你将被期望在以后重新注册该客户端以接收离线信息(如果有的话)。你不需要考虑这样的情况:当客户端被取消注册时,另一个客户端使用相同的信息进行注册。
  • 从服务器上成功取消注册后,应该在客户端显示以下状态信息:
    $ >>> [[You are Offline. Bye.]
2.4 离线聊天
聊天应用程序的另一个功能是实现离线聊天。当客户端离线时,服务器会记录客户端从其他客户端收到的聊天信息,并在以后客户端重新上线时提供这些信息。以类似的方式,当客户端退出聊天会话时,服务器应保存离线聊天信息。这有客户端和服务器两部分。
客户端。 客户端在两种情况下发送脱机信息。
  • 当收件人在其本地的客户表中处于离线状态。(通过通知离开退出–第2.1节)
  • 当发送至客户端的消息出现超时。(通过沉默离开–第2.1节或客户之间潜在的连接不良退出)
    在上面给出的两种情况下,客户端都要向服务器发送一个自动保存消息的请求。这个请求还应该包括
  • 预期收件人的姓名
  • Message
    如果成功,客户端应显示以下状态信息:
    $ >>> [服务器收到的信息并保存]
    退出的客户端应能用以下方式重新登录:
    $ >>> reg : 指示服务器登录或注册该客户端(即在表中将相关客户端的状态改为在线)。
服务器。
  • 当服务器收到离线信息时,它必须为不同的客户端分别保存它。(例如,你可以为每个客户使用文件,将一个客户的所有离线信息保存在其相应的文件中)。
  • 当服务器收到来自客户端的保存消息请求时,它必须检查预定收件人的状态。
  • 如果接收者的客户端仍然处于活动状态,那么向发送保存消息请求的客户端发送一条错误信息:
    $ >> [客户端存在!!] 同时向客户端发送表格,使其得到更新。
  • 如果收件人的客户端不活跃,那么服务器应该把相应的客户端的状态改为离线,把更新的表广播给所有活跃的客户端,并把消息保存在与收件人相关的文件中。
  • 保存的信息也应该有其相关的时间戳信息。(你可以用gettimeofday()得到这个信息)。
  • 还需要向提出保存消息请求的客户端发送一个ack。 当一个已注销的客户返回时。
  • 服务器需要检查该客户是否有离线信息:
  • 如果有
    • 将所有的离线信息发送给客户
    • 在服务器中清除它们
    • 将客户的状态改为在线
    • 将该表广播给所有在线客户。
  • 如果没有
    • 将客户的状态改为在线
    • 将表广播给所有的客户。
  • 清除服务器中的信息可以确保服务器不会重复发送相同的离线信息。在离线消息显示之前,这个状态消息也应该在客户端显示:
    $ >> [You have messages]
    例如。
    假设:
  • 有三个客户端
    - 客户端1脱机
    - 其他两个客户端向客户端1发送消息
    客户端1在服务器中的离线信息应保存为:
    >>客户端2:Hi!
    >> 客户端3:Hello!!
    当客户端1返回时(重新登录),这应该在客户端1中打印出来
    客户端1 :
    >> You Have Messages
    >> 客户端2: 嗨!
    >> 客户端3:你好!!
2.5 频道-群组聊天
一旦客户端在服务器上注册,他们应该被添加到一个频道,在那里他们可以与所有其他客户端和服务器通信。发送到这个频道的信息应该被广播给所有在线的客户端,除了发送者客户端本身。对于离线的客户端,信息应该被保存起来,一旦他们重新登录,就会被发送给客户端。为了使实现更容易,客户端可以使用服务器来转发通道中的消息。
客户端。
  • 在注册时,客户端应该被添加到一个预定义的频道。为了简化实现,你可以假设所有的客户都是这个通道的成员,而不需要额外的通道注册过程 - 使用下面的命令向通道发送消息: :
    $ >> [send_all ] 这个命令应该把消息发送到服务器,以便分发给客户(同样,消息长度应该是可变的)。
  • 发送消息的客户端必须在500毫秒内等待服务器的应答。一旦收到服务器的应答,也需要显示适当的状态信息:
    $ >>> [[Message received by Server.]]
  • 如果客户端(发送者)在时限内没有收到服务器的应答,客户端应该重试五次。如果它五次都失败了,客户端应该显示消息。
  • \ $ >>> [Server not responding.]
    • 从服务器收到通道消息的客户端,应该向服务器发送一个ack,客户端应该显示收到的消息。
      $ >> [Channel_Message : message ] 。 注意:'通道消息’应该是一个硬编码的字符串
服务器。
  • 在收到来自客户端的通道消息后,服务器应该向发送方客户端发送一个ack。
  • 服务器可以使用同一个表来向所有活动的(在线的)客户端广播消息,因为所有的客户端都自动成为这个预定义通道的成员,不需要额外的通道注册过程。
  • 服务器也应该期待来自所有活动(在线)客户端(除了发送方客户端)的ack,服务器最多可以等待500毫秒来接收所有客户端的ack。
  • 如果服务器在时限内没有收到客户端的ack响应,服务器应该检查预期收件人的状态(在离线聊天中提到)。
  • 如果接收的客户端不活跃,那么服务器应该将相应的客户端的状态改为离线,将更新的表格广播给所有活跃的客户端,并将消息保存在相关的文件中(在离线聊天中提到)
  • 对于离线客户端,消息应该与离线聊天保存在同一个文件中。消息也应与相关的时间戳信息一起保存(在离线聊天中提到),并且Channel-Message应附在消息的开头,作为群聊消息的指示。
  • 当客户返回时,服务器需要检查任何离线消息,并根据收到的时间将其发送给客户。 例如。
    假设:
  • 有三个客户端
  • 客户端1离线
  • 客户端2发送频道消息:Welcome to Channel
  • 客户端2发送消息给客户端1:Hello
  • 客户端3发送消息给客户端1:Hello!
    client 1 在服务器中的离线消息应保存为
    >>> Channel-Message client 1: Welcome to Channel
    >>> client 2: Hello
    >>> client 3: Hello!!
    当Client 1 返回(重新登录)时,应在Client 1 中打印
    Client 1 :
    >>> You Have Messages
    >>> Channel Message client 1: Welcome to Channel
    >>> client 2: Hello
    >>> client 3: Hello!!
    Client 2 应显示的消息:(仅显示来自 SSH 的输出,示例中不包括客户端输入):
    Client 2 :
    >>> Message received by server.
    >>> No ACK from client 1, message sent to server.
    >>> Messages received by the server and saved.
    Client 3 应显示的消息:(仅显示来自 SSH 的输出,示例中不包括Client输入):
    Client 3 :
    >>> Channel Message client 2: Welcome to Channel
    >>> No ACK from client 1, message sent to server.
    >>> Messages received by the server and saved.
3. 实现展示 使用方法,打开四个Anaconda prompt窗口,或者其余的linux or Dos窗口
其中一个是Server,另外三个是client 1,2,3
命令为:python udpServer(1)Modified.py
以及:python udpClient(1)Modified.py 客户名
(1)要先打开Server,然后client1,2,3登录
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

(2)本代码重点采用“showall”命令进行交互
client1: Max uses “showall” command
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

client2: Logan uses “showall” command
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

Max的客户端套接字仅显示 2个,是因为没有自动更新,他看到的是其他 2 个客户端的日志记录消息。这意味着由于缺乏自动更新而存在延迟
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

为了更新客户端消息,我们将使用"showall"函数,其目的也是显示所有活跃用户
(3)聊天
client1 Max 找client2 Logan聊天,只需输入send Logan : 聊天内容
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

由于自动更新有延迟,我们需要使用showall命令更新这个聊天内容,内容就会从服务器Server发送到对应的client
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

同理,client 2 Logan 也需要showall 进行更新并同时可以看到信息
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

Logan再给Max回信息
后面截图showall就不再截图(毕竟重复了)
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

Max看到Logan回信
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

但此时client3 Naoi却是什么都看不到
(4)退出(注销) : Offline
client1 给 client2 发送一则信息后就 exit
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

此时,client2 和client3 能看到,Max下线了
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

【多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread】在client2也“exit”后,服务器会显示丢失的连接,因为它无法接收已注销用户的消息,并且找不到用户,因为他处于非活动状态
多线程|简单的聊天应用程序(多客户端聊天服务器) from multithread
文章图片

以上主要是私聊的展示,群里和离线聊天等功能在代码中有详解
代码

    推荐阅读