基于P2P范型的即时聊天系统

基于P2P范型的即时聊天系统

该系统是我本科时上的分布式课的作业,当时觉得还挺有意思的,昨晚已经把它上传到GitHub了,链接在此。系统主要设计了以下几个功能:

  1. 点对点单人聊天;
  2. 多人在线同时聊天;
  3. 用户可以自由加入和退出系统;
  4. 具备用户在线状态监听功能;

系统页面设计如下:
image-11

图1.1 页面设计

一、实验目的

使用Java Socket来实现一个简单的基于P2P范型的即时聊天系统。实践开发主要涉及的技术是Java Socket编程和多线程技术。

二、实验要求

分析系统需要实现的功能,由于演示的是简单的P2P即时聊天系统,因此,我们仅仅设计了如下几个功能:点对点单人聊天、多人同时在线聊天、用户可以自由加人和退出系统、具备用户在线状态监视。采用类似于中心化拓扑结构P2P模式,所有客户都需要与中心服务器相连,并将自己的网络地址写人服务器中,服务器只需要监听和更新用户列表信息,并发送给客户最新的用户列表信息即可。当需要点对点聊天时,客户端只需要从本地用户列表中读取日标用户的网络地址,并连接目标用户,即可实现通信。因为是P2P系统,客户端要同时扮演服务器和客户端两个角色,所以,用户登录后都会创建一个接收其他用户连接的监听线程,以实现服务器的功能。其中,中心服务器和客户端需要实现的任务如下:

  1. 中心服务器的主要任务:
    1. 创建Socket、绑定地址和端口号,监听并接收客户端的连接请求。
    2. 服务器端在客户连接后自动获取客户端用户名、IP地址和端口号,并将其保存在服务器端的用户列表中,同时更新所有在线用户的客户端在线用户列表信息,以方便客户了解上下线的实时情况,以进行聊天。
    3. 当有用户下线时,服务器端要能即时监听到,并更新用户列表信息,发送给所有在线客户端。
    4. 对在线用户数量进行统计。
  2. 客户端的主要任务:
    1. 客户端创建Socket,并调用connect()函数,向中心服务器发送连接请求。
    2. 客户端在登录后必须充当服务器,以接收其他用户的连接请求,所以需要创建一个用户接收线程来监听。
    3. 用户登录后需要接收来自服务器的所有在线用户信息列表,并更新本地的用户列表信息,以方便选择特定用户进行聊天。
    4. 客户端可以使用群发功能,向在线用户列表中的所有用户发送聊天信息。

三、实验步骤

3.1 系统总体设计

中心服务器启动后会自动创建一个监听线程,以接收客户端发来的连接请求。当客户端与服务器连接后,客户端会将自己的信息(用户名、IP地址和端口号等)写人Socket,服务器端从此Socket中读取该用户信息,并登记到用户信息列表中。然后,服务器将最新的用户信息列表群发给所有在线的客户端,以便客户端得到最新的用户列表。图3.1中步骤1、2展示了客户登录服务器的过程。

image-01

图3.1 客户端与中心服务器连接过程

每个连接到中心服务器的客户都会得到最新的用户信息列表。如图3.1中步骤3所示,若客户2欲与客户3聊天,则客户2检索自己的用户信息列表,得到客户3的用户信息后,便可与客户3进行连接,实现通信。此过程并不需要中心服务器的干预。

当有一个客户需要下线时,如图3.2中的客户1,那么客户1首先将下线请求写人Socket,中心服务器接收到含有下线请求标记的信息后,客户1便通过握手机制下线(为了安全关闭Socket)。客户1安全下线后,中心服务器会将客户1的用户信息从在线列表中删除,并将更新后的用户列表、下线用户名称和当前网络的在线用户情况等群发给所有在线客户端以便客户端得到最新的在线用户列表。

image-02

图3.2 客户下线过程

3.2 函数功能设计

1)Server类和Client类

首先设计中心服务器和客户端系统界面。创建中心服务器Server类,派生自JFrame类,并创建按钮、文本框、列表等。同样,创建客户端Client类,也派生自JFrame类,并创建相应的组件。Server类和Client类都需要使用eventListener()函数,从而对界面上的按钮等动作进行监听。

按钮上有个监听函数(可以理解为一个触发器,当按下时,就触发了这个事件,实际上就是调用了这个函数)。比如服务器的登录:

//登录
userLog.addActionListener(e -> {
   try {
       if (!userNameTextFile.getText().isEmpty()) {
           login();
       }
   } catch (IOException ex) {
       setChatRecord("用户名不能为空");
   }
});

当监听到有按下动作时,就调用login()函数,login函数中会启动一个监听线程,用户监听客户的连接请求。客户的也如此。

Server类中除了包含系统界面上的一些组件成员外,还有用于维护在线用户信息的UserInfo对象、用于连接的ServerSocket对象和Socket对象,及用于Socket输人、输出流的对象。服务器生成后会进行相应的初始化,并监听服务器界面中的按钮动作,予以相应处理。

当单击“启动服务器”按钮时,会触发调用startServer()方法,该方法为服务器选定特定的端口号,并创建服务器端监听线程ServerListenThread(服务器端监听线程类ServerListenThread的一个实例),等待客户端的连接请求。同时,服务器还会创建一个线程ServerReceiveThread,用于接收客户端发来的下线请求,并将更新后的用户列表群发给所有用户。

Client类中除了包含系统界面上的一些组件成员外,还有用于设置聊天记录的函数setChatRecord()和发送聊天消息的函数sendMessage()

2)Node类

创建P2P网络结点Node类,其中包含用户名、IP地址、端口号和布尔变量上线通知、下线通知和是否刚上线。

3)RandomPort类

创建RandomPort类,用于客户端分配随机可用端口号,由网络知识可知,可用端口号要小于65535。用Random类提供的方法生成一个随机端口值后,再用此端口来初始化ServerSocket对象,以检查此端口是否可用。

4)ComWithServer类

当客户端与服务器连接后,会创建一个线程ComWithServer,用于将自己的信息发送给服务器,并获取服务器返回的最新用户列表。同时,客户端创建ClientSendThread线程,用于发送本端的聊天信息。此外,还创建了接收线程ClientReceiveThread,把自己当做服务器,接收来自其他客户端发来的信息。

5)UserInfo类

创建用户列表类Userlnfo,用于维护中心服务器端和客户端的在线用户信息。Userlnfo类中含有一个ArrayList<Node>类型的UserNodeList属性,用于保存在线用户信息,以及一些向UserNodeList中添加用户结点、删除用户结点、统计用户列表结点数、按Node.usermame检素列表和按索引检素列表等行为。

6)OnlineOfflineMessage类

Record类型,不可变。用来在类和应用程序之间传送数据,这里就是传送的Node节点信息。当服务器监听到有用户上线或者下线时,就将该用户节点信息写入服务器的 OnlineOfflineMessage类对象实例中。由于不可变性,所以服务器与其他在线客户的线程也能得到上线或者下线用户节点的正确信息。

7)ClientSendThread类和ClientReceiveThread类

在客户端要发送消息时,根据选中“在线用户”列表获取用户名,然后根据用户名在Userlnfo中搜索该用户结点(存放了该用户的ip地址和端口号),然后知道了ip地址和端口号,就可以创建连接、与另外一个客户端进行通信了。每次点击发送会创建一个线程用于客户端发送,每给一个客户发送完成后会关闭连接,给下一个客户发送再建立新的连接,给所有选中的客户发送完成后,该线程就结束。这部分的一对一通信和多人聊天本质一样,多人聊天通过遍历用户列表实现。

8)ServerListenThread类和ServerReceiveThread类

中心服务器启动后会自动创建一个监听线程,以接收客户端发来的连接请求。当客户端与服务器连接后,客户端会将自己的信息(用户名、卫地址和端口号等)写入Socket,服务器端从此Socket中读取该用户信息,并登记到用户信息列表中。然后,服务器将最新的用户信息列表群发给所有在线的客户端,以便客户端得到最新的用户列表。

四、运行结果与分析

  1. 运行ServerTest.java启动服务器端,再分别启动三个客户端,登录用户ABC并更新“在线用户列表”,如图4.1所示。

    image-41

    图4.1 连接
  2. A用户在“在线用户列表”选中用户C发送消息,如图4.2所示。

    image-42

    图4.2 单点通信
  3. B用户在“在线用户列表”选中用户AC发送消息,如图4.3所示。

    image-43

    图4.3 一对多通信
  4. 用户可以自由退出,并且服务器和剩下在线用户能够监听到用户下线消息,并且更新列表如图4.4和图4.5所示。

    image-44

    图4.4 自由退出

    image-45

    图4.5 实时监听

五、实验中遇到的问题、解决方法及体会

5.1 问题一

在实验的过程中,关于如何将用户在线列表更新并发送给所有用户,我们最终使用了定时器任务。

timer.schedule(new TimerTask() {
    @Override
    public void run() {
        StringBuilder userList = new StringBuilder();
        if (node != null && node.isOfflineInfo()) {
            userList.append("下线通知@@");
            userList.append(server.offlineMessage.node().toString());
            node.setOfflineInfo(false);
            try {
                updateInformation(userList);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else if (node != null && node.isOnlineInfo()) {
            userList.append("上线通知@@");
            userList.append(server.onlineMessage.node().toString());
            node.setOnlineInfo(false);
            try {
                updateInformation(userList);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else if (node != null && node.isJustOnline()) {
            userList.append("更新列表@@");
            userList.append(userInfo.toString());
            node.setJustOnline(false);
            try {
                updateInformation(userList);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}, date, 1000);

当新的用户上线后,为了使其他用户怎么得到这个消息,在``Node`节点中设置了三个布尔类型的变量:

private boolean isOnlineInfo;   //上线通知
private boolean isOfflineInfo;  //下线通知
private boolean isJustOnline;  //是否刚上线

举个例子:

现在有AB两个客户在线,C客户要上线了。服务器监听到连接请求,所有就新产生了一个C的用户节点以及与新用户进行通信的ServerReceiveThread线程,然后服务器通过 userInfo.setOnlineStatus(true)函数把所有在线用户的节点的 isOnlineInfo变量设置成true,同时后面应该再跟一句 node.setOnlineInfo(false)(不给自己发通知,自己需要更新列表);这样ServerReceiveThread线程就知道了有新用户上线,给在线客户发送这个用户的信息。下线通知也如此。

这是个定时器,每隔一秒就会执行一次,相当于这个定时器(定时器本身也是一个独立的线程)就看看需不需要给在线用户发送上线/下线消息,或者给刚上线的用户发送列表消息。

5.2 问题二

另外一个主要的问题就是发送消息的格式,接收到的消息怎么拆分出用户名,IP地址,端口号,或者当发送消息怎么组合在一起发送。这当中字符串的分割功不可没,就是split函数,另外由于系统中频繁的更新字符串消息,所以StringBuilder类也功不可没,用好这两个函数对本次编程中有极为重要的意义。
送列表消息。

5.3 问题三

另外UI交互方面也有些瑕疵,后面我也做了些调整,如下图所示:
image-51

图5.1 UI交互的改善
  • 0
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 基于p2p范型即时聊天系统是一种通过点对点连接实现的聊天方式。这种系统使用p2p技术,不需要经过中央服务器进行通信,直接在用户之间建立连接,实现单人聊天和多人同时在线聊天。 对于点对点单人聊天,当两个用户之间建立连接后,他们可以直接发送消息、图片、文件等,实现实时的交流。由于不需要经过服务器,点对点传输减少了通信的延迟和带宽消耗,提高了聊天的效率和速度。同时,点对点连接也增强了聊天的安全性,因为没有第三方介入,传输的内容更加私密。 而对于多人同时在线聊天,基于p2p范型即时聊天系统也提供了方便的解决方案。在这种系统中,每个用户都可以与其他在线用户直接建立连接,形成一个多人聊天网络。当有用户加入或退出聊天时,其他用户都可以得到及时的通知。这种系统的优势在于可以实现快速的实时聊天体验,并且可以自由选择和邀请其他用户进行多人聊天。同时,这种系统也支持群组聊天的功能,用户可以创建和加入不同的聊天群组,方便地与多人进行交流和分享。 总之,基于p2p范型即时聊天系统具有高效、安全的特点,可以实现点对点单人聊天和多人同时在线聊天。这种系统可以为用户提供方便的沟通交流方式,满足不同用户的需求。 ### 回答2: 基于p2p范型即时聊天系统是一种通过点对点通信,使得单人和多人可以实时进行在线聊天系统。 在这个系统中,每个用户都是地位平等的节点,可以作为发送者和接收者参与聊天。用户之间直接通过点对点连接进行消息的传输,而不需要经过中央服务器。这种分布式的通信方式不仅可以提高传输效率,还可以增加系统的可靠性和安全性。 对于点对点单人聊天,用户可以选择需要聊天的对象,并且可以通过系统提供的界面实时发送文字、图片、声音或其他表情等信息。接收者在收到消息后可以立即回复,实现双方之间的即时交流。此外,用户还可以进行多媒体文件的传输、搜索聊天历史记录等操作,以增强聊天体验。 在多人同时在线聊天方面,系统提供了群组功能,用户可以创建或加入群组,并能在群组内与其他成员进行聊天。类似于单人聊天,多人聊天也支持文字、图片等多种传输方式,并能实时显示其他成员的消息。在群组中,用户可以进行群主管理、发送群公告、邀请好友进群等操作,以满足用户群聊需求。 为了提高系统的稳定性和安全性,p2p聊天系统通常会使用加密算法对消息进行加密传输,并且采用去中心化的数据存储方式,确保用户的隐私和数据的安全。 总之,基于p2p范型即时聊天系统通过点对点通信,实现了点对点单人聊天和多人同时在线聊天的功能,为用户提供了快捷、便利的在线交流方式,并且通过加密和去中心化等技术保障了系统的稳定性和安全性。 ### 回答3: 基于p2p范型即时聊天系统,点对点单人聊天和多人同时在线聊天是其中的两个主要功能。 点对点单人聊天是指两个用户之间建立直接连接,实现一对一的实时对话。在这种模式下,信息不经过服务器中转,而是直接从发送方传输到接收方。这种方式能够提供更快的传输速度和更强的实时性,同时也减少了服务器资源的消耗。用户可以通过输入对方的ID或者扫描二维码等方式添加对方为好友,建立连接后即可开始聊天。用户可以发送文字、表情、图片、语音等多种类型的消息,丰富聊天内容。同时,为了保证安全性,可以采用加密算法来确保信息传输的安全性。 多人同时在线聊天是指多个用户可以在同一个聊天群组内进行实时对话。在这种模式下,用户可以创建聊天群组,并邀请其他用户加入。群组可以设置不同的权限,例如只有群主能够邀请成员、只能群主和管理员发送公告等。所有群组成员都能够看到其他成员发出的消息,并且可以在群组内自由交流。用户可以实时查看其他成员的在线状态,方便协调和组织群内活动或讨论。同时,为了提高用户体验,可以支持在群组内发送图片、语音、文件等多种类型的消息。 以上就是基于p2p范型即时聊天系统中点对点单人聊天和多人同时在线聊天的主要功能和特点。这种系统可以满足用户进行一对一或多对多的实时对话需求,并且能够提供较高的传输速度和实时性,同时保护用户的信息安全。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值