功能需求:
1 每运行一次主函数,创建一个客户端聊天界面;
2 客户端界面分三块,公屏(显示所有客户端发送的信息),私屏(用于输入个人想要发送的信息),发送按钮(点击一次,将客户端信息发送到服务端)
3 进入客户端时公屏提示 欢迎xxx来到多人聊天室,关闭客户端时提示 xxx已离开聊天室
4 客户端展示在文末,源文件地址也在文末,可以直接转尾部
============================================================================
所需知识:
1 IO流基础知识,用于读写发送的信息
2 Jframe jpane,用于创建图形界面
3 socket基础知识,用于创建客户端,服务端,互相传递信息
4 线程基础知识
5 计时器基础知识
======================================================================
主要思路:
1 创建客户端界面,
2 在界面中创建客户端,每点击一次,就发送信息到服务端
3 服务端一直开启,持续接受客户端信息.然后将接受到的信息发送到所有客户端
4 客户端将服务器发送的信息显示在公屏
===========================================================================
代码如下:
第一步:画客户端外边框
/*** @Title: chatJFrame.java
* @Package view
* @Description:
*@authorMr.Yao
* @date 2019年8月28日下午4:53:47
*@versionV1.0*/
packagechatview;importjava.awt.Dimension;importjava.awt.Toolkit;importjava.util.Scanner;importjavax.swing.JFrame;/*** @ClassName: chatJFrame
* @Description: 继承JFrame,画聊天界面外边框
*@authorMr.Yao
* @date 2019年8月28日下午4:53:47
**/
public class ChatJFrame extendsJFrame {//重写构造方法
publicChatJFrame() {//输出让用户输入用户名,作为之后客户端的名字
System.out.println("请输入用户名");
Scanner input=newScanner(System.in);
String name=input.next();//设置聊天室的名称
this.setTitle(name);//获取屏幕
Toolkit dt =Toolkit.getDefaultToolkit();
Dimension ss=dt.getScreenSize();//获取屏幕宽高
int width = (int)ss.getWidth();int height = (int)ss.getHeight();//设置居中和宽高
this.setBounds((width - 500) /2,(height - 500)/2,500,500);//添加chatJPanl对象,添加画布,并传入姓名
this.add(newChatJPanel(name));//设置禁止窗口缩放
this.setResizable(false);//当关闭窗口时关闭JVm,
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);//设置可见
this.setVisible(true);
}
}
=====================================================================
第二步 创建画布,并建立客户端
如何做到点击send按钮发送信息-?---------->建按钮点击事件,点击一次就获取当前文本输入框的信息,并用write()方法写出到服务端
如何做到持续接受服务端发来的信息?----------->利用计时器,每0.5s执行一次,如果服务端有发送信息,则将其显示到公屏中
/*** @Title: chatJPane.java
* @Package view
* @Description:
*@authorMr.Yao
* @date 2019年8月28日下午4:54:05
*@versionV1.0*/
packagechatview;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.Socket;importjava.net.UnknownHostException;importjava.util.Timer;importjava.util.TimerTask;importjavax.swing.JButton;importjavax.swing.JPanel;importjavax.swing.JTextArea;/*** @ClassName: chatJPane
* @Description: 继承JPanl类,用于画图形中三块区域,并建立客户端发送信息和接受信息的功能
*@authorMr.Yao
* @date 2019年8月28日下午4:54:05
**/
public class ChatJPanel extendsJPanel{//声明姓名字符串,用于接收chatJFrame传过来的用户姓名
publicString name;//添加文本域,作为聊天信息公屏框
publicJTextArea screen;//添加文本域,作为用户信息输入框
publicJTextArea input;//添加发送按钮,点击按钮,将输入框信息发送出去
publicJButton send;//申明客户端
publicSocket socket;//申明客户端输出,输入流
OutputStream os;
InputStream is;//无参构造方法
publicChatJPanel() {}//有参构造
publicChatJPanel(String name) {//接收用户姓名
this.name=name;//取消流式布局
this.setLayout(null);//创建公屏并设置宽高
screen=newJTextArea();
screen.setBounds(20, 20, 450, 280);this.add(screen);//创建输入框并设置宽高
input=newJTextArea();
input.setBounds(20,320,300,120);this.add(input);//创建按钮,并设置宽高//按钮对象
send = newJButton();
send.setBounds(340, 320, 100, 120);
send.setText("SEND");this.add(send);//新建客户端对象,并设置ip地址和端口号
try{
socket=new Socket("127.0.0.01",12321);//获取客户端输入 输出流
os=socket.getOutputStream();
is=socket.getInputStream();//输出欢迎xxx来到聊天室
String s="欢迎"+name+"来到聊天室!!!!";
os.write(s.getBytes());
os.flush();
}catch(UnknownHostException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}/*给按钮添加点击功能,点击按钮时将输入框信息发送到服务端*/send.addActionListener(newActionListener() {
@Override//重写方法
public voidactionPerformed(ActionEvent e) {//获取输入框当前的内容,并加上用户姓名
String text = name+"说:"+input.getText();//将输入框输出到服务端
try{//写出数据
os.write(text.getBytes());//刷新
os.flush();
}catch(IOException e1) {//TODO Auto-generated catch block
e1.printStackTrace();
}//清空输入框
input.setText("");
}
});//创建计时器,每500毫秒启动一次,接受服务端发出来的信息
new Timer().schedule(newTimerTask() {
@Override//重写方法,接受服务端的信息
public voidrun() {//TODO Auto-generated method stub//定义字符串,接受服务器返回的信息
String text="";try{//创建byte数组,保存服务器返回的字节信息
byte[] b=new byte[1024];//定义变量记录读取的字节数
int len=-1;//读取服务器返回字节
len=is.read(b);//转换为字符
text=new String(b,0,len);
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}if(text!="") {//添加到屏幕
screen.append(text+"\n");
}
}
},500,500);
}
}
===========================================================================
第三步 创建服务端
如何做到服务持续接受不同客户端信息并发送到所有客户端?-------> 服务一直开启,利用死循环 while()不停接受客户端soket,每接受一个就添加到一个集合中,并创建一个线程.线程的内容是遍历集合,将服务端接受的信息发送到所有客户端
创建集合,用于保存服务端接受到的socket
/*** @Title: ServerList.java
* @Package chatserver
* @Description:
*@authorMr.Yao
* @date 2019年8月28日下午7:56:41
*@versionV1.0*/
packagechatserver;importjava.net.Socket;importjava.util.ArrayList;/*** @ClassName: ServerList
* @Description: 创建集合,保存每个客户端的socket
*@authorMr.Yao
* @date 2019年8月28日下午7:56:41
**/
public classServerList {//创建集合
public static ArrayList list=new ArrayList();
}
server类用于接受客户端并开启线程,还需建立一个线程类,并重写run方法,重写后的run方法用于遍历集合并将信息输出到所有客户端
/*** @Title: Server.java
* @Package chatserver
* @Description:
*@authorMr.Yao
* @date 2019年8月28日下午7:53:37
*@versionV1.0*/
packagechatserver;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.ServerSocket;importjava.net.Socket;/*** @ClassName: Server
* @Description: 创建服务器,持续接受客户端信息
*@authorMr.Yao
* @date 2019年8月28日下午7:53:37
**/
public classServer {public static voidmain(String[] args) {//TODO Auto-generated method stub//创建服务器
ServerSocket ss = null;try{
ss=new ServerSocket(12321);//循环接受客户端
while(true) {
Socket socket=ss.accept();//将socket添加到数组
ServerList.list.add(socket);//创建匿名线程并启动
new Thread(newServerThread(socket)).start();
}
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ServerThread类实现了runnable接口,重写run方法
/*** @Title: ServerThread.java
* @Package chatserver
* @Description:
*@authorMr.Yao
* @date 2019年8月29日上午11:58:47
*@versionV1.0*/
packagechatserver;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.Socket;/*** @ClassName: ServerThread
* @Description: 创建线程类,用于循环接受客户端的信息和将信息发送到所有的客户端
*@authorMr.Yao
* @date 2019年8月29日上午11:58:47
**/
public class ServerThread implementsRunnable {//声明变量,接受用户客户端名称,用于输出XXX下线了
public String name=null;//声明socket对象
publicSocket socket;//无参构造
publicServerThread() {}//传参构造
publicServerThread(Socket socket) {this.socket=socket;
}//重写run方法
@Overridepublic voidrun() {//TODO Auto-generated method stub//创建输入流
try{
InputStream is=socket.getInputStream();//持续接受客户端信息
while(true) {//接受客户端信息
byte[] b=new byte[1024];int len=-1;
len=is.read(b);//获取客户名称,从第一句 欢迎xxx来到客户端中截取出名称
String string=new String(b,0,len);if(string.contains("欢迎")&&name==null) {//截取名称
name=string.substring(2,string.indexOf("来"));
}//将该信息发送到每个客户端
for(Socket socket:ServerList.list) {//创建输出流
OutputStream os=socket.getOutputStream();//输出信息
os.write(b, 0, len);//刷新
os.flush();
}
}
}catch(IOException e) {//TODO: handle exception//出现异常,说明该客户端已关闭,向其他在线客户端发送信息:xxx下线了//e.printStackTrace();//将集合中的已关闭的socket对象删除,
ServerList.list.remove(this.socket);//循环输出信息
for(Socket socket:ServerList.list) {//创建输出流
try{
OutputStream os=socket.getOutputStream();//输出信息
os.write((name+"下线了").getBytes());//刷新
os.flush();
}catch(IOException e1) {//TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
}
第四步 创建启动类,写主函数
/*** @Title: ChatStart.java
* @Package chatstart
* @Description:
*@authorMr.Yao
* @date 2019年8月28日下午7:51:14
*@versionV1.0*/
packagechatstart;importchatview.ChatJFrame;/*** @ClassName: ChatStart
* @Description: 主方法,程序入口
*@authorMr.Yao
* @date 2019年8月28日下午7:51:14
**/
public classChatStart {public static voidmain(String[] args) {//TODO Auto-generated method stub//创建jf边框对象
ChatJFrame jf=newChatJFrame();
}
}
==============================================================================================
运行效果展示:
---->需要先运行server,开启服务端.然后运行主函数,每运行一次,就会创建一个客户端
-----源文件目录结构:
源码:
链接:https://pan.baidu.com/s/18aPqsoJsP4ZZYf6ZkWzU-g
提取码:exva