热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaSE项目之聊天室

引子:当前,互联网体系结构的参考模型主要有两种,一种是OSI参考模型,另一种是TCPIP参考模型。一、OSI参考模型,即开放式通信系统互联参考模型(OSIRM,OpenSystem

引子:

当前,互联网 体系结构的参考模型主要有两种,一种是OSI参考模型,另一种是TCP/IP参考模型。

一、OSI参考模型,即开放式通信系统互联参考模型(OSI/RM,Open Systems Interconnection Reference Model),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。 

OSI参考模型将实现网络互连的通信协议分为7层,自上而下分别是:

第7层应用层:OSI中的最高层,为用户提供各项互联网应用,如公司老板通过浏览器上网、发送电子邮件等。 常见的协议有:HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等。

第6层表示层:相当公司中替老板写信的助理。

第5层会话层:相当于公司中收寄信、写信封与拆信封的秘书。

第4层传输层:提供终端到终端的可靠连接,相当于公司中跑邮局的送信职员。

第3层网络层: 确保信件通过一系列路由到达目的地。

第2层数据链路层: 决定访问网络介质的方式,并处理流控制。

第1层物理层:处于OSI参考模型的最底层,物理层的主要功能是利用物理传输介质为数据链路层提供物理连接,以便透明地传输比特流;该层的常用设备有网卡、集线器、中继器、调制解调器、网线、双绞线、同轴电缆等各种物理设备。

数据发送时,从第七层传到第一层,接收数据则相反。

上三层总称为“应用层”,用来控制软件方面;下四层总称为“数据流层”,用来管理硬件。除了物理层之外,其他层都是用软件实现的。

二、TCP/IP参考模型。

  ┌────------────┐┌─┬─┬─-┬─┬─-┬─┬─-┬─┬─-┬─┬─-┐
  │        ││D│F│W│F│H│G│T│I│S│U│ │
  │        ││N│I│H│T│T│O│E│R│M│S│其│
  │第四层,应用层 ││S│N│O│P│T│P│L│C│T│E│ │
  │        ││ │G│I│ │P│H│N│ │P│N│ │
  │        ││ │E│S│ │ │E│E│ │ │E│它│
  │        ││ │R│ │ │ │R│T│ │ │T│ │
  └───────------─┘└─┴─┴─-┴─┴─-┴─┴─-┴─┴─-┴─┴-─┘
  ┌───────-----─┐┌─────────-------┬──--------─────────┐
  │第三层,传输层 ││   TCP   │    UDP    │
  └───────-----─┘└────────-------─┴──────────--------─┘
  ┌───────-----─┐┌───----──┬───---─┬────────-------──┐
  │        ││     │ICMP│          │
  │第二层,网间层 ││     └──---──┘          │
  │        ││       IP            │
  └────────-----┘└────────────────────-------------─-┘
  ┌────────-----┐┌─────────-------┬──────--------─────┐
  │第一层,网络接口││ARP/RARP │    其它     │
  └────────------┘└─────────------┴─────--------──────┘

我们在对上述两种参考模型有些了解后,接下来主要看TCP和UDP。我们先来看二者的区别:

1.TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接;

2.TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付;

3.TCP面向字节流,实际上是TCP把数据看成是一连串无结构的字节流;UDP是面向报文的,它没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(如IP电话,实时视频会议等)

4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信;

5.TCP首部开销20字节;UDP的首部开销小,只有8个字节;

6.TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。

在Java中,对遵守TCP协议的类有ServerSocket和Socket,遵守UDP协议的类有DatagramSocket。我这里提供的聊天室项目,是基于TCP协议的。

该项目分为4个包,分别是utils(提供工具),ui(提供窗体界面),server(服务器子线程),client(客户端子线程)。

utils包下有两个类,分别是HostInfo和Release。HostInfo.java代码如下:

import java.net.InetAddress;
import java.net.UnknownHostException;
public abstract class HostInfo {
public static  String IP=getIP();
public static final int PORT=10086;
public static final int NUM=50;
public static final String NEW_LINE="\r\n";
private static String getIP(){
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
return null;
}
}
}

Release.java的代码如下:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public abstract class Release {
public static void release(Socket socket,BufferedWriter bw){
release(null,socket,null,bw);
}
public static void release(Socket socket,BufferedReader br){
release(null,socket,br,null);
}
public static void release(Socket socket){
release(null,socket,null,null);
}
public static void release(ServerSocket server){
release(server,null,null,null);
}
public static void release(ServerSocket server,Socket socket,BufferedReader br,BufferedWriter bw){
if(server!=null){
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bw!=null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

ui包下有两个类,分别是UIserver和UIclient。UIserver.java的代码如下:

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import 聊天室.server.Server;
import 聊天室.utils.HostInfo;
public class UIserver extends JFrame {
private JPanel jp=new JPanel();
private JLabel jl_ipTips=new JLabel("服务器ip:");
public static JTextField jtf_ip=new JTextField(HostInfo.IP);
private JLabel jl_portTips=new JLabel("服务器端口:");
public static JTextField jtf_port=new JTextField(HostInfo.PORT+"");
public static JButton bt_open=new JButton("启动服务器");
public static JTextArea jta_log=new JTextArea();
private JScrollPane jsp_log=new JScrollPane(jta_log);
public UIserver(){
jp.setLayout(new FlowLayout());
jp.add(jl_ipTips);
jp.add(jtf_ip);
jp.add(jl_portTips);
jp.add(jtf_port);
jp.add(bt_open);
jp.add(jsp_log);
jl_ipTips.setPreferredSize(new Dimension(100, 50));
jtf_ip.setPreferredSize(new Dimension(150, 50));
jl_portTips.setPreferredSize(new Dimension(100, 50));
jtf_port.setPreferredSize(new Dimension(150, 50));
bt_open.setPreferredSize(new Dimension(260, 50));
jsp_log.setPreferredSize(new Dimension(260, 192));
jta_log.setLineWrap(true);
jta_log.setEditable(false);
bt_open.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent arg0) {
new Server().start();
}
});
add(jp);
setTitle("聊天应用控制服务器");
setBounds(100, 50, 300, 400);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new UIserver();
}
}

UIclient.java的代码如下:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import 聊天室.client.Client;
public class UIclient extends JFrame {
private JPanel jp_chat=new JPanel();
public static JTextArea jta_chat=new JTextArea();
private JScrollPane jsp_chat=new JScrollPane(jta_chat);
private JPanel jp_send=new JPanel();
public static  JTextField jtf_desip=new JTextField("请输入对方Ip");
public static  JTextArea jta_message=new JTextArea();
private JScrollPane jsp_message=new JScrollPane(jta_message);
private JButton bt_send=new JButton("发送");
private Client client;
public UIclient(){
jp_chat.add(jsp_chat);
jta_chat.setLineWrap(true);
jta_chat.setEditable(false);
jsp_chat.setPreferredSize(new Dimension(550, 400));
jp_send.add(jtf_desip);
jp_send.add(jsp_message);
jta_message.setLineWrap(true);
jp_send.add(bt_send);
jtf_desip.setPreferredSize(new Dimension(100, 50));
jsp_message.setPreferredSize(new Dimension(250, 50));
bt_send.setPreferredSize(new Dimension(100, 50));
jtf_desip.addFocusListener(new FocusAdapter(){
@Override
public void focusGained(FocusEvent e) {
jtf_desip.setText("");
}
});
jta_message.addFocusListener(new FocusAdapter(){
@Override
public void focusGained(FocusEvent e) {
jta_message.setText("");
}
});
bt_send.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent arg0) {
client.send();
}
});
add(jp_chat, BorderLayout.CENTER);
add(jp_send, BorderLayout.SOUTH);
setTitle("群聊窗口");
setBounds(300, 100, 600, 500);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
client=new Client();
client.start();
}
public static void main(String[] args) {
new UIclient();
}
}

server包下有两个类,分别是Server和Transport。Server.java的代码如下:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import 聊天室.ui.UIserver;
import 聊天室.utils.HostInfo;
import 聊天室.utils.Release;
public class Server extends Thread {
private ServerSocket server;
public static List clients=new ArrayList<>();
@Override
public void run() {
try {
server=new ServerSocket(Integer.parseInt(UIserver.jtf_port.getText().trim()));
} catch (IOException e) {
Release.release(server);
throw new RuntimeException("服务器端口被占!");
}
UIserver.bt_open.setText("已启动服务器");
UIserver.bt_open.setEnabled(false);
UIserver.jta_log.append("服务器成功启动!"+HostInfo.NEW_LINE);
new Accept().start();
}
class Accept extends Thread{
private Socket socket;
@Override
public void run() {
int num=0;
while(num
try {
socket=server.accept();
} catch (IOException e) {
Release.release(socket);
throw new RuntimeException("客户端连接失败!");
}
num++;
String str="第 "+num+" 个客户端连接成功!==>"+socket.getInetAddress().getHostAddress()+" :"+socket.getPort()+HostInfo.NEW_LINE;
UIserver.jta_log.append(str);
clients.add(new Transport(socket));
}
UIserver.jta_log.append("超出服务器负荷!");
Release.release(server);
}
}
}

Transport.java的代码如下:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import 聊天室.utils.Release;
public class Transport extends Thread {
private Socket socket;
private String ip;
public Transport(Socket socket){
this.socket=socket;
this.ip=socket.getInetAddress().getHostAddress();
this.start();
}
@Override
public void run() {
BufferedReader br=null;
BufferedWriter ownbw=null;
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
ownbw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
Server.clients.remove(this);
Release.release(socket);
throw new RuntimeException("获取流失败!");
}
String str=null;
try {
while((str=br.readLine())!=null){
String[] split = str.split(":", 2);
if(split.length<=1){
ownbw.write("数据格式错误!");
ownbw.newLine();
ownbw.flush();
}
String desip=split[0];
String cOntent=split[1];
BufferedWriter desbw=null;
boolean isOnLine=false;
for(Transport des:Server.clients){
if(desip.equals(des.ip)){
isOnLine=true;
desbw=new BufferedWriter(new OutputStreamWriter(des.socket.getOutputStream()));
desbw.write(str);
desbw.newLine();
desbw.flush();
}
}
if(!isOnLine){
ownbw.write("对方不在线!");
ownbw.newLine();
ownbw.flush();
}
}
} catch (IOException e) {
Server.clients.remove(this);
Release.release(socket);
throw new RuntimeException("获取流失败!");
}
}
}

client包下有一个类Client.java,该类的代码如下:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import 聊天室.ui.UIclient;
import 聊天室.ui.UIserver;
import 聊天室.utils.HostInfo;
import 聊天室.utils.Release;
public class Client extends Thread {
private Socket socket;
@Override
public void run() {
try {
socket=new Socket(UIserver.jtf_ip.getText().toLowerCase(), Integer.parseInt(UIserver.jtf_port.getText().trim()));
}  catch (IOException e) {
Release.release(socket);
throw new RuntimeException("客户端创建失败!");
}
BufferedReader br=null;
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
Release.release(socket);
throw new RuntimeException("获取流失败!");
}
String str=null;
try {
while((str=br.readLine())!=null){
UIclient.jta_chat.append(str+HostInfo.NEW_LINE);
}
} catch (IOException e) {
Release.release(socket);
throw new RuntimeException("获取流失败!");
}
}
public void send(){
new Send().start();
}
class Send extends Thread{
@Override
public void run() {
BufferedWriter bw=null;
try {
bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
Release.release(socket,bw);
throw new RuntimeException("获取流失败!");
}
String str=UIclient.jtf_desip.getText().trim()+":"+UIclient.jta_message.getText().trim();
try {
bw.write(str);
bw.newLine();
bw.flush();
} catch (IOException e) {
Release.release(socket,bw);
throw new RuntimeException("获取流失败!");
}
}
}
}

JavaSE项目之聊天室


推荐阅读
  • 深入解析:Synchronized 关键字在 Java 中对 int 和 Integer 对象的作用与影响
    深入探讨了 `Synchronized` 关键字在 Java 中对 `int` 和 `Integer` 对象的影响。尽管初看此题似乎简单,但其实质在于理解对象的概念。根据《Java编程思想》第二章的观点,一切皆为对象。本文详细分析了 `Synchronized` 关键字在不同数据类型上的作用机制,特别是对基本数据类型 `int` 和包装类 `Integer` 的区别处理,帮助读者深入理解 Java 中的同步机制及其在多线程环境中的应用。 ... [详细]
  • 在 Android 开发中,`android:exported` 属性用于控制组件(如 Activity、Service、BroadcastReceiver 和 ContentProvider)是否可以被其他应用组件访问或与其交互。若将此属性设为 `true`,则允许外部应用调用或与之交互;反之,若设为 `false`,则仅限于同一应用内的组件进行访问。这一属性对于确保应用的安全性和隐私保护至关重要。 ... [详细]
  • 深入解析Java虚拟机的内存分区与管理机制
    Java虚拟机的内存分区与管理机制复杂且精细。其中,某些内存区域在虚拟机启动时即创建并持续存在,而另一些则随用户线程的生命周期动态创建和销毁。例如,每个线程都拥有一个独立的程序计数器,确保线程切换后能够准确恢复到之前的执行位置。这种设计不仅提高了多线程环境下的执行效率,还增强了系统的稳定性和可靠性。 ... [详细]
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • 浏览器作为我们日常不可或缺的软件工具,其背后的运作机制却鲜为人知。本文将深入探讨浏览器内核及其版本的演变历程,帮助读者更好地理解这一关键技术组件,揭示其内部运作的奥秘。 ... [详细]
  • 虚函数表指针vptr的功能测试与分析
    类的虚函数调用依赖于虚函数表来实现。虚函数表是由编译器自动生成的一段内存区域,用于存储包含虚函数的类中每个虚函数的入口地址。这些入口地址本质上是指针类型,从而使得动态绑定成为可能。本文对虚函数表指针(vptr)的功能进行了详细的测试与分析,探讨了其在多态性和继承机制中的作用及其性能影响。 ... [详细]
  • 在分析和解决 Keepalived VIP 漂移故障的过程中,我们发现主备节点配置如下:主节点 IP 为 172.16.30.31,备份节点 IP 为 172.16.30.32,虚拟 IP 为 172.16.30.10。故障表现为监控系统显示 Keepalived 主节点状态异常,导致 VIP 漂移到备份节点。通过详细检查配置文件和日志,我们发现主节点上的 Keepalived 进程未能正常运行,最终通过优化配置和重启服务解决了该问题。此外,我们还增加了健康检查机制,以提高系统的稳定性和可靠性。 ... [详细]
  • 二分查找算法详解与应用分析:本文深入探讨了二分查找算法的实现细节及其在实际问题中的应用。通过定义 `binary_search` 函数,详细介绍了算法的逻辑流程,包括初始化上下界、循环条件以及中间值的计算方法。此外,还讨论了该算法的时间复杂度和空间复杂度,并提供了多个应用场景示例,帮助读者更好地理解和掌握这一高效查找技术。 ... [详细]
  • 本指南详细介绍了如何利用华为云对象存储服务构建视频点播(VoD)平台。通过结合开源技术如Ceph、WordPress、PHP和Nginx,用户可以高效地实现数据存储、内容管理和网站搭建。主要内容涵盖华为云对象存储系统的配置步骤、性能优化及安全设置,为开发者提供全面的技术支持。 ... [详细]
  • VS2019 在创建 Windows 恢复点时出现卡顿问题及解决方法
    在使用 Visual Studio 2019 时,有时会在创建 Windows 恢复点时遇到卡顿问题。这可能是由于频繁的自动更新导致的,每次更新文件大小可能达到 1-2GB。尽管现代网络速度较快,但这些更新仍可能对系统性能产生影响。本文将探讨该问题的原因,并提供有效的解决方法,帮助用户提升开发效率。 ... [详细]
  • Unity3D 中 AsyncOperation 实现异步场景加载及进度显示优化技巧
    在Unity3D中,通过使用`AsyncOperation`可以实现高效的异步场景加载,并结合进度条显示来提升用户体验。本文详细介绍了如何利用`AsyncOperation`进行异步加载,并提供了优化技巧,包括进度条的动态更新和加载过程中的性能优化方法。此外,还探讨了如何处理加载过程中可能出现的异常情况,确保加载过程的稳定性和可靠性。 ... [详细]
  • 在Conda环境中高效配置并安装PyTorch和TensorFlow GPU版的方法如下:首先,创建一个新的Conda环境以避免与基础环境发生冲突,例如使用 `conda create -n pytorch_gpu python=3.7` 命令。接着,激活该环境,确保所有依赖项都正确安装。此外,建议在安装过程中指定CUDA版本,以确保与GPU兼容性。通过这些步骤,可以确保PyTorch和TensorFlow GPU版的顺利安装和运行。 ... [详细]
  • 在跨线程调用UI控件方法时,通常使用同步调用机制,如 `控件.Invoke(Delegate, 参数)`。这里需要声明并实现一个委托,因为控件本身并不知道如何处理跨线程操作。通过将具体的实现逻辑封装在委托中,控件可以正确地执行这些操作,确保线程安全性和UI的一致性。此外,为了提高性能和可维护性,建议对频繁的跨线程调用进行优化,例如使用异步调用或批量处理请求。 ... [详细]
  • 近日,我在处理一个复杂的前端问题时遇到了极大困扰。具体来说,我之前开发了一个功能丰富的纯jQuery代码的前端GridView控件,实现了多种功能和视觉效果,并在多个项目中表现良好。然而,最近在尝试应用 `border-box` 布局模式时,却遇到了意想不到的兼容性和性能问题。这提醒我们在条件尚未完全成熟的情况下,应谨慎使用 `border-box` 布局模式,以免引入不必要的复杂性和潜在的bug。 ... [详细]
  • 在Eclipse中提升开发效率,推荐使用Google V8插件以增强Node.js的调试体验。安装方法有两种:一是通过Eclipse Marketplace搜索并安装;二是通过“Help”菜单中的“Install New Software”,在名称栏输入“googleV8”。此插件能够显著改善调试过程中的性能和响应速度,提高开发者的生产力。 ... [详细]
author-avatar
fspygh
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有