java聊天室源代码(练习项目)

聊天室界面如下:



聊天室源代码链接

一、服务端:

窗体:

package sonyi.server;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

public class WindowServer {
	public static JFrame window;
	public static JTextArea textMessage;//聊天记录
	public static JList<String> user;//用户列表
	public static int ports;
	JButton start,send,exit;
	JTextField portServer,message,name;
	
	//主函数入口
	public static void main(String[] args) {
		new WindowServer();
	}

	//初始化窗体
	public WindowServer() {
		init();
	}
	
	//初始化内容
	public void init(){//采用绝对布局
		window = new JFrame("服务端");
		window.setLayout(null);
		window.setBounds(200, 200, 500, 400);
		window.setResizable(false);//不可改变大小
		
		JLabel label1 = new JLabel("端口号:");
		label1.setBounds(10, 8, 50, 30);
		window.add(label1);
		
		portServer = new JTextField();
		portServer.setBounds(60, 8, 100, 30);
		portServer.setText("30000");
		window.add(portServer);
		
		JLabel names = new JLabel("用户名:");
		names.setBounds(180, 8, 55, 30);
		window.add(names);
		
		name = new JTextField();
		name.setBounds(230, 8, 60, 30);
		name.setText("服务端");
		window.add(name);
		
		start = new JButton("启动");
		start.setBounds(300, 8, 80, 30);
		window.add(start);
		
		exit = new JButton("关闭");
		exit.setBounds(390, 8, 80, 30);
		window.add(exit);
		
		
		JLabel label2 = new JLabel("用户列表");
		label2.setBounds(40, 40, 80, 30);
		window.add(label2);
		
		
		user = new JList<String>();
		JScrollPane scrollPane = new JScrollPane(user);//添加滚动条
		scrollPane.setBounds(10, 70, 120, 220);
		window.add(scrollPane);
		
		textMessage = new JTextArea();
		textMessage.setBounds(135, 70, 340, 220);
		textMessage.setBorder(new TitledBorder("聊天记录"));//设置标题
		textMessage.setEditable(false);//不可编辑
		//文本内容换行的两个需要配合着用
		textMessage.setLineWrap(true);//设置文本内容自动换行,在超出文本区域时,可能会切断单词
		textMessage.setWrapStyleWord(true);//设置以自动换行,以单词为整体,保证单词不会被切断
		JScrollPane scrollPane1 = new JScrollPane(textMessage);//设置滚动条
		scrollPane1.setBounds(135, 70, 340, 220);
		window.add(scrollPane1);
		
		message = new JTextField();
		message.setBounds(10, 300, 360, 50);
		
		window.add(message);
		
		send = new JButton("发送");
		send.setBounds(380, 305, 70, 40);
		window.add(send);
		
		myEvent();	//添加监听事件
		window.setVisible(true);
	}
	
	public void myEvent(){
		window.addWindowListener(new WindowAdapter() {//关闭窗体
			public void windowClosing(WindowEvent e){
				//如果有客户端存在,发信息给客户端,并退出
				if(StartServer.userList != null && StartServer.userList.size() != 0){
					try {
						new SendServer(StartServer.userList, "" , "4");//4代表服务端退出
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
				System.exit(0);//退出窗体
			}
		});
		
		exit.addActionListener(new ActionListener() {	//退出连接
			public void actionPerformed(ActionEvent e) {
				if(StartServer.ss == null || StartServer.ss.isClosed()){//如果已退出,弹窗提醒
					JOptionPane.showMessageDialog(window, "服务器已关闭");
				}else {
					//发信息告诉客户端,要退出
					if(StartServer.userList != null && StartServer.userList.size() != 0){
						try {
							new SendServer(StartServer.userList, "" , 4 + "");	
						} catch (IOException e1) {
							e1.printStackTrace();
						}
					}
					try {
						start.setText("启动");
						exit.setText("已关闭");
						StartServer.ss.close();//关闭服务端
						StartServer.ss = null;
						StartServer.userList = null;
						StartServer.flag = false;//改变服务端循环标记
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}	
			}
		});
		
		//开启服务端
		start.addActionListener(new ActionListener() {		
			public void actionPerformed(ActionEvent e) {
				//如果服务端已经开启,弹窗提醒服务端已开启
				if(StartServer.ss != null && !StartServer.ss.isClosed()){
					JOptionPane.showMessageDialog(window, "服务器已经启动");
				}else {
					ports = getPort();//获取端口号
					if(ports != 0){
						try {
							StartServer.flag = true;//改变服务端接收循环标记
							new Thread(new StartServer(ports)).start(); //开启服务端接收线程
							start.setText("已启动");
							exit.setText("关闭");
						} catch (IOException e1) {
							JOptionPane.showMessageDialog(window, "启动失败");
						}
					}
				}
			}
		});
		
		//点击按钮发送消息
		send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				sendMsg();
			}
		});
		
		//按回车发送消息
		message.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				if(e.getKeyCode() == KeyEvent.VK_ENTER){
					sendMsg();
				}
			}
		});
	}
	
	//发送消息方法
	public void sendMsg(){
		String messages = message.getText();
		//判断内容是否为空
		if("".equals(messages)){
			JOptionPane.showMessageDialog(window, "内容不能为空!");
		}else if(StartServer.userList == null || StartServer.userList.size() == 0){//判断是否已经连接成功
			JOptionPane.showMessageDialog(window, "未连接成功,不能发送消息!");
		}else {
			try {
				//将信息发送给所有客户端
				new SendServer(StartServer.userList, getName() + ":" + messages, 1 + "");
				//将信息添加到客户端聊天记录中
				WindowServer.textMessage.append(getName() + ":" + messages + "\r\n");
				message.setText(null);//消息框设置为空
			} catch (IOException e1) {
				JOptionPane.showMessageDialog(window, "发送失败!");
			}
		}
	}
	
	//获取端口号
	public int getPort(){
		String port = portServer.getText();
		if("".equals(port)){//判断端口号是否为空
			JOptionPane.showMessageDialog(window, "端口号为口");
			return 0;
		}else {
			return Integer.parseInt(port);//返回整形的端口号
		}	
	}	
	
	//获取服务端名称
	public String getName(){
		return name.getText();
	}
}

发送接收线程:

package sonyi.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Vector;

import javax.swing.JOptionPane;

//启动服务端接收客户端的线程
public class StartServer implements Runnable{
	private int port;
	public static ArrayList<Socket> userList = null;
	public static Vector<String> userName = null;
	public static ServerSocket ss = null;
	public static boolean flag = true;
	
	//传入端口号
	public StartServer(int port) throws IOException{
		this.port = port;		
	}

	public void run() {
		Socket s = null;
		userList = new ArrayList<Socket>();//客户端端口容器
		userName = new Vector<String>();//用户名称容器
		//System.out.println("启动服务端");
		try {
			ss = new ServerSocket(port);//启动服务端
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		while (flag) {//开启循环,等待接收客户端
			try {	
				s = ss.accept();//接收客户端
				userList.add(s);//将客户端的socket添加到容器中
				
				//打印客户端信息
				String id = s.getInetAddress().getHostName();
				System.out.println(id + "-----------connected");
				System.out.println("当前客户端个数为:" + userList.size());
				
				//启动与客户端相对应的信息接收线程
				new Thread(new ReceiveServer(s,userList,userName)).start();
		
			} catch (IOException e) {
				JOptionPane.showMessageDialog(WindowServer.window, "服务端退出!");
			}	
		}
	}
}

//服务端信息接收线程
class ReceiveServer implements Runnable{
	private Socket socket;
	private ArrayList<Socket> userList;
	private Vector<String> userName;
	
	public ReceiveServer(Socket s,ArrayList<Socket> userList,Vector<String> userName) {
		this.socket = s;
		this.userList = userList;
		this.userName = userName;
	}
	
	public void run() {	
		try {
			//读取信息流
			BufferedReader brIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			while(true){		
				char info = (char)brIn.read();//先读取信息流的首字符,并判断信息类型
				String line = brIn.readLine();//读取信息流的信息内容
				
				if(info == '1'){//1代表收到的是信息
					WindowServer.textMessage.append(line + "\r\n");//将信息添加到服务端聊天记录中
					//设置消息显示最新一行,也就是滚动条出现在末尾,显示最新一条输入的信息
					WindowServer.textMessage.setCaretPosition(WindowServer.textMessage.getText().length());
					new SendServer(userList, line, "1");//将信息转发给客户端
				}
				
				if(info == '2'){//2代表有新客户端建立连接
					userName.add(line);//将新客户端用户名添加到容器中
					WindowServer.user.setListData(userName);//更新服务端用户列表
					new SendServer(userList, userName, "2");//将用户列表以字符串的形式发给客户端
				}
				
				if(info == '3'){//3代表有用户端退出连接
					userName.remove(line);//移除容器中已退出的客户端用户名
					userList.remove(socket);//移除容器中已退出的客户端的socket
					WindowServer.user.setListData(userName);//更新服务端用户列表
					new SendServer(userList, userName, "3");//将用户列表以字符串的形式发给客户端
					socket.close();//关闭该客户端的socket
					break;//结束该客户端对于的信息接收线程
				}
			}	
		} catch (IOException e) {
			e.printStackTrace();
		}
	}	
}

//服务端发送信息
class SendServer {
	SendServer(ArrayList<Socket> userList,Object message,String info) throws IOException{
		String messages = info + message;//添加信息头标记
		PrintWriter pwOut = null;
		for(Socket s : userList){//将信息发送给每个客户端
			pwOut = new PrintWriter(s.getOutputStream(),true);
			pwOut.println(messages);
		}
	}
}	

二、客户端:

窗体:

package sonyi.client;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

public class WindowClient {
	JTextField port,name,ip,message;
	JButton send;
	public static JFrame window;
	public static JButton link,exit;
	public static JTextArea textMessage;
	public static Socket socket = null;
	public static JList<String> user;
	
	//主函数入口
	public static void main(String[] args) {
		new WindowClient();
	}
	
	//初始化窗体
	public WindowClient() {
		init();
	}
	
	//窗体初始化内容
	public void init(){//采用绝对布局
		window = new JFrame("客户端");
		window.setLayout(null);
		window.setBounds(200, 200, 500, 400);
		window.setResizable(false);
		
		JLabel label = new JLabel("主机IP:");
		label.setBounds(10, 8, 50, 30);
		window.add(label);
		
		ip = new JTextField();
		ip.setBounds(55, 8, 60, 30);
		ip.setText("127.0.0.1");
		window.add(ip);
		
		
		JLabel label1 = new JLabel("端口号:");
		label1.setBounds(125, 8, 50, 30);
		window.add(label1);
		
		port = new JTextField();
		port.setBounds(170, 8, 40, 30);
		port.setText("30000");
		window.add(port);
		
		JLabel names = new JLabel("用户名:");
		names.setBounds(220, 8, 55, 30);
		window.add(names);
		
		name = new JTextField();
		name.setBounds(265, 8, 60, 30);
		name.setText("客户端1");
		window.add(name);
		
		link = new JButton("连接");
		link.setBounds(335, 8, 75, 30);
		window.add(link);
		
		exit = new JButton("退出");
		exit.setBounds(415, 8, 75, 30);
		window.add(exit);
		
		JLabel label2 = new JLabel("用户列表");
		label2.setBounds(40, 40, 80, 30);
		window.add(label2);
		
		user = new JList<String>();
		JScrollPane scrollPane = new JScrollPane(user);//设置滚动条
		scrollPane.setBounds(10, 70, 120, 220);
		window.add(scrollPane);
		
		textMessage = new JTextArea();
		textMessage.setBounds(135, 70, 340, 220);
		textMessage.setEditable(false);//文本不可编辑
		textMessage.setBorder(new TitledBorder("聊天记录"));//设置标题
		//文本内容换行的两个需要配合着用
		textMessage.setLineWrap(true);//设置文本内容自动换行,在超出文本区域时,可能会切断单词
		textMessage.setWrapStyleWord(true);//设置以自动换行,以单词为整体,保证单词不会被切断
		JScrollPane scrollPane1 = new JScrollPane(textMessage);//设置滚动条
		scrollPane1.setBounds(135, 70, 340, 220);
		window.add(scrollPane1);
		
		message = new JTextField();
		message.setBounds(10, 300, 360, 50);
		message.setText(null);
		window.add(message);
		
		send = new JButton("发送");
		send.setBounds(380, 305, 70, 40);
		window.add(send);
		
		myEvent();//添加监听事件
		window.setVisible(true);//设置窗体可见	
	}
	
	
	public void myEvent(){//事件监听
		window.addWindowListener(new WindowAdapter() {//退出窗体
			public void windowClosing(WindowEvent e){
				//如果仍在连接,发信息给服务端,并退出
				if(socket != null && socket.isConnected()){
					try {
						new SendClient(socket, getName(), 3 + "");
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
				System.exit(0);
			}
		});
		
		//关闭连接
		exit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				//如果仍在连接,将信息发给服务端
				if(socket == null){
					JOptionPane.showMessageDialog(window, "已关闭连接");
				}else if(socket != null && socket.isConnected()){
					try {
						new SendClient(socket, getName(), "3");//发送信息给服务端
						link.setText("连接");
						exit.setText("已退出");
						socket.close();//关闭socket
						socket = null;
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			}
		});
		
		//建立连接
		link.addActionListener(new ActionListener() {		
			public void actionPerformed(ActionEvent e) {
				//判断是否已经连接成功
				if(socket != null && socket.isConnected()){	
					JOptionPane.showMessageDialog(window, "已经连接成功!");
				}else {
					String ipString = ip.getText();//获取ip地址
					String portClinet = port.getText();//获取端口号
					
					if("".equals(ipString) || "".equals(portClinet)){//判断获取内容是否为空
						JOptionPane.showMessageDialog(window, "ip或端口号为空!");
					}else {
						try {
							int ports = Integer.parseInt(portClinet);//将端口号转为整形
							socket = new Socket(ipString,ports);//建立连接
							link.setText("已连接");//更改button显示信息
							exit.setText("退出");
							new SendClient(socket, getName(), 2 + "");//发送该客户端名称至服务器
							new Thread(new ReceiveClient(socket)).start();//启动接收线程
						} catch (Exception e2) {
							JOptionPane.showMessageDialog(window, "连接未成功!可能是ip或端口号格式不对,或服务器未开启。");
						}
					}
				}
			}
		});
		
		//点击按钮发送信息
		send.addActionListener(new ActionListener() {			
			public void actionPerformed(ActionEvent e) {
				sendMsg();
			}
		});	
		
		//按回车发送信息
		message.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				if(e.getKeyCode() == KeyEvent.VK_ENTER){
					sendMsg();
				}
			}
		});
	}
	
	//发送信息的方法
	public void sendMsg(){
		String messages = message.getText();//获取文本框内容
		if("".equals(messages)){//判断信息是否为空
			JOptionPane.showMessageDialog(window, "内容不能为空!");
		}
		else if(socket == null || !socket.isConnected()){//判断是否已经连接成功
			JOptionPane.showMessageDialog(window, "未连接成功,不能发送消息!");
		}else {
			try {
				//发送信息
				new SendClient(socket,getName() + ":" + messages,"1");
				message.setText(null);//文本框内容设置为空
			} catch (IOException e1) {
				JOptionPane.showMessageDialog(window, "信息发送失败!");
			}
		}	
	}
	
	//获取客户端名称
	public String getName(){
		return name.getText();
	}
}

发送接收线程:

package sonyi.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;

//启动客户端接收线程
public class StartClient {
	public StartClient(Socket s) throws UnknownHostException, IOException{	
		new Thread(new ReceiveClient(s)).start();
	}
}

//客户端接收线程
class ReceiveClient implements Runnable{
	private Socket s;
	public ReceiveClient(Socket s) {
		this.s = s;
	}
	public void run() {
		try {
			//信息接收流
			BufferedReader brIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
			while(true){	
				char info = (char)brIn.read();//读取信息流首字符,判断信息类型
				String line = brIn.readLine();//读取信息流内容
				
				if(info == '1'){//代表发送的是消息
					WindowClient.textMessage.append(line + "\r\n");	//将消息添加到文本域中
					//设置消息显示最新一行,也就是滚动条出现在末尾,显示最新一条输入的信息
					WindowClient.textMessage.setCaretPosition(WindowClient.textMessage.getText().length());
				}
				
				if(info == '2' || info == '3'){//有新用户加入或退出,2为加入,3为退出
					String sub = line.substring(1, line.length()-1);
					String[] data = sub.split(",");
					WindowClient.user.clearSelection();
					WindowClient.user.setListData(data);
				}
				
				if(info == '4'){//4代表服务端退出
					WindowClient.link.setText("连接");
					WindowClient.exit.setText("已退出");
					WindowClient.socket.close();
					WindowClient.socket = null;
					break;
				}
			}	
		} catch (IOException e) {
			JOptionPane.showMessageDialog(WindowClient.window, "客户端已退出连接");
		}
	}	
}

//客户端发送信息类
class SendClient {
	SendClient(Socket s,Object message,String info) throws IOException{	
		String messages = info + message;
		PrintWriter pwOut = new PrintWriter(s.getOutputStream(),true);
		pwOut.println(messages);
	}
}	

运行过程中应该还有很多bug,希望码友们指正,互相交流。



Java聊天室程序 需求分析 2.1 业务需求 1. 与聊天室成员一起聊天。 2. 可以与聊天室成员私聊。 3. 可以改变聊天内容风格。 4. 用户注册(含头像)、登录。 5. 服务器监控聊天内容。 6. 服务器过滤非法内容。 7. 服务器发送通知。 8. 服务器踢人。 9. 保存服务器日志。 10.保存用户聊天信息。 2.2 系统功能模块 2.2.1 服务器端 1.处理用户注册 2.处理用户登录 3.处理用户发送信息 4.处理用户得到信息 5.处理用户退出 2.2.2 客户端 1.用户注册界面及结果 2.用户登录界面及结果 3.用户发送信息界面及结果 4.用户得到信息界面及结果 5.用户退出界面及结果 2.3 性能需求 运行环境:Windows 9x、2000、xp、2003,Linux 必要环境:JDK 1.5 以上 硬件环境:CPU 400MHz以上,内存64MB以上 3.1.2 客户端结构 ChatClient.java客户端程序启动类,负责客户端的启动和退出。 Login.java客户端程序登录界面,负责用户帐号信息的验证与反馈。 Register.java客户端程序注册界面,负责用户帐号信息的注册验证与反馈。 ChatRoom.java客户端程序聊天室主界面,负责接收、发送聊天内容与服务器端的Connection.java 亲密合作。 Windowclose 为ChatRoom.java的内部类,负责监听聊天室界面的操作,当用户退出时返回给服务器信息。 Clock.java客户端程序的一个小程序,实现的一个石英钟功能。 3. 2 系统实现原理 当用户聊天时,将当前用户名、聊天对象、聊天内容、聊天语气和是否私聊进行封装,然后与服务器建立Socket连接,再用对象输出流包装Socket的输出流将聊天信息对象发送给服务器端 当用户发送聊天信息时,服务端将会收到客户端用Socket传输过来的聊天信息对象,然后将其强制转换为Chat对象,并将本次用户的聊天信息对象添加到聊天对象集Message中,以供所有聊天用户访问。 接收用户的聊天信息是由多线程技术实现的,因为客户端必须时时关注更新服务器上是否有最新消息,在本程序中设定的是3秒刷新服务器一次,如果间隔时间太短将会增加客户端与服务器端的通信负担,而间隔时间长就会让人感觉没有时效性,所以经过权衡后认为3秒最佳,因为每个用户都不可能在3秒内连续发送信息。 当每次用户接收到聊天信息后将会开始分析聊天信息然后将适合自己的信息人性化地显示在聊天信息界面上。 4.1.1 问题陈述 1.接受用户注册信息并保存在一个基于文件的对象型数据库。 2.能够允许注册过的用户登陆聊天界面并可以聊天。 3.能够接受私聊信息并发送给特定的用户。 4.服务器运行在自定义的端口上#1001。 5.服务器监控用户列表和用户聊天信息(私聊除外)。 6.服务器踢人,发送通知。 7.服务器保存日志。
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值