使用Java建立聊天客户端
---------------siwuxie095
关于
聊天服务器,详见本人博客的分类:来一杯Java,
里面的
使用ServerSocket建立聊天服务器(二)
本人博客(任选一个)链接:
https://www.baidu.com/s?ie=UTF-8&wd=siwuxie095
将 使用ServerSocket建立聊天服务器(二) 中的 ChatSocket.java
和 ChatManager.java 略作修改:
ChatSocket.java:
package com.siwuxie095.socket; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.net.Socket; /** * run()循环执行的读取的工作,即当前的服务器会不断的从客户端读取内容 * 将读取到的内容发送到集合Vector中的所有客户端(除了自身) * * output()方法中,代码段(1)等效于代码段(2) * * @author siwux * */ //创建用于Socket通信的线程:ChatSocket public class ChatSocket extends Thread {
Socket socket;
//创建构造方法,传入Socket对象 public ChatSocket(Socket socket) { this.socket=socket; }
public
//(1) // try { // socket.getOutputStream().write((out+"\n").getBytes("UTF-8")); // } catch (UnsupportedEncodingException e) { // e.printStackTrace(); // } catch (IOException e) { // System.out.println("断开了一个客户端连接"); // ChatManager.getChatManager().remove(this); // e.printStackTrace(); // }
//(2) try { //对当前的Socket执行 //使用getOutputStream()获取输出流,通过输出流向外输出数据 //返回值是OutputStream类型,创建以接收返回值 OutputStream os=socket.getOutputStream();
//创建一个BufferedWriter作为数据的输出,传入匿名对象,指定编码,层层包装 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(os,"UTF-8")); //让BufferedWriter输出字符串 //同时加一个换行符,表示一行已完结,这样flush时才会强制输出 //如果不加,数据只会传到当前socket的缓冲区,而发不到其他的客户端的界面上 bw.write(out+"\n"); //因为带缓冲,所以需要强制输出,不然无法输出 bw.flush(); }catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { System.out.println("断开了一个客户端连接"); ChatManager.getChatManager().remove(this); e.printStackTrace(); } }
//复写run()方法 @Override public
output("你已经连接到了服务器");
try {
//对Socket的输入流进行包装 //在指定InputStreamReader时,指定编码的字符集 //有异常抛出,用 try catch 捕获 BufferedReader br=new BufferedReader( new InputStreamReader( socket.getInputStream(),"UTF-8"));
String line=null; //读取从客户端发送给服务器的数据 while ((line=br.readLine())!=null) { System.out.println(line); //通过静态方法,将自己传入,同时传入line ChatManager.getChatManager().publish(this, line); }
//关闭流 br.close();
System.out.println("断开了一个客户端连接"); ChatManager.getChatManager().remove(this);
} catch (IOException e) { System.out.println("断开了一个客户端连接"); ChatManager.getChatManager().remove(this); e.printStackTrace(); }
} } |
ChatManager.java:
package com.siwuxie095.socket; import java.util.Vector; /** * ChatManager 将不同的socket所新建的ChatSocket线程管理起来 * 由于一个聊天服务器只能有一个聊天的管理器:ChatManager * 所以要把这个类作单例化处理 * 单例化的第一步就是让这个类的构造方法变成 private 类型 * * @author siwux * */ public class ChatManager {
//让构造方法变成private类型,完成单例化的第一步 private ChatManager(){}
//为当前类创建一个实例 private static final ChatManager cm=new ChatManager();
//创建方法 getChatManager(),返回ChatManager类型 public static ChatManager getChatManager() { return cm; }
//至此,完成了这个类(ChatManager)的单例化 //接下来就是对ChatSocket线程进行管理
//创建一个Vector,指定泛型为ChatSocket Vector<ChatSocket> vector=new Vector<>();
//创建add()方法,为当前集合添加一个新的ChatSocket对象 //在创建ChatSocket时使用,即ServerListener.java中使用 public //调用Vector的add()方法,传入cs即可 vector.add(cs); }
public vector.remove(cs); }
//创建publish()方法,其中的某个ChatSocket线程可以调用publish() //向其他的客户端(其他的ChatSocket线程)发送信息 //传入线程本身和需要发送的信息 public
//因为要发送给集合Vector中的其他所有线程,要使用遍历 for (int i = 0; i < vector.size(); i++) { //获取循环中的第 i 个对象 ChatSocket csx=vector.get(i);
//当前发送信息的线程就不用再接收这条信息, //判断发送消息的对象是不是当前对象 if (!cs.equals(csx)) { csx.output(msg); } } }
} |
聊天客户端:
工程名:MyJavaChatClient
包名:com.siwuxie095.main、com.siwuxie095.view
类名:StartClient.java(主类)、ChatManagerX.java、MainWindow.java
工程结构目录如下:
StartClient.java(主类):
package com.siwuxie095.main; import java.awt.EventQueue; import com.siwuxie095.view.MainWindow; public class StartClient { public static
//将窗体MainWindow.java的主方法里的代码复制粘贴到这里(StartClient.java) //将MainWindow.java里的主方法删除,这样就可以在主程序中将窗体执行出来了 EventQueue.invokeLater(new Runnable() { public try { MainWindow frame = new MainWindow(); frame.setVisible(true); //在创建Window时传递对MainWindow的引用 ChatManagerX.getCM().setWindow(frame); } catch (Exception e) { e.printStackTrace(); } } });
} } |
ChatManagerX.java:
package com.siwuxie095.main; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; import com.siwuxie095.view.MainWindow; //需要将这个类单例化 public class ChatManagerX { //让构造方法变成private类型,完成单例化的第一步 private ChatManagerX(){}
//创建一个 private static final 修饰的实例 private static final ChatManagerX instance=new ChatManagerX();
//创建公有的 getChatManager()获取上面的私有实例 //这样,当前的ChatManager类就只会有唯一的一个实例 public static ChatManagerX getCM() { return instance; }
//现在界面中只能向ChatManager发送数据 //为了让接收到的数据放到界面上,最简便的方法即在本地引用MainWindow MainWindow window; //实现setWindow()方法 public this.window = window; //在ChatManager中操作window.appendText()在界面中显示数据 window.appendText("提示:文本框已经和ChatManager绑定了"); }
//创建一个Socket对象 Socket socket; //创建输入流 BufferedReader BufferedReader reader; //创建输出流 PrintWriter 这里不使用 BufferedWriter PrintWriter writer;
//具体实现连接服务器的操作 public
//呼叫连接时创建一个新的线程 //对网络的处理放到线程中执行 //(先建立连接,之后循环从服务器读取数据) new Thread(){
@Override public
try { socket=new Socket(ip, 12345);
//获取输入和输出流,层层包装 writer=new PrintWriter( new OutputStreamWriter( socket.getOutputStream(),"UTF-8"));
reader=new BufferedReader( new InputStreamReader( socket.getInputStream(),"UTF-8"));
//创建完输入输出流,需要循环监听当前输入流是否有数据 String line; while ((line=reader.readLine())!=null) { //通过window.appendText()将数据输出到界面中 window.appendText("收到:"+line); }
//读取数据为空,则连接结束,关闭流 writer.close(); reader.close(); //置空,更安全 writer=null; reader=null;
} catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
}.start(); }
//点击发送按钮时的操作 public //发送时,writer不为空才能发送 if (writer!=null) { //为当前的socket输出内容时,需要添加 \n 进行换行 //这时flush才能强制把这一行输出, //即这一行完结了,flush才会把它输出 // //如果不加 \n 数据实际上是传出去了, //但只是传到了当前socket的缓冲区域 //只有关闭了socket,socket才会把它强制输出,这是不可取的 //因为聊天要保证实时性,所以要加一个换行符 writer.write(out+"\n"); //flush强制刷新输出 writer.flush(); }else { //提示信息 window.appendText("提示:当前的连接已经中断..."); }
} } |
MainWindow.java:
package com.siwuxie095.view; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.border.EmptyBorder; import com.siwuxie095.main.ChatManagerX; /** * 添加 * 添加 * 选择根容器 contentPane,将布局改为 GroupLayout * * 在 JTextArea 上下方各添加一个JTextField、JButton * * @author siwux * */ public class MainWindow extends JFrame { /** * 添加默认序列版本ID * Add default serial version ID */ private static final private JPanel contentPane;
//将文本控件等声明到类当中,作为整个类的变量 JTextArea txt; private JTextField ip; private JTextField send;
/** * Create the frame. */ public MainWindow() { //添加属性 setAlwaysOnTop(true) //这样打开多个窗口都可以显示在其他应用的上方 setAlwaysOnTop(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 504, 353); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane);
txt = new JTextArea(); txt.setText("Ready...");
ip = new JTextField(); ip.setText("127.0.0.1"); ip.setColumns(10);
JButton button = new JButton("连接到服务器"); button.addMouseListener(new MouseAdapter() { @Override public //使用ChatManager的connect()和按钮的监听事件绑定 //通过ip.getText()获得需要连接的服务器的IP地址 //通过IP来建立Socket连接 ChatManagerX.getCM().connect(ip.getText());
} }); button.addActionListener(new ActionListener() { public } });
send = new JTextField(); send.setText("你好"); send.setColumns(10);
JButton btnNewButton = new JButton("发送"); btnNewButton.addMouseListener(new MouseAdapter() { @Override public //使用ChatManager的send()和按钮的监听事件绑定 //传入send文本框的文本 //第一个send()到服务器 //第二个appendText()添加到中间的显示界面 ChatManagerX.getCM().send(send.getText()); appendText("我说:"+send.getText()); //当前文本发送出去后,需要将当前的文本框清空 send.setText("");
} }); btnNewButton.addActionListener(new ActionListener() { public } });
//下面这一大段是自动生成的代码,不用管 GroupLayout gl_contentPane = new GroupLayout(contentPane); gl_contentPane.setHorizontalGroup( gl_contentPane.createParallelGroup(Alignment.LEADING) .addGroup(gl_contentPane.createSequentialGroup() .addContainerGap() .addGroup(gl_contentPane.createParallelGroup(Alignment.LEADING) .addComponent(txt, GroupLayout.DEFAULT_SIZE, 417, Short.MAX_VALUE) .addGroup(gl_contentPane.createSequentialGroup() .addComponent(send, GroupLayout.PREFERRED_SIZE, 302, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(btnNewButton, GroupLayout.DEFAULT_SIZE, 109, Short.MAX_VALUE)) .addGroup(gl_contentPane.createSequentialGroup() .addComponent(ip, GroupLayout.PREFERRED_SIZE, 321, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(button, GroupLayout.PREFERRED_SIZE, 90, Short.MAX_VALUE))) .addContainerGap()) ); gl_contentPane.setVerticalGroup( gl_contentPane.createParallelGroup(Alignment.LEADING) .addGroup(gl_contentPane.createSequentialGroup() .addGroup(gl_contentPane.createParallelGroup(Alignment.BASELINE) .addComponent(ip, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(button)) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(txt, GroupLayout.DEFAULT_SIZE, 201, Short.MAX_VALUE) .addPreferredGap(ComponentPlacement.RELATED) .addGroup(gl_contentPane.createParallelGroup(Alignment.BASELINE) .addComponent(send, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(btnNewButton))) ); contentPane.setLayout(gl_contentPane); }
//为当前类创建一个appendText()方法,传入一个String public //添加到中间的显示界面 txt.append("\n"+in); } } |
先将服务器运行:
再运行客户端:
客户端界面:
点击
连接到服务器,服务器
弹出提示:
点击
确定,客户端弹出服务器发出的消息:
再次运行另一个客户端,两个客户端之间发送消息:
此时,服务器的控制台也会打印所有经过服务器的消息:
整个流程:
两个客户端
通过 IP 和 端口 向服务器发起 Socket 通信请求,
服务器监听到请求并同意
两个客户端就是两个
Socket 对象,服务器创建两个 ChatSocket 线程,
分别处理这两个连接,且这两个线程由
ChatManager 管理
这两个
ChatSocket 线程先向两个客户端发出消息:"你已经连接
到了服务器",实际上这是服务器发送给客户端的消息
客户端之间发送消息:客户端
A 发出消息后,服务器接收到该消息,
并将其转发到客户端 B,即 服务器 相当于 中转站
如果有多个客户端,服务器依然是中转站,将一个客户端的消息转发
给其他客户端(相当于
群聊)
【made by siwuxie095】
使用Java建立聊天客户端的更多相关文章
- 使用 Java 创建聊天客户端-1
1.聊天客户端文本框的搭建. 项目截图:java project 代码: (1).ChatManager.java package com.nantian.javachatclient.main; i ...
- 使用 Java 创建聊天客户端-2
1.项目截图 java聊天核心代码: MyJavaChatClient ================================================================ ...
- Java Socket聊天室编程(二)之利用socket实现单聊聊天室
这篇文章主要介绍了Java Socket聊天室编程(二)之利用socket实现单聊聊天室的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 在上篇文章Java Socket聊天室编程(一)之 ...
- Java Socket聊天室编程(一)之利用socket实现聊天之消息推送
这篇文章主要介绍了Java Socket聊天室编程(一)之利用socket实现聊天之消息推送的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下 网上已经有很多利用socket实现聊天的例子了 ...
- 使用 ServerSocket 建立聊天服务器-1
1.代码目录 2.ChatSocket.java --------------------------------------------------------------------------- ...
- 看完这篇包你进大厂,实战即时聊天,一文说明白:聊天服务器+聊天客户端+Web管理控制台。
一.前言 说实话,写这个玩意儿是我上周刚刚产生的想法,本想写完后把代码挂上来赚点积分也不错.写完后发现这东西值得写一篇文章,授人予鱼不如授人以渔嘛(这句话是这么说的吧),顺便赚点应届学生MM的膜拜那就 ...
- 搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 (1)
搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 原文地址(英文):http://www.networkcomms.net/creating ...
- 用c#写的一个局域网聊天客户端 类似小飞鸽
用c#写的一个局域网聊天客户端 类似小飞鸽 摘自: http://www.cnblogs.com/yyl8781697/archive/2012/12/07/csharp-socket-udp.htm ...
- Socket 基础解析使用ServerSocket建立聊天服务器
很简单的教程哦! 1.socket 简介 Socket 又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求.ServerSocket 用于 ...
随机推荐
- 删除rime输入法
mac: 首先将输入法从偏好设置-键盘-输入源中去除,添加系统的输入法. 然后执行命令: $ killall Squirrel $ sudo rm -Rf "/Library/Input M ...
- Spring3.2.9 + JdbcTemplate 事务
XML新增声明式事务配置 <!-- 事务管理器 --> <bean id="transactionManager" class="org.springf ...
- mysql ORM框架及SQLAlchemy
一 介绍 SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取 ...
- 计算机_软件技巧_01_优雅地再word中插入代码
二.参考资料 1.如何优雅的在 Microsoft word中插入代码
- uva10815(set的应用)
紫书例题,这道题的例程让我长了知识.以前没有用过cctype和stringstream相关的东西.很实用,值得学习. #include <cctype>的函数 c++中应该是#includ ...
- POJ - 3150 :Cellular Automaton(特殊的矩阵,降维优化)
A cellular automaton is a collection of cells on a grid of specified shape that evolves through a nu ...
- HihoCoder1070 区间最小值(简单线段树)
个测试点(输入文件)有且仅有一组测试数据. 每组测试数据的第1行为一个整数N,意义如前文所述. 每组测试数据的第2行为N个整数,分别描述每种商品的重量,其中第i个整数表示标号为i的商品的重量weigh ...
- Unity之将Texture保存成png
http://blog.csdn.net/bingheliefeng/article/details/51177505 using UnityEngine;using System.Collectio ...
- 【转】Tomcat和Weblogic的区别
J2ee开发主要是浏览器和服务器进行交互的一种结构.逻辑都是在后台进行处理,然后再把结果传输回给浏览器.可以看出服务器在这种架构是非常重要的. 这几天接触到两种Java的web服务器,做项目用的Tom ...
- git-远程协作
远程协作相关命令 1.1. git clone 克隆远程仓库 git clone支持的协议有: 例如: git clone https://github.com/332473775/gitTestPr ...