JAVA简易聊天室1.0版本(1)

声明:本文参考多篇java聊天室设计的文章。

java简易聊天室制作要求

1. 具有简易的GUI界面

2. 能实现一个聊天室中多人聊天

3. 可以两人私聊

提示:使用socket通信

首先我参考的是某b站上的彭姐直播间的一个多人聊天室。跟着UP主一行行写代码,我认为这样可以学习到他人写一个项目时的思路,从而运用到自己日常生活中,但是在俺看来(仅俺个人意见),这个UP主的思路略微有一点点混乱,不够简洁明了。不过优点就是,他有许多带领萌新修补bug或者回头修改不够好的代码的过程,大家能看到作为一个程序猿的思考方式。

后来我又去某b站上看了一些相关的视频,我认为一个对于做GUI界面的程序来说,可以先清晰明了地设计好操作界面,然后对于按钮进行处理,再设计内逻辑是比较好的思路(这样能让代码看起来没那么凌乱)

二话不说,先上多人聊天室的代码:(初级1.0:只有多人聊天的功能)

ClientChat.java

package test1217.chat;

import java.awt.BorderLayout;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;


public class ClientChat extends JFrame{
	

	public static void main(String[] args) {
		//调用构造方法
		ClientChat cc=new ClientChat();
		cc.init();
	}
	
//定义客户端窗口组件
	//设置放置多行文本域(设置长度为行10列20)
	private JTextArea ta=new JTextArea(10,20);
	//设置滚动条,并将文本域添加到滚动条中
	private JScrollPane sp=new JScrollPane(ta);
	//设置放置单行文本框
	private JTextField tf=new JTextField(20);
	//设置面板
	private JPanel jp=new JPanel();
	//设置按钮
	private JButton jb=new JButton("发送");
	
	
	
	 /********************************TCP 客户端 start**********************************/
	
//基于TCP的Socket通信
    // 静态常量主机端口号
	private static final String CONNSTR="127.0.0.1";
    // 静态常量服务器端口号
	private static final int CONNPORT=8888;//定义端口号
	private Socket s=null;
	//输出流
	private DataOutputStream dos=null;
	 //客户端连接上服务器判断符号
	private boolean isConn=false;
	
//构造方法
	public ClientChat() throws HeadlessException {
		super();
		// TODO 自动生成的构造函数存根
	}
	
	
//设计窗口
	public void init() {
		//设置标题
		this.setTitle("多人聊天室 客户端");
		//将文本框和按钮添加到面板中
		jp.add(tf);
		jp.add(jb);
		//布局,将sp放在中间
		this.add(sp, BorderLayout.CENTER);
		//将jp放在南边
		this.add(jp,BorderLayout.SOUTH);
		//将jp放在南边
		this.setBounds(400,400,400,400);
		//关闭窗口
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//禁止ta里用光标输入文字,文字显示区域不能编辑
		ta.setEditable(false);
		//光标聚焦在tf里
		tf.requestFocus();
	
		
		
//匿名内部类创建监视器(对tf做监听,拿到tf的内容,append到ta)
//输入流
		tf.addActionListener (new ActionListener(){
			
			public void actionPerformed(ActionEvent e) {
				//将输入的字符显示在tf里
				String strSend=tf.getText();
				
				if(strSend.trim().length()==0) {//判断输入的字符串的长度是否为0,.trim()可以去掉字符串前后空格
					return;
				}
				
				//需要将监听的输入内容strSend发送到服务器上
				send(strSend);
				//使得输入字符回车键之后将原输入框里的字符清空
				tf.setText(" ");
				}
			});
		
			try {
				s=new Socket(CONNSTR,CONNPORT);//这里做的Scoket连接
				//表示已连上服务器
				isConn=true;
			
			} catch (UnknownHostException e1) {
				// TODO 自动生成的 catch 块
				e1.printStackTrace();
			} catch (IOException e1) {
				// TODO 自动生成的 catch 块
				e1.printStackTrace();
			}
			//设置窗口可见
			this.setVisible(true);
			//窗口打开即启动多线程
			new Thread(new Receive()).start();
		}
	

	
//输出流:发送信息到服务器上的方法

	public void send(String str) {
		try {
				dos=new DataOutputStream(s.getOutputStream());
				dos.writeUTF(str);
				} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
				}
			}

	
//多线程的类-----实现了Runnable接口的,需要被封装在多线程类里
	class Receive implements Runnable {

		public void run() {
			try {
				//窗口打开即启动多线程
				while(isConn) {
					DataInputStream dis=new DataInputStream(s.getInputStream());
					String str=dis.readUTF();
					ta.append(str);
					}
			} catch (SocketException e) {
				System.out.println("服务器意外终止!");
				ta.append("服务器意外中断!");
			}
			catch (IOException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
			
		}
	}
}
	
/********************************TCP 客户端 end**********************************/

ServerChat.java 

package test1217.chat;

import java.awt.BorderLayout;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowListener;
import java.awt.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;



/********************************TCP 服务端 start**********************************/
public class ServerChat extends JFrame{
	//main()方法
	public static void main(String[] args) {
		ServerChat sc=new ServerChat();
	}
	
	//端口
	private static final int PORT=8888;
	//创建多行文本区Ta
	private JTextArea serverTa=new JTextArea();
	//滚动条的设置,并入ta
	private JScrollPane Sp=new JScrollPane(serverTa);
	//因为有两个按钮在布局的同一个方位,因此需要创建一个面板容器放置
	private JPanel btnTool=new JPanel();
	//创建按钮
	private JButton startBtn=new JButton("启动");
	private JButton stopBtn=new JButton("停止");		
	//Socket通信
	private  static ServerSocket ss=null;
	private Socket s=null;
	
	  // 多个客户端访问时,客户端对象存放入List中
	private ArrayList<ClientConn> ccList=new ArrayList<ClientConn>();
	
	// 服务器启动的标志 (其实ServerSocket ss 初始化出来时以为者服务器的启动)
	private boolean isStart=false;
	
	//构造方法,也可以写成public void init()初始化,打开窗口
	public ServerChat() { 
		//设置标题
		this.setTitle("服务器端");
		//设置serverTa的位置
		this.add(Sp,BorderLayout.CENTER);
		//将按钮加到btnTool面板里
		btnTool.add(startBtn);
		btnTool.add(stopBtn);
		//将该面板放置在窗口的南面
		this.add(btnTool,BorderLayout.SOUTH);
		//设置窗口大小
		this.setBounds(0,0,500,500);
		//设置窗口是否可见
		this.setVisible(true);
		
		//判断是否启动服务器
		if(isStart) {
			serverTa.append("服务器已经成功启动了!\n");
		}else {
			serverTa.append("服务器还未启动,请点击启动按钮!\n");
		}
		//关闭窗口
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		//对停止按钮建立监视器
		stopBtn.addActionListener(new ActionListener(){

			public void actionPerformed(ActionEvent e) {
		
			try {	
				if(ss!=null) {
					ss.close();
					isStart=false;
			}
			System.exit(0);
			serverTa.append("服务器断开!");
			System.out.println("服务器停止!");
			}catch (IOException e1) {
					// TODO 自动生成的 catch 块
					e1.printStackTrace();
				}
			}
			
		});
		
		//对启动按钮建立启动键监视器
		startBtn.addActionListener(new ActionListener() {
		
			public void actionPerformed(ActionEvent e) {
				System.out.println("hhh");
				try {
					if(ss==null){
						ss=new ServerSocket(PORT);
					}
					isStart=true;
					serverTa.append("服务器已经启动了!"+"\n");
				} catch(IOException e1) {
					
					e1.printStackTrace();
				}
				
			}
		});
		
		//让serverTa区域只能显示不能改变
		serverTa.setEditable(false);
		//调用启动方法
		startServer();
	}
	
	
		//服务器启动的方法
	public void startServer() {
		try{
			try {
			ss=new ServerSocket(PORT);//给予ss端口号(常量,在上面已经定义)
			isStart=true;
		}catch(IOException e2) {
			
			e2.printStackTrace();
		}
		/**
		*为保证多个客户端连上,这里应采用while循环
		*可以接受多个客户端的连接
		*判断符为服务器开关的判断符
		**/
		while (isStart) {
			//accept()从客户端“连接”的队列中返回一个最近的新连接。accept正常返回,就代表有新客户连接。
			s = ss.accept();
			//接收一个客户端,就将它加到ccList1里。
			ccList.add(new ClientConn(s));
			System.out.println("一个客户端连接服务器:" + s.getInetAddress() + "/" + s.getPort()+"\n");//连接上的客户端的地址和客户端
			serverTa.append("一个客户端连接服务器:" + s.getInetAddress() + "/" + s.getPort()+"\n");
		}
			}catch(SocketException e) {
				System.out.println("服务器中断了!");		
			}catch(IOException e) {
				e.printStackTrace();
		}
	}
	
		//服务器停止的方法
		public void stopServer() {
		
			}
	
	//服务器端接收数据的方法应该是多线程的接收
	/*public void reciveStr() {
		try {
			dis=new DataInputStream(s.getInputStream());
			String str=dis.readUTF();
			System.out.println(str);
			serverTa.append(str);
			} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}*/
	
		//内部类声明 这个对象是属于服务器端一个连接对象,客户端与服务器的联系
			class ClientConn implements Runnable{
				Socket s=null;
			
				public ClientConn(Socket s) {
					this.s=s;
			 /**
             * 线程启动在这里:
             * 初始化方法里 初始化一个线程 ,线程中封装的是自己,做整个线程的调用
             */
			(new Thread(this)).start();
		}
		
		
		
		//同时接受客户端信息的---多线程run()方法接收数据
		public void run() {
			 try {
				 
				DataInputStream dis=new DataInputStream(s.getInputStream());
				//为了让服务器能够收到每个客户端的多句话,则采用循环模式
				while(isStart) {
					//读数据
					String str=dis.readUTF();
					
					System.out.println(s.getInetAddress()+"|"+s.getPort()+"说:"+"\n"+str+"\n");
					serverTa.append(s.getInetAddress()+"|"+s.getPort()+"说:"+"\n"+str+"\n");
					//为了方便之后的信息返回,将客户发送内容设定为字符串strSend
					String strSend=s.getInetAddress()+"|"+s.getPort()+"说:"+"\n"+str+"\n";
					//需要遍历ccList,调用send方法,在客户端接收信息是多线程的接收
					Iterator<ClientConn> it=ccList.iterator();
					while(it.hasNext()) {
						ClientConn o=it.next();
						o.send(strSend);
					}
				}
				
			} catch(SocketException e){
				System.out.println(s.getInetAddress()+"|"+s.getPort()+"一个客户端下线了"+"\n");
				serverTa.append(s.getInetAddress()+"|"+s.getPort()+"一个客户端下线了"+"\n");
			 }catch (IOException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
		
		//内部类里:向每个连接对象发送数据的方法
		public void send(String str) {
			try {
				DataOutputStream dos=new DataOutputStream(this.s.getOutputStream());
				dos.writeUTF(str);
			}catch (SocketException e) {
                e.printStackTrace();
			}catch(IOException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	
}

/********************************TCP 服务端 end**********************************/

多人聊天室主要是要有服务端和多个客户端这两个界面, 他们通过相同的端口号进行连接,要实现多个客户端同时在线,则需要服务端开启多线程服务。

我使用的是eclispe。下一篇文章则会解决私聊问题。

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值