大学Java基础课程设计——网络聊天室

不登高山,不知天之高也;不临深溪,不知地之厚也。

| @Author:TTODS

项目简介

项目名称:TT聊天室。
项目功能:此项目通过连接数据库实现了用户的注册登录功能,支持用户创建房间或者加入别人的房间,并在房间内进行多人实时聊天。
此项目是我本学期Java基础课程的课程设计,综合Java基础课程中的文件输入输出、多线程、Swing编程、网络编程、数据库编程等技术实现了聊天室的基本功能。

系统设计与实现

聊天室系统的总体设计

聊天室系统的采用Client-Server(C/S)模式,服务器与客户端之间使用数据包(data package)作为交换的消息。用户通过聊天室客户端的用户界面向服务器发送请求(request)包,服务器通过接收客户端发送的请求包,根据请求内容与数据库查询结果给予应答向客户端返回一个数据包,然后客户端接收服务器的回应(response),根据回应的内容将操作的结果通过界面反馈给用户。聊天室系统的总体设计如下图(图1):
聊天室总体设计

图1 聊天室系统的总体设计图
服务器端功能设计

服务器大致由以下部分组成(如图2):
在这里插入图片描述


图2 服务器的组成部分

  • 已连接的客户端列表:只要客户端连接了服务器,就会将客户端的socket传入这个列表中,便于后面服务器向每个连接的服务器发送消息(如:在线房间数更新,该列表中包括所有登录了的客户端socket)。
private HashSet<Socket> clients;//已连接的客户端
  • 房间列表:用户创建的房间都会被抽象为一个ChatRoom对象,存入该列表中,使用了观察者模式,服务器收到客户端聊天信息会发到指定的ChatRoom,再由该ChatRoom对象广播给该房间内的客户端(ChatRoom对象中含一个Socket列表存放该房间内的客户端Socket);
private LinkedList<ChatRoom> chatRooms;// 当前开放的房间列表
  • 数据库连接:控制数据库的连接,查询与更新。
private Connection con = null; // 数据库连接
  • 服务器数据处理器:服务器要针对每一个客户端单独开设一个线程,监听客户端请求,为减小服务器的压力,采用线程池管理。
private ServerDataHandler sDataHandler;// 服务器数据处理器,运用线程池,处理多个客户端请求
  • 客户端连接监听器:监听客户端的连接,一旦检测到新的客户端连接就让服务器数据处理器在线程池中创建一个新的线程。
	private AcceptListener acceptListener;//客户端连接监听器
客户端功能设计

客户端大致由以下部分组成(如图3):
在这里插入图片描述


图3 客户端的组成部分

  • 客户端窗口:使用swing显示客户端的用户界面,并与用户进行交互。
	ClientFrame clientFrame;// 用户界面
  • 客户端信息接收器:使用一个线程接收服务器的应答。
	ClientReceiver clientReceiver;// 客户端接收器,用来随时接收服务器发来的消息
  • 客户端用户:记录用户当前的状态与属性,为界面显示提供基本信息。
	TTUser user;// 当前登录用户的引用
数据包

由于本系统客户端与服务器消息传输使用的是字符缓冲流,且服务器要通过此消息区分不同的请求,针对此需求,我设计了一个数据包(DataPack)类,这个类有点类似网络上常用的JSON数据,可以通过字符串解析为DataPack对象,但是它比JSON简单,它分为三个域(如图4):
在这里插入图片描述


图4 数据包的基本结构

  • Type : 一个枚举型的标识,标名该请求/应答得类型。
	final private RequestType type;
//请求的所有类型
	 public static enum RequestType{
		signUp,signIn,signOut,enterRoom,createRoom,exitRoom,sendMessage,updateRoomCnt,updateUserCnt,rename
		}
  • Status : 服务器回应时的状态(成功/失败),客户端基于此状态判断请求是否成功。
	final private String status;
  • Body : 请求的附带数据,比如登录时,客户端要将用户名和密码发送给服务器,若密码验证成功,服务器要将在数据库查询到的用户昵称发回给客户端,客户端便可以借此完成用户信息的初始化。
	final private String body;	
用户操作处理流程
  • 用户注册

在这里插入图片描述


表1 用户注册模块

用户点击注册按钮后,程序会从界面收集表单的完整性,若有未填的数据项会给出提示,当数据符合要求时就会将数据整合成请求包的形式发送给服务器。服务器返回的结果会被客户端接收器接收,根据回应给用户以界面反馈。

  • 用户登录

在这里插入图片描述


表2 用户登录模块

用户点击登录按钮后,程序会从界面收集表单的完整性,若有未填的数据项会给出提示,当数据符合要求时就会将数据整合成请求包的形式发送给服务器。服务器返回的结果会被客户端接收器接收,根据回应给用户以界面反馈。

  • 创建房间

在这里插入图片描述


表3 创建房间模块

服务器收到创建房间请求时,会将房间对象加入自己的房间列表中,方便进一步对房间进行消息推送。

  • 进入房间

在这里插入图片描述


表4 加入房间模块

客户端socket被放入房间的客户端列表后,该房间内客户端向服务器发送消息请求后,该消息会由服务器分到该房间,再由该房间对该房间内所有的客户端转发(广播)。

  • 退出房间

在这里插入图片描述


表5 退出房间模块

用户点击退出的房间按钮,服务器处理后,客户端离开房间不再接收该房间的推送,界面返回房间操作界面。

  • 消息发送

在这里插入图片描述


表6 聊天信息发送模块

房间内某一客户端发送的聊天信息,经服务器处理会推送到该房间内所有的客户端。

  • 用户昵称修改

在这里插入图片描述


表7 用户昵称修改模块

用户注册时,会默认注册名为用户昵称,登陆后可以修改。

客户端界面设计

注册界面:用户可以通过此界面完成注册操作,用户输入用户名和密码便可以完成注册,若已有账号,可以选择按“To sign in”按钮前往登录界面。
在这里插入图片描述
登录界面:用户可以通过此界面完成登录操作,用户输入用户名和密码便可以完成登录,若没有账号,可以选择按“Create an account”按钮前往注册界面。
在这里插入图片描述
创建/加入房间的界面:此界面是用户登录后显示的界面,用户可以选择创建房间或者加入别人的房间来进入聊天界面,也可以选择退出登录来切换账号,此外,该界面还提供软件基本信息(在线房间数,当前系统时间等)的显示和用户昵称的修改等功能。
在这里插入图片描述
聊天界面:此界面是聊天界面,用户可编辑消息发送给当前房间内的所有用户。还可以通过点击“exit room”按钮来退出房间(若是房主退出房间意味着该房间关闭,所有该房间内的用户都会被强制退出房间);
在这里插入图片描述

数据库设计

由于这是Java基础的课程设计,数据库这方面没有花功夫,只是使用了一个简单的用户表用来存放用户的账号密码
在这里插入图片描述

源码展示

服务器端代码
  • 服务器主体类(TTServer.java)
package ttserver;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.*;
import java.util.*;
import java.util.Date;

public class TTServer {
	private LinkedList<ChatRoom> chatRooms;// 当前开放的房间列表
	private ServerDataHandler sDataHandler;// 服务器数据处理器,运用线程池,处理多个客户端请求
	private Connection con = null; // 数据库连接
	private ServerSocket serverSocket; //服务器socket
	private AcceptListener acceptListener; //连接监听器
	private HashSet<Socket> clients;   //已连接的客户端集合(实际上是登陆后的客户端集合)
	private BufferedWriter logWriter = null; //日志字符输出流
	static HashMap<Character, ArrayList<String>> illegalWordList; //敏感词列表
	//敏感词加载
	static {
		try {
			illegalWordList = new HashMap<>();
			loadIllegalWordList();
		} catch (Exception e) {
			// TODO 自动生成的 catch 块
			System.out.println("敏感词列表加载失败");
		}
	}

	public TTServer() {
		// TODO 自动生成的构造函数存根
		try {
			// 连接数据库
			connectMySQL();
			// 打开连接
			serverSocket = new ServerSocket(9000);
			// 加载敏感词列表

			// 启动日志[!附加模式]
			logWriter = new BufferedWriter(new FileWriter("TTchatRoom.log", true));
			// 初始化房间列表
			chatRooms = new LinkedList<>();
			//初始化连接池
			clients=new HashSet<>();
			// 启动连接监听器
			acceptListener = new AcceptListener();
			acceptListener.start();
			// 启动请求处理器
			sDataHandler = new ServerDataHandler(this);

			System.out.println(new Date().toLocaleString() + ": 服务器启动成功");
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			System.err.println("服务器启动失败,请检查是否该端口是否已被其他程序占用");
			e.printStackTrace();
		}
	}

	// 连接数据库
	public void connectMySQL() {
		// 声明Connection对象

		// 驱动程序名
		String driver = "com.mysql.cj.jdbc.Driver";
		// URL指向要访问的数据库名 test
		String url = "jdbc:mysql://localhost:3306/ttchatroom?serverTimezone=UTC";
		// MySQL配置时的用户名
		String user = "root";
		// MySQL配置时的密码
		String password = "";
		try {
			// 加载驱动程序
			Class.forName(driver);
			// 1.getConnection()方法,连接MySQL数据库!!
			con = DriverManager.getConnection(url, user, password);

			if (!con.isClosed())
				System.out.println("成功以 " + user + " 身份连接到数据库!!!");
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

	//获取开放房间的列表
	public LinkedList<ChatRoom>getChatRoomList() {
		return this.chatRooms;
	}
	//获取已连接的客户端socket集合
	public 	HashSet<Socket> getConnectedClientSet() {
		return this.clients;
	}
	// sql查询,返回结果集
	public synchronized ResultSet SQLSelect(String sql) throws Exception {
		System.out.println(sql);
		Statement statement = con.createStatement();
		ResultSet rs = statement.executeQuery(sql);
		return rs;
	}

	// sql更新,返回修改是否成功
	public synchronized boolean SQLUpdate(String sql) throws Exception {
		PreparedStatement sta = con.prepareStatement(sql);
		int rows = sta.executeUpdate();
		if (rows == 0)
			return false;
		return true;
	}

	// 加载敏感词列表
	static void loadIllegalWordList() throws Exception {
		BufferedReader bur = new BufferedReader(new FileReader("IllegalWordList.txt"));
		String s = null;
		while ((s = bur.readLine()) != null) {
			if (!illegalWordList.containsKey(s.charAt(0))) {
				illegalWordList.put(s.charAt(0), new ArrayList<String>());
			}
			illegalWordList.get(s.charAt(0)).add(s);
		}
		bur.close();
	}

	// 替换敏感词
	static String replaceIllegalWord(String s) {
		for (int i = 0; i < s.length(); i++) {
			if (illegalWordList.containsKey(s.charAt(i))) {
				for (String word : illegalWordList.get(s.charAt(i))) {
					int len = word.length();
					if (i + len <= s.length() && s.substring(i, i + len).equals(word)) {
						char[] replaceWord = new char[len];
						Arrays.fill(replaceWord, '*');
						s = s.substring(0, i) + new String(replaceWord) + s.substring(i + len);
						i += len - 1;
					}
				}
			}
		}
		return s;
	}

	//获取当前房间数量
	public int getRoomCnt() {
		return chatRooms.size();
	}

	// 注册房间,在房间创建时调用
	public synchronized void registerRoom(ChatRoom chatRoom) {
		chatRooms.add(chatRoom);

	}

	// 将客户端注册到房间
	public synchronized void registerClient(Long roomId, Socket client) {
		for (ChatRoom chatRoom : chatRooms) {
			if (chatRoom.getRoomId() == roomId) {
				chatRoom.registerClient(client);
			}
		}
	}

	// 将客户端从聊天室中移除
	public synchronized void removeClientFromRoom(Long roomId, Socket client) {
		if(clients.contains(client)) clients.remove(client);
		for (ChatRoom chatRoom : chatRooms) {
			if (chatRoom.getRoomId() == roomId) {
				chatRoom.removeClient(client);
			}
		}
	}
	
	//将客户端移除连接列表
	public synchronized void removeClient(Socket client) {
		clients.remove(client);
	}

	// 分发一条消息到相应的房间,再由房间对象广播消息
	public synchronized void sendMessge(long roomId, String message) {
		for (ChatRoom chatRoom : chatRooms) {
			if (chatRoom.getRoomId() == roomId) {
				chatRoom.broadcast(message);
				break;
			}
		}

	}

	// 移除房间,在房间关闭时使用(使房间中的所有客户端,房间为空时,房间会自动清除)
	public synchronized void removeRoom(long roomId) {
		int i = 0;
		for (; i < chatRooms.size(); i++) {
			if (chatRooms.get(i).getRoomId() == roomId) {
				chatRooms.get(i).broadcast("exitRoom", "roomClosed", "房主退出了房间,房间关闭");
				chatRooms.remove(i);
				break;
			}
		}
		
	}

	public ServerSocket getServerSocket() {

		return serverSocket;
	}

	// 服务器连接监听器
	class AcceptListener extends Thread {
		@Override
		public void run() {
			// TODO 自动生成的方法存根
			while (true) {
				try {
					Socket clientSocket = serverSocket.accept();
					sDataHandler.addClient(clientSocket);
					clients.add(clientSocket);
					// 把这个客户端加入ServerDataHandler的处理队列
				} catch (IOException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}
		}
	}

	//记录日志
	public synchronized void writeLog(String log) throws Exception {
		logWriter.write("[" + (new Date().toLocaleString()) + "]" + log);
		logWriter.newLine();
		logWriter.flush();
	}

	//启动服务器
	public static void main(String[] args) {
		new TTServer();
	}
}

  • 聊天室类(ChatRoom.java)
package ttserver;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedList;

public class ChatRoom {
	final private long roomId ; //房间编号
	TTServer server;
	LinkedList <Socket> clients=null; //房间内的客户端列表
	
	//构造方法
	public ChatRoom(TTServer server,long roomId){
		this.roomId = roomId;
		clients = new LinkedList<>();
		this.server = server;
	}
	
	public long getRoomId() {
		return roomId;
	}
	
	//注册房间
	public synchronized void registerClient(Socket client) {
		clients.add(client);
	}
	
	//移除房间内的某个客户端,移除后房间变空则从服务器房间列表中移除此房间
	public synchronized void removeClient(Socket client) {
		for(int i=0;i<clients.size();i++) {
			if(clients.get(i)==client) {
				clients.remove(i);
				break;
			}
		}
		if(clients.size()==0) server.removeRoom(roomId);;
	}
	
	//获取房间内的用户数
	public int  getUserCnt() {
		return clients.size();
	}
	
	//在房间内广播消息(转发玩家发送的聊天消息)
	public void broadcast(String message) {
		for(Socket client : clients) {
			try {
				BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
				
				bw.write("{type:sendMessage,status:success,body:{"+message+"}}");
				bw.newLine();
				bw.flush();
			} catch (IOException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
	}
	//在房间内广播一个数据包
	public void broadcast(String type,String status,String body) {
		for(Socket client : clients) {
			try {
				BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
				
				bw.write("{type:"+type+",status:"+status+",body:{"+body+"}}");
				bw.newLine();
				bw.flush();
			} catch (IOException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
	}
}
  • 服务器数据处理器(ServerDataHandler.java)
package ttserver;

import java.io.*;
import java.net.Socket;
import java.sql.ResultSet;
import java.util.concurrent.*;

import dataPack.*;
import dataPack.DataPack.RequestType;

public class ServerDataHandler {
	private TTServer server;
	private ExecutorService pool; //线程池

	public ServerDataHandler(TTServer s) {
		// TODO 自动生成的构造函数存根
		server = s;
		pool = Executors.newFixedThreadPool(8);
	}

	// 增加连接的客户端对象
	public void addClient(Socket client) {
		Runnable task = new Runnable() {
			public void run() {
				BufferedReader br = null;
				BufferedWriter bw = null;
				String userName = null;// 从数据库获取
				long userId = -1;// 从数据库获取
				long roomId = -1;
				try {
					br = new BufferedReader(new InputStreamReader(client.getInputStream()));
					bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
				} catch (IOException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
				while (!client.isClosed()) {
					String s = "";
					try {
						while ((s = br.readLine()) != null) {
							DataPack r = new DataPack(s);
							System.out.println(r);
							server.writeLog(r.toString());
							RequestType type = r.getType();
							if (type == RequestType.signUp) {
								// 注册请求
								String usrname = r.get("userName");
								String password = r.get("password");
								String sql = "insert into user(userName,password,name) values(" + usrname + ","
										+ password + "," + usrname + ")";
								//检查该用户名是否已被注册
								ResultSet rs = server.SQLSelect("select * from user where userName = "+usrname);
								if(rs.next()) {
									bw.write("{type:signUp,status:error,body:{该用户名已被注册}}");
									bw.newLine();
									bw.flush();
									server.writeLog("{type:signUp,status:error,body:{该用户名已被注册}}");
								}else if (server.SQLUpdate(sql)) {
									bw.write("{type:signUp,status:success,body:{注册成功}}");
									bw.newLine();
									bw.flush();
									server.writeLog("{type:signUp,status:success,body:{注册成功}}");
								} else {
									bw.write("{type:signUp,status:error,body:{注册失败,服务器内部错误}}");
									bw.newLine();
									bw.flush();
									server.writeLog("{type:signUp,status:error,body:{注册失败,服务器内部错误}}");
								}

							} else if (type == RequestType.signIn) {
								// 登录请求
								// 验证密码
								String usrname = r.get("userName");
								String pswd = r.get("password");
								//检查用户名是否存在
								ResultSet rs = server.SQLSelect(
										"select password,id,name from user where userName='" + usrname + "'");
								if (!rs.next()) {
									bw.write("{type:signIn,status:error,body:{用户名不存在,请检查是否输入错误,如果您还没有账号,请先注册一个}}");
									bw.newLine();
									bw.flush();
									server.writeLog("{type:signIn,status:error,body:{用户名不存在,请检查是否输入错误,如果您还没有账号,请先注册一个}}");
								} else if (rs.getString("password").equals(pswd)) {
									//检查密码是否正确
									userId = rs.getLong("id");
									userName = rs.getString("name");
									bw.write("{type:signIn,status:success,body:{userId:" + userId + ",userName:" + userName + "}}");
									bw.newLine();
									bw.flush();
									server.writeLog("{type:signIn,status:success,body:{userId:" + userId + ",userName:" + userName + "}}");
									bw.write("{type:updateRoomCnt,status:,body:{num:"+server.getRoomCnt()+"}}");
									bw.newLine();
									bw.flush();
									bw.write("{type:rename,status:success,body:{newName:"+userName+"}}");
									bw.newLine();
									bw.flush();
									server.writeLog("{type:rename,status:success,body:{newName:"+userName+"}}");
									
								} else {
									bw.write("{type:signIn,status:error,body:{用户名或密码错误}}");
									bw.newLine();
									bw.flush();
									server.writeLog("{type:signIn,status:error,body:{用户名或密码错误}}");
								}
							} else if (type == RequestType.createRoom) {
								// 创建房间的请求
								roomId = userId;
								//将房间加入服务器的房间列表
								ChatRoom chatRoom = new ChatRoom(server, roomId);
								server.registerRoom(chatRoom);
								bw.write("{type:createRoom,status:success,body:{roomId:" + roomId + "}}");
								bw.newLine();
								bw.flush();
								server.writeLog("{type:createRoom,status:success,body:{roomId:" + roomId + "}}");
								//通知所有客户端更新在线房间数
								for(Socket client:server.getConnectedClientSet()) {
									BufferedWriter buw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); 
									buw.write("{type:updateRoomCnt,status:,body:{num:"+server.getRoomCnt()+"}}");
									buw.newLine();
									buw.flush();
								}
							} else if (type == RequestType.signOut) {
								// 退出房间,如果该用户没有退出房间,就退出房间
								server.removeClientFromRoom(roomId, client);
								//移除连接列表
								server.removeClient(client);

							} else if (type == RequestType.enterRoom) {
								//通过roomId找到房间,并将客户端socket放入该房间的客户端列表
								roomId = Long.parseLong(r.get("roomId"));
								boolean f=false;
								for(ChatRoom cr : server.getChatRoomList()) {
									if(cr.getRoomId()==roomId) {
										f=true;
										break;
									}
									bw.write("{type:enterRoom,status:error,body:{该房间不存在}}");
									bw.newLine();
									bw.flush();
								}
								if(!f) continue;
								//成功加入房间,返回成功消息
								server.registerClient(roomId, client);
								bw.write("{type:enterRoom,status:success,body:{roomId:" + roomId + "}}");
								bw.newLine();
								bw.flush();
								server.writeLog("{type:enterRoom,status:success,body:{roomId:" + roomId + "}}");
								//在该房间内广播欢迎消息
								String helloMessage = "系统(房间号:" + roomId + "):欢迎" + userName + "加入房间!!!(づ ̄ 3 ̄)づ";
								server.sendMessge(roomId, helloMessage);
								//通知该房间的客户端,更新人数
								for(ChatRoom chatRoom:server.getChatRoomList()) {
									if(chatRoom.getRoomId()==roomId) {
										chatRoom.broadcast("updateUserCnt","","num:"+chatRoom.getUserCnt());
									}
								}
							} else if (type == RequestType.exitRoom) {
								bw.write("{type:exitRoom,status:success,body:{}}");
								bw.newLine();
								bw.flush();
								server.writeLog("{type:exitRoom,status:success,body:{}}");
								server.removeClientFromRoom(roomId, client);
								server.sendMessge(roomId, "系统(房间号:" + roomId + "):" + userName + "退出了房间");
								// 房主退出房间,关闭房间
								if (roomId == userId) {
									server.removeRoom(roomId);
								}else {
									//通知该房间的客户端,更新人数
									for(ChatRoom chatRoom:server.getChatRoomList()) {
										if(chatRoom.getRoomId()==roomId) {
											chatRoom.broadcast("updateUserCnt","","num:"+chatRoom.getUserCnt());
										}
									}
								}
							} else if (type == RequestType.sendMessage) {
								//敏感词屏蔽
								String content = r.get("content");
								String message = userName + ":" + TTServer.replaceIllegalWord(content);
								//交给房间转发消息给房间内容易客户端
								server.sendMessge(roomId, message);
							}else if(type==RequestType.rename) {
								//更新数据库信息
								String sql = "update `user` set name=\""+ r.get("newName")+"\" where id =  "+userId+";";
								if(server.SQLUpdate(sql)) {
									bw.write("{type:rename,status:success,body:{newName:"+r.get("newName")+"}}");
									bw.newLine();
									bw.flush();
									userName = r.get("newName");
									server.writeLog("{type:rename,status:success,body:{}}");
								}else {
									bw.write("{type:rename,status:error,body:{改名失败,请检查名字是否过长}}");
									bw.newLine();
									bw.flush();
									server.writeLog("{type:rename,status:error,body:{改名失败,请检查名字是否过长}}");
								}
							} 
							else {
								System.out.println("未知请求");
							}
						}

					} catch (Exception e) {
						// TODO 自动生成的 catch 块
						e.printStackTrace();
						System.out.println(userName+"退出了连接");
						//将该客户端的socket从连接集移除
						server.getConnectedClientSet().remove(client);
						//关闭监听线程
						break;
					}
				}
			}
		};
		//加入线程池
		pool.submit(task);
	}
}

客户端代码
  • 客户端主体(TTClient.java)
package ttclient;

import java.awt.event.*;
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 java.net.UnknownHostException;

import dataPack.*;

public class TTClient {
	Socket socket;
	ClientReceiver clientReceiver;// 客户端接收器,用来随时接收服务器发来的消息
	ClientFrame clientFrame;// 用户界面
	TTUser user;// 当前登录用户的引用
	BufferedReader br=null;
	BufferedWriter bw = null;
	public TTClient() {
		// TODO 自动生成的构造函数存根
		try {
			// 连接服务器,本地测试暂时使用"local host"
			socket = new Socket("localhost", 9000);
			clientFrame = new ClientFrame(this);
			clientFrame.showSignInInterface();
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			clientReceiver = new ClientReceiver(this);
			clientReceiver.start();
			// 窗口关闭时,关闭输入输出
			clientFrame.addWindowListener(new WindowAdapter() {
				@Override
				public void windowClosing(WindowEvent e) {
					// TODO 自动生成的方法存根
					try {
						socket.close();
					} catch (IOException e1) {
						// TODO 自动生成的 catch 块
						e1.printStackTrace();
					}

				}
			});
		} catch (UnknownHostException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
	
	TTUser getUser() {
		return user;
	}
	public Socket getSocket() {
		return socket;
	}
	//向服务器发送请求
	public void SendRequest(DataPack r) throws IOException {
		System.out.println(r.toString());
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
		bw.write(r.toString());
		bw.newLine();
		bw.flush();
	}

	// 注册账号
	void signUp(String userName, String password) {
		DataPack a  =new DataPack(DataPack.RequestType.signUp,"{userName:'"+userName+"',password:'"+password+"'}") ;
		try {
			SendRequest(a);
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}

	// 登录账号
	void signIn(String userName, String password) {
		// 向服务器发送登录请求,
		DataPack request  = new DataPack(DataPack.RequestType.signIn,"userName:"+userName+",password:"+password) ;
		try {
			//发送请求
			SendRequest(request);
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
	void signOut() {
		DataPack request = new DataPack(DataPack.RequestType.signOut,"");
		try {
			//发送请求
			SendRequest(request);
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}

	// 创建房间
	void createRoom() {
		DataPack request = new DataPack(DataPack.RequestType.createRoom,"");
		try {
			SendRequest(request);
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
		}
		
	}

	// 进入房间
	void enterRoom(long roomId) {
		DataPack request = new DataPack(DataPack.RequestType.enterRoom,"roomId:"+roomId);
		try {
			SendRequest(request);
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
	
	//退出房间
	void exitRoom() {
		DataPack request = new DataPack(DataPack.RequestType.exitRoom,"");
		try {
			SendRequest(request);
		}catch (Exception e) {
			// TODO: handle exception
			}
		}
	// 发送消息
	void sendMessage(String content,long roomId) {
		DataPack request = new DataPack(DataPack.RequestType.sendMessage,"content:"+content);
			try {
				SendRequest(request);
			} catch (IOException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		
	}

	void rename(String newName) {
		DataPack request = new DataPack(DataPack.RequestType.rename,"newName:"+newName);
		try {
			SendRequest(request);
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
	// 接收消息
	void receiveMessage(String message) {
		// 界面上显示新消息
		clientFrame.receiveMessage(message);
	}
	
	
	public static void main(String[] args) {
		//启动客户端
		new TTClient();
	}
}

  • 客户端窗口(ClientFrame.java)
package ttclient;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.swing.*;
import javax.swing.text.DefaultCaret;

public class ClientFrame extends JFrame {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	// 窗口显示的四个模式
	final static int SIGN_IN = 0;
	final static int SIGN_UP = 1;
	final static int IN_THE_ROOM = 2;
	final static int OUT_OF_ROOM = 3;

	TTClient client;

	private Container contentPane;

	private JTextPane showArea = null;
	private JScrollPane showScrollPane = null;
	private JLabel timeLabel = null;
	private Calendar now;
	private JLabel NameLabel;//用户名显示
	private JLabel roomCntLabel;//房间计数,out_room_interface;
	private JLabel userCntLabel;//房间内人数计数,in_room_interface;
	private int CntLabelWidth,timeLableWidth,NameLableWidth,settingBtnWidth,exitBtnWidth;
	public ClientFrame(TTClient client) {
		this.client = client;
		// TODO 自动生成的构造函数存根
		setTitle("TTChatRoom");
		setIconImage(new ImageIcon("images/TTChatRoom.png").getImage());
		contentPane = getContentPane();
		contentPane.setBackground(Color.WHITE);
		timeLabel = new JLabel();
		timeLabel.setHorizontalAlignment(JLabel.CENTER);
		timeLabel.setFont(new Font("Times New Roman", Font.ITALIC, 15));
		timeLabel.setBounds(166, 0, 200, 30);
		roomCntLabel = new JLabel("", JLabel.CENTER);
		userCntLabel = new JLabel("",JLabel.CENTER);
		CntLabelWidth=timeLableWidth=NameLableWidth=190;
		settingBtnWidth=80;
		exitBtnWidth=150;
		NameLabel =  new JLabel("昵称", JLabel.CENTER);
		NameLabel.setFont(new Font("黑体", Font.ITALIC, 15));
		NameLabel.setBounds(timeLableWidth+CntLabelWidth, 0, NameLableWidth, 30);
		now = Calendar.getInstance();
		new updateTimebar().start();
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	public void showSignInInterface() {
		contentPane.removeAll();
		setBounds(700, 150, 500, 700);
		setLayout(null);
		setResizable(false);
		// 第一个子面板,显示应用图标
		JPanel iconPanel = new JPanel();
		iconPanel.setBackground(Color.white);
		iconPanel.setLayout(null);
		// 图标
		ImageIcon icon = new ImageIcon("images/TTChatRoom.png");
		// 图标缩放
		icon.setImage(icon.getImage().getScaledInstance(100, 100, Image.SCALE_DEFAULT));
		// 图标标签
		JLabel iconLabel = new JLabel(icon);
		iconLabel.setBounds(0, 0, 500, 120);
		// 文字标签
		JLabel titleLabel = new JLabel("Sign in to TTChatRoom", JLabel.CENTER);
		titleLabel.setFont(new Font("Times New Roman", 0, 30));
		titleLabel.setBounds(0, 120, 500, 30);
		iconPanel.add(iconLabel);
		iconPanel.add(titleLabel);
		iconPanel.setBounds(0, 30, 500, 180);

		// 第二个子面板,显示玩家登录表单
		JPanel formPanel = new JPanel();
		formPanel.setBackground(Color.white);
		GridLayout gL = new GridLayout(5, 1);
		gL.setVgap(8);
		formPanel.setLayout(gL);
		JLabel usernamrLabel = new JLabel("Username or email address");
		usernamrLabel.setFont(new Font("Times New Roman", Font.BOLD, 25));
		JTextField nameInput = new JTextField();
		nameInput.setFont(new Font("黑体", Font.BOLD, 25));
		JLabel passwordLabel = new JLabel("Password");
		passwordLabel.setFont(new Font("Times New Roman", Font.BOLD, 25));
		JButton signInBtn = new JButton("Sign in");
		signInBtn.setFont(new Font("Times New Roman", Font.BOLD, 25));
		signInBtn.setBackground(new Color(50, 201, 85));
		JPasswordField passwordInput = new JPasswordField();
		passwordInput.setFont(new Font("宋体", Font.BOLD, 25));
		signInBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// 处理登录的代码
				String userName = nameInput.getText();
				String passWord = new String(passwordInput.getPassword());
				if (userName.length() == 0) {
					showErrorMessage("请输入用户名!");
					return;
				} else if (passWord.length() == 0) {
					showErrorMessage("请输入密码!");
					return;
				}
				client.signIn(userName, passWord);
			}
		});
		formPanel.add(usernamrLabel);
		formPanel.add(nameInput);
		formPanel.add(passwordLabel);
		formPanel.add(passwordInput);
		formPanel.add(signInBtn);
		formPanel.setBounds(50, 200, 400, 300);

		// 第三个子面板,引导新用户注册
		JPanel signUpPanel = new JPanel();
		signUpPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
		JLabel guideLabel = new JLabel("New to TTChatRoom?");
		guideLabel.setFont(new Font("Times New Roman", Font.BOLD, 25));
		JButton toSignUpBtn = new JButton("Create an account.");
		toSignUpBtn.setBackground(new Color(63, 146, 210));
//			toSignUpBtn.setBackground(new Color(50,201,85));
		toSignUpBtn.setFont(new Font("Times New Roman", Font.BOLD, 25));
		toSignUpBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				showSignUpInterface();
			}
		});
		signUpPanel.add(guideLabel);
		signUpPanel.add(toSignUpBtn);
		signUpPanel.setBounds(50, 510, 400, 100);
		contentPane.add(iconPanel);
		contentPane.add(formPanel);
		contentPane.add(signUpPanel);
		contentPane.repaint();
		setVisible(true);
		
	}

	public void showSignUpInterface() {
		contentPane.removeAll();
		setBounds(700, 150, 500, 700);
		setLayout(null);
		setResizable(false);
		// 第一个子面板,显示应用图标
		JPanel iconPanel = new JPanel();
		iconPanel.setBackground(Color.white);
		iconPanel.setLayout(null);
		// 图标
		ImageIcon icon = new ImageIcon("images/TTChatRoom.png");
		// 图标缩放
		icon.setImage(icon.getImage().getScaledInstance(100, 100, Image.SCALE_DEFAULT));
		// 图标标签
		JLabel iconLabel = new JLabel(icon);
		iconLabel.setBounds(0, 0, 500, 120);
		// 文字标签
		JLabel titleLabel = new JLabel("Create your account", JLabel.CENTER);
		titleLabel.setFont(new Font("Times New Roman", 0, 30));
		titleLabel.setBounds(0, 120, 500, 30);
		iconPanel.add(iconLabel);
		iconPanel.add(titleLabel);
//			iconPanel.setBorder(BorderFactory.createLineBorder(Color.black));
		iconPanel.setBounds(0, 30, 500, 180);

		// 第二个子面板,显示玩家注册表单
		JPanel formPanel = new JPanel();
		formPanel.setBackground(Color.white);
		GridLayout gL = new GridLayout(7, 1);
		gL.setVgap(8);
		formPanel.setLayout(gL);
		JLabel usernameLabel = new JLabel("Username or email address");
		usernameLabel.setFont(new Font("Times New Roman", Font.BOLD, 25));
		JTextField nameInput = new JTextField();
		nameInput.setFont(new Font("Times New Roman", Font.BOLD, 25));
		JLabel passwordLabel = new JLabel("Password");
		passwordLabel.setFont(new Font("黑体", Font.BOLD, 25));
		JLabel passwordLabel1 = new JLabel("Confirm Password");
		passwordLabel1.setFont(new Font("黑体", Font.BOLD, 25));
		JButton signUpBtn = new JButton("Sign up for TTChatRoom");
		signUpBtn.setFont(new Font("Times New Roman", Font.BOLD, 25));
		signUpBtn.setBackground(new Color(63, 146, 210));
		JPasswordField passwordInput = new JPasswordField();
		passwordInput.setFont(new Font("宋体", Font.BOLD, 25));
		JPasswordField passwordInput1 = new JPasswordField();
		passwordInput1.setFont(new Font("宋体", Font.BOLD, 25));
		signUpBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// 处理注册的代码
				String userName = nameInput.getText();
				String pswd = new String(passwordInput.getPassword());
				String pswd1 = new String(passwordInput1.getPassword());
				if (userName.length() == 0) {
					showErrorMessage("请输入用户名!");
					return;
				}
				if (pswd.length() == 0) {
					showErrorMessage("请输入密码!");
					return;
				}
				if (pswd1.length() == 0) {
					showErrorMessage("请输入第二遍密码!");
					return;
				}
				for (int i = 0; i < userName.length(); i++) {
					char ch = userName.charAt(i);
					if (!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '@'
							|| ch == '_' || ch == '.')) {
						showErrorMessage("用户名中不可以有特殊字符,用户名应为大小写字母(a~z,A~Z),数字(0~9)和#、_、.等符号的组合。");
						return;
					}
				}
				if (!pswd.equals(pswd1)) {
					showErrorMessage("输入的两次密码不一样");
					return;
				}
				client.signUp(userName, pswd);
			}
		});
		formPanel.add(usernameLabel);
		formPanel.add(nameInput);
		formPanel.add(passwordLabel);
		formPanel.add(passwordInput);
		formPanel.add(passwordLabel1);
		formPanel.add(passwordInput1);
		formPanel.add(signUpBtn);
		formPanel.setBounds(50, 200, 400, 300);

		// 第三个子面板,引导用户回到登录界面
		JPanel signUpPanel = new JPanel();
		signUpPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
		JLabel guideLabel = new JLabel("Already have an account?");
		guideLabel.setFont(new Font("Times New Roman", Font.BOLD, 25));
		JButton toSignInBtn = new JButton("To sign in.");
		toSignInBtn.setBackground(new Color(50, 201, 85));
		toSignInBtn.setFont(new Font("Times New Roman", Font.BOLD, 25));
		toSignInBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				showSignInInterface();
			}
		});
		signUpPanel.add(guideLabel);
		signUpPanel.add(toSignInBtn);
		signUpPanel.setBounds(50, 510, 400, 100);
		contentPane.add(iconPanel);
		contentPane.add(formPanel);
		contentPane.add(signUpPanel);
		
		contentPane.repaint();
		setVisible(true);
	}
	void showOutOfRoomInterface() {
		contentPane.removeAll();
		setBounds(getX(), getY(), 800, 600);
		contentPane.setLayout(null);
		// 子面板1 ,显示当前状态信息
		JPanel statusPanel = new JPanel();
		statusPanel.setLayout(null);
	
		roomCntLabel.setFont(new Font("黑体", Font.ITALIC, 15));
		roomCntLabel.setBounds(0, 0, CntLabelWidth, 30);
//		timeLabel = new JLabel();
//		timeLabel.setHorizontalAlignment(JLabel.CENTER);
//		timeLabel.setFont(new Font("黑体", Font.ITALIC, 15));
//		timeLabel.setBounds(216, 0, 266, 30);
//		NameLabel = new JLabel("昵称:"+client.getUser().getName(), JLabel.CENTER);
		ImageIcon signOutIcon = new ImageIcon("images/Sign out.png");
		signOutIcon = new ImageIcon(signOutIcon.getImage().getScaledInstance(28, 28, Image.SCALE_DEFAULT));
		JButton signOutBtn = new JButton("sign out", signOutIcon);
		signOutBtn.setBackground(Color.lightGray);
		signOutBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				client.signOut();
			}
		});
		signOutBtn.setBounds(NameLableWidth+CntLabelWidth+timeLableWidth+settingBtnWidth, 0, exitBtnWidth, 30);
		ImageIcon settingIcon = new ImageIcon("images/setting.png");
		settingIcon = new ImageIcon(settingIcon.getImage().getScaledInstance(28, 28, Image.SCALE_DEFAULT));
		JButton settingButton = new JButton("修改");
		settingButton.setBackground(Color.lightGray);
		settingButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				String newName = JOptionPane.showInputDialog("请输入新名称:");
				if(newName!=null&&newName.length()!=0) {
					client.rename(newName);
				}else {
					
				}
			}
		});
		settingButton.setBounds(NameLableWidth+timeLableWidth+CntLabelWidth,0,settingBtnWidth,30);
		statusPanel.add(settingButton);
		statusPanel.add(roomCntLabel);
		statusPanel.add(timeLabel);
		statusPanel.add(NameLabel);
		statusPanel.add(signOutBtn);
		statusPanel.setBounds(0, 0, 800, 30);

		// 子面板二,提供创建房间和加入房间按钮
		JPanel roomBtnsPanel = new JPanel();
		roomBtnsPanel.setLayout(null);
		roomBtnsPanel.setBackground(Color.white);
		// 创建房间按钮
		ImageIcon createIcon = new ImageIcon(
				new ImageIcon("images/createRoom1.png").getImage().getScaledInstance(150, 150, Image.SCALE_DEFAULT));
		JButton createRoomBtn = new JButton(createIcon);
		createRoomBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				client.createRoom();
			}
		});
		createRoomBtn.setBackground(Color.white);
		createRoomBtn.setBounds(120, 73, 160, 160);
		JLabel createLabel = new JLabel("创建房间", JLabel.CENTER);
		createLabel.setFont(new Font("黑体", Font.PLAIN, 20));
		createLabel.setBounds(120, 240, 160, 40);
		roomBtnsPanel.add(createRoomBtn);
		roomBtnsPanel.add(createLabel);
		// 加入房间按钮
		ImageIcon joinIcon = new ImageIcon(
				new ImageIcon("images/joinRoom.png").getImage().getScaledInstance(150, 150, Image.SCALE_DEFAULT));
		JButton enterRoomBtn = new JButton(joinIcon);
		enterRoomBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				String roomId = JOptionPane.showInputDialog("请输入房间号");
				try {
					long rd = Long.parseLong(roomId);
					client.enterRoom(rd);
				} catch (NumberFormatException exception) {
					// TODO: handle exception
					JOptionPane.showMessageDialog(enterRoomBtn, "请输入正确的房间号", "房间号错误", JOptionPane.ERROR_MESSAGE);
					return;
				}
			}
		});
		enterRoomBtn.setBackground(Color.white);
		enterRoomBtn.setBounds(320, 73, 160, 160);
		JLabel joinLabel = new JLabel("加入房间", JLabel.CENTER);
		joinLabel.setFont(new Font("黑体", Font.PLAIN, 20));
		joinLabel.setBounds(320, 240, 160, 40);
		roomBtnsPanel.add(joinLabel);
		roomBtnsPanel.add(enterRoomBtn);
		// 帮助按钮
		ImageIcon helpIcon = new ImageIcon(
				new ImageIcon("images/help.png").getImage().getScaledInstance(150, 150, Image.SCALE_DEFAULT));
		JButton helpBtn = new JButton(helpIcon);
		helpBtn.setBackground(Color.white);
		helpBtn.setBounds(520, 73, 160, 160);
		JLabel helpLabel = new JLabel("帮  助", JLabel.CENTER);
		helpLabel.setFont(new Font("黑体", Font.PLAIN, 20));
		helpLabel.setBounds(520, 240, 160, 40);
		helpBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				showErrorMessage("功能尚未完善");
			}
		});
		roomBtnsPanel.add(helpLabel);
		roomBtnsPanel.add(helpBtn);
		roomBtnsPanel.setBounds(0, 33, 800, 400);
		contentPane.add(statusPanel);
		contentPane.add(roomBtnsPanel);
		contentPane.repaint();
	}

	void showInRoomInterface() {
		contentPane.removeAll();
		setBounds(getX(),getY(), 800, 600);
		contentPane.setLayout(null);
		// 子面板1 ,显示当前状态信息
		JPanel statusPanel = new JPanel();
		statusPanel.setLayout(null);
		userCntLabel.setFont(new Font("黑体", Font.ITALIC, 15));
		userCntLabel.setBounds(0, 0, CntLabelWidth, 30);
//		userNameLabel = new JLabel("昵称:"+client.getUser().getName(), JLabel.CENTER);
		setVisible(true);
		ImageIcon signOutIcon = new ImageIcon("images/Sign out.png");
		signOutIcon = new ImageIcon(signOutIcon.getImage().getScaledInstance(28, 28, Image.SCALE_DEFAULT));
		JButton exitBtn = new JButton("exit room", signOutIcon);
		
		exitBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				client.exitRoom();
			}
		});
		ImageIcon settingIcon = new ImageIcon("images/setting.png");
		settingIcon = new ImageIcon(settingIcon.getImage().getScaledInstance(28, 28, Image.SCALE_DEFAULT));
		JButton settingButton = new JButton("修改");
		settingButton.setBackground(Color.lightGray);
		settingButton.setBounds(NameLableWidth+timeLableWidth+CntLabelWidth,0,settingBtnWidth,30);
		settingButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				showErrorMessage("房间内不能修改昵称哦");
			}
		});
		exitBtn.setBackground(Color.lightGray);
		exitBtn.setBounds(CntLabelWidth+timeLableWidth+NameLableWidth+settingBtnWidth, 0, exitBtnWidth, 30);
		statusPanel.add(settingButton);
		statusPanel.add(userCntLabel);
		statusPanel.add(timeLabel);
		statusPanel.add(NameLabel);
		statusPanel.add(exitBtn);
		statusPanel.setBounds(0, 0, 800, 30);
		// 子面板二,聊天信息面板
		JPanel showPanel = new JPanel();
		showPanel.setBounds(0, 32, 800, 450);
		showPanel.setLayout(null);
		showPanel.setBackground(Color.lightGray);
		showArea = new JTextPane();
		showScrollPane = new JScrollPane();
		showScrollPane.setViewportView(showArea);
		showScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		DefaultCaret caret = (DefaultCaret)showArea.getCaret();
		caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
		showArea.setBounds(0, 0, 780, 450);
		showArea.setEditable(false);
		showArea.setFont(new Font("黑体", Font.PLAIN, 18));
		showScrollPane.setBounds(0, 0, 795, 450);
		showPanel.add(showScrollPane);
		// 子面板三,输入面板
		JPanel inputPanel = new JPanel();
		inputPanel.setLayout(null);
		inputPanel.setBounds(0, 482, 800, 80);
		JTextField inpuTextField = new JTextField();
		inpuTextField.setBounds(20, 10, 650, 60);
		inpuTextField.setFont(new Font("黑体", Font.PLAIN, 20));
		ImageIcon submitIcon = new ImageIcon(
				new ImageIcon("images/submit.png").getImage().getScaledInstance(60, 60, Image.SCALE_DEFAULT));
		JButton submitBtn = new JButton(submitIcon);
		submitBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO 自动生成的方法存根
				String content = inpuTextField.getText().trim();
				if (content.length() == 0) {
					showErrorMessage("请不要发送空白消息");
					return;
				}
				client.sendMessage(content, client.getUser().getRoomId());
				inpuTextField.setText("");
			}
		});
//			submitBtn.setFont(new Font("黑体",Font.PLAIN,20));
		submitBtn.setBounds(680, 10, 80, 60);
		submitBtn.setBackground(Color.white);
		inputPanel.add(inpuTextField);
		inputPanel.add(submitBtn);
		contentPane.add(showPanel);
		contentPane.add(statusPanel);
		contentPane.add(inputPanel);
		contentPane.repaint();
	}

	void showErrorMessage(String errMessage) {
		JOptionPane.showMessageDialog(null, errMessage, "错误提示", JOptionPane.ERROR_MESSAGE);
	}

	void receiveMessage(String message) {
		if (showArea != null) {
			String oldString = showArea.getText();
			StringBuilder mess  = new StringBuilder();
			for(int i=0;i<message.length();i++) {
				if(i!=0&&i%42==0) {
					mess.append("\n");
				}
				mess.append(message.charAt(i));
			}
			if (oldString.length() != 0)
				showArea.setText(oldString + "\n" + mess);
			else {
				showArea.setText(""+mess);
			}
		}
	}
	
	void updateRoomCnt(int num) {
		if(roomCntLabel!=null) {
			roomCntLabel.setText("当前活跃房间数:"+num);
			roomCntLabel.repaint();
		}
	} 
	void updateUserCnt(int num) {
		if(userCntLabel!=null) {
			userCntLabel.setText("当前房间("+ client.getUser().getRoomId()+" )人数:"+num);
			userCntLabel.repaint();
		}
	}
	
	void updateUserName(String newName) {
		if(NameLabel!=null) {
		NameLabel.setText("昵称:"+newName);
		}
		System.out.println("updateName");
	}
	private class updateTimebar extends Thread {
		@Override
		public void run() {
			while(timeLabel!=null) {
				try {
					Thread.sleep(1000);
					now.setTime(new Date());
					timeLabel.setText(now.getTime().toLocaleString());
				} catch (InterruptedException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}
		}
	}

	

	public static void main(String[] args) {
		new ClientFrame(null).showSignUpInterface();
	}
}

  • 客户端接收器(ClientReceiver.java)
package ttclient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import javax.swing.JOptionPane;
import dataPack.DataPack.*;
import dataPack.DataPack;

public class ClientReceiver extends Thread {
	TTClient client;
	Socket clientSocket;
	ClientFrame clientFrame;
	public ClientReceiver(TTClient client) {
		// TODO 自动生成的构造函数存根
		this.client = client;
		clientFrame = client.clientFrame;
		clientSocket = client.getSocket();
	}

	@Override
	public void run() {
		// TODO 自动生成的方法存根
		BufferedReader br = null;
		try {
			br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		String content = "";
		try {
			while (!clientSocket.isClosed()&&(content = br.readLine()) != null&&!clientSocket.isClosed()) {
				System.out.println(" receiver: "+content);
				DataPack response = new DataPack(content);
				DataPack.RequestType type = response.getType();
				String status = response.getStatus();
				if(type==RequestType.sendMessage) {
					//接收消息,显示
					client.receiveMessage(response.getBody());
				}else if(type==RequestType.signIn) {
					//登录成功,换界面
					if(status.equals("success")) {
						client.user = new TTUser(Long.parseLong(response.get("userId")),response.get("userName"));
						clientFrame .showOutOfRoomInterface();
					}else {
						//登录失败,给出提示
						JOptionPane.showMessageDialog(clientFrame,response.getBody(),"登录失败",JOptionPane.ERROR_MESSAGE);
					}
				}else if(type==RequestType.signUp) {
					//注册成功
					if(status.equals("success")) {
						JOptionPane.showMessageDialog(clientFrame,"注册成功,快去登录试试吧!");
					}else if(response.getBody()!=null)
						clientFrame.showErrorMessage(response.getBody());
					else clientFrame.showErrorMessage("注册失败");
				}else if(type==RequestType.signOut) {
					//退出登录成功返回登录界面
					if(status.equals("success")) {
						clientFrame.showSignInInterface();
					}else {
						//失败
						clientFrame.showErrorMessage(response.getBody());
					}
				}else if(type==RequestType.createRoom) {
					//创建房间成功,自动进入房间
					if(status.equals("success")) {
						long roomId = Long.parseLong(response.get("roomId"));
						client.enterRoom(roomId);
						}else {
						//失败
					}
				}else if(type==RequestType.enterRoom) {
					//进入房间成功,切换界面
					if(status.equals("success")) {
						client.getUser().setRoomNum(Integer.parseInt(response.get("roomId")));
						System.out.println("进入房间成功,开始接收房间消息");
						clientFrame.showInRoomInterface();
					}else {
						//失败
						clientFrame.showErrorMessage(response.getBody());
					}
				}else if(type==RequestType.exitRoom) {
					//退出房间成功,切换界面
					if(status.equals("success")) {
						clientFrame.showOutOfRoomInterface();
					}else if(status.equals("roomClosed")){
						client.exitRoom();
						clientFrame.showErrorMessage(response.getBody());
					}
					else{
						//失败
					}
				}else if(type==RequestType.updateRoomCnt){
					//更新界面显示
					clientFrame.updateRoomCnt(Integer.parseInt(response.get("num")));
				}else if(type==RequestType.updateUserCnt) {
					//更新界面显示
					clientFrame.updateUserCnt(Integer.parseInt(response.get("num")));
				}else if(type==RequestType.rename) {
					if(status.equals("success")) {
						//更新用户状态
						client.getUser().setName(response.get("newName"));
						//更新界面显示
						clientFrame.updateUserName(response.get("newName"));
					}else {
						clientFrame.showErrorMessage(response.getBody());
					}
				}
				else {
					System.out.println("未知请求");
				}
			}

		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			System.out.println("客户端接收器关闭");
		}

	}
}

  • 用户(TTUser.java)
package ttclient;

public class TTUser {
	private String name;//用户名
	private long id;//用户编号
	private long roomId;//当前所在的房间编号
	private boolean isRoomOwner;//是否为该房间的房主
	public TTUser(long Id,String name) {
		// TODO 自动生成的构造函数存根
		this.setId(Id);
		this.setName(name);
		setRoomNum(-1);
		setRoomOwner(false);
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public long getRoomId() {
		return roomId;
	}
	public void setRoomNum(long roomNum) {
		this.roomId = roomNum;
	}
	public boolean isRoomOwner() {
		return isRoomOwner;
	}
	public void setRoomOwner(boolean isRoomOwner) {
		this.isRoomOwner = isRoomOwner;
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}	
}
数据包代码
  • 数据包类(DataPack.java)
package dataPack;

public class DataPack {
	//请求的所有类型
	 public static enum RequestType{
			signUp,signIn,signOut,enterRoom,createRoom,exitRoom,sendMessage,updateRoomCnt,updateUserCnt,rename
		}
	final private RequestType type;
	final private String body;
	final private String status;
	//通过字符串DataPack对象
	public DataPack(String dataPackString) {
		this.status = getStatus(dataPackString);
		this.type = getType(dataPackString);
		this.body = getBody(dataPackString);
	}
	//请求构造方法,通过类型和数据构成
	public DataPack(RequestType type,String data) {
		this.status = null;
		this.type = type;
		this.body = data;
	}
	
	//将字符串解析为DataPack对象
	public static  DataPack parse(String requestString) {
		return new DataPack(requestString);
	}

	public RequestType getType() {
		return type;
	}
	public String getBody() {
		return body;
	}
	public String getStatus() {
		return status;
	}
	
	//获取特定键对应的值
	public String get(String key) {
		int index= body.indexOf(key);
		if(index!=-1) {
			return body.substring(index+key.length()+1).split(",")[0].split("}")[0];
		}
		return null;
	}
	
	public static RequestType getType(String requestString) {
		RequestType type =null;
		int index  =requestString.indexOf("type:");
		if(index==-1) return type;
		 String typeString=requestString.substring(index+5).split(",")[0];
		 for(RequestType type1:RequestType.values()) {
			 if(typeString.equals(type1.toString()))
				 return type1;
		 }
		 return type;
	}
	
	public static String getBody(String dataPackString) {
		String body=null;
		int index = dataPackString.indexOf("body:{");
		if(index==-1) return null;
		body = dataPackString.substring(index+6,dataPackString.lastIndexOf("}}"));
		return body;
	}
	
	public static String getStatus(String responseString) {
		String status=null;
		int index = responseString.indexOf("status:");
		if(index==-1) return status;
		return responseString.substring(index+7).split(",")[0].split("}}")[0];
	}
	
	public String toString() {
		return "{type:"+getType()+",status:"+getStatus()+",body:{"+getBody()+"}}";
	}	
}

项目总结

这个项目实现了多人多房间的网络聊天功能,使用数据库记录玩家账户信息,实现了基本的注册登录功能,已存在的功能实现和界面的设计都是比较好的,但是缺少了足够强大的数据库来作为支撑,只能支持实时聊天,既不能保存用户群组信息,也不能在用户离线时接收消息,等待用户连接后再推送给用户。此项目只能像一个文字版的腾讯会仪一样,客户在约定好的时间进入特定的房间进行聊天。但是尽管如此,这个项目的可拓展性还是很强的,它拥有一套简单而强大的数据交换协议。
在技术方面,此项目使用了几乎在Java基础课程中学到的所有基础知识,使用了文件的输入输出、网络编程、数据库编程、多线程编程还使用了线程池技术来管理线程。此项目是通过对Java基础知识学习后第一次综合实战项目,也是对自己在前段时间学习效果的检测与考验,整体来说还算可以,但是仍然发现了个人在项目开发时有一些缺点,比如在需求分析时有时分析的不够深入,在软件设计过程中也存在犯错的地方。此外由于时间关系这个项目还有很多不完善的地方比如异常处理,还有很多代码逻辑有待优化。
一个阶段的结束意味着下一个阶段的开始,在以后的学习开发中要吸取从这次课程设计过程中的经验教训,戒骄戒躁,砥砺前行。

附加说明

此项目的所代码,依赖文件和数据库备份文件我都上传到了我的资源中:点击下载
在这里插入图片描述


- THE END -
  • 17
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除,从账户中取出amt,如果amt>账户余额抛出异常,一个实体Bean可以表示不同的数据实例,我们应该通过主键来判断删除哪个数据实例…… ejbCreate函数用于初始化一个EJB实例 5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口函数得到远程接口的引用,用远程接口的引用访问EJB。 EJB中JNDI的使用源码例子 1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户可以在终端上直接地使用它,但是它的主要作用是供程序使用的。本规范尝试满足大型主机、微型主机、个人工作站、和TACs 的不同需求。例如,容易实现协议的设计Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天通信演示代码 2个目标文件,一个服务器,一个客户端。 Java Telnet客户端实例源码 一个目标文件,演示Socket的使用。 Java 组播组中发送和接受数据实例 3个目标文件。 Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密   Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。   设定字符串为“张三,你好,我是李四”   产生张三的密钥对(keyPairZhang)   张三生成公钥(publicKeyZhang)并发送给李四,这里发送的是公钥的数组字节   通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张三编码后的公钥,将其解码,李四用张三的公钥加密信息,并发送给李四,张三用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 同上 java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥   Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从文件中得到公钥编码的字节数组、如何从字节数组解码公钥。 Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输   Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲区、写入数据到文件、关闭输入流、关闭套接字关闭输出流、输出错误信息等Java编程小技巧。 Java数组倒置 简单 Java图片加水印,支持旋转和透明度设置 摘要:Java源码,文件操作,图片水印   util实现Java图片水印添加功能,有添加图片水印和文字水印,可以设置水印位置,透明度、设置对线段锯齿状边缘处理、水印图片的路径,水印一般格式是gif,png,这种图片可以设置透明度、水印旋转等,可以参考代码加以改进做成小工具。 Java右键弹出菜单源码 简单 Java圆形按钮实例代码,含注释 两个目标文件,自绘button。 Java圆形电子时钟源代码 1个目标文件 内容索引:JAVA源码,系统相关,电子钟   用JAVA编写的指针式圆形电子钟,效果图如下所示,其实代码很简单,希望对你有帮助。 Message-Driven Bean EJB实例源代码 2个目标文件 摘要:Java源码,初学实例,EJB实例   Message-Driven Bean EJB实例源代码,演示一个接收购物订单的消息驱动Bean,处理这个订单同时通过e-mail的形式   //给客户发一个感谢消息,消息驱动Bean必须实现两个接口MessageDrivenBean和MessageListener   在对象创建的过程中将被容器调用,onMessage函数方法接收消息参数,将其强制转型为合适的消息类型,同时打印出消息的内容。同时一个mail note将被发送给消息发送者,发送一个e-mail通知给由recipient参数确定的e-mail账号,查询mail 服务器的会话……   还包括消息客户端程序,通过连接创建会话。创建发送者和映射消息。发送消息,同时对文本进行少量修改,发送end-of-messages消息,最后关闭连接。 Tcp服务端与客户端的JAVA实例源代码 2个目标文件 摘要:Java源码,文件操作,TCP,服务器   Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3个目标文件 第一步:运行ServerData.java 启动服务器,然后服务器处于等待状态 第二步:运行LoginData.java 启动(客户端)登陆界面 输入用户名 ip为本机localhost 第三步:在登陆后的界面文本框输入文本,然后发送 可以同时启动多个客户端 实现群聊。 浮动的广告 嵌套在html中 各种EJB之间的调用示例 7个目标文件 摘要:Java源码,初学实例,EJB调用实例   各种EJB之间的调用源码示例,用远程接口的引用访问EJB、函数将被FirstEJB调用,同时它将调用secondEJB 基于JAVA的UDP服务器模型源代码 2个目标文件 摘要:Java源码,网络相关,UDP   基于JAVA的UDP服务器模型源代码,内含UDP服务器端模型和UDP客户端模型两个小程序,向JAVA初学者演示UDP C/S结构的原理。 简单聊天软件CS模式 2个目标文件 一个简单的CS模式的聊天软件,用socket实现,比较简单。 凯撒加密解密程序 1个目标文件 1、程序结构化,用函数分别实现 2、对文件的加密,解密输出到文件 利用随机函数抽取幸运数字 简单 EJB的真实世界模型(源代码) 15个目标文件 摘要:Java源码,初学实例,基于EJB的真实世界模型   基于EJB的真实世界模型,附源代码,部分功能需JSP配合完成。 J2ME优化压缩PNG文件 4个目标文件 内容索引:JAVA源码,综合应用,J2me游戏,PNG,图形处理   这是个J2ME控制台程序,它能剔除PNG文件中的非关键数据段,减少文件大小从而达到压缩图片的目的。而图片的质量并不会受到损失。使用时候只需在控制台窗口执行jar就可以了。 Java 3DMenu 界面源码 5个目标文件 内容索引:Java源码,窗体界面,3DMenu   Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码,文件操作,压缩包查看   Java zip压缩包查看程序,应用弹出文件选择框,选择ZIP格式的压缩文件,可以像Winrar软件一样查看压缩文件内部的文件及文件夹,源码截图如上所示。 Java 数字签名、数字证书生成源码 2个目标文件 摘要:JAVA源码,系统相关,数字签名,数字证书   Java 数字签名、数字证书的相关实例。   关于数字签名:产生RSA密钥对(myKeyPair),得到RSA密钥对,产生Signature对象,对用私钥对信息(info)签名,用指定算法产生签名对象,用私钥初始化签名对象,将待签名的数据传送给签名对象(须在初始化之后),用公钥验证签名结果,使用公钥初始化签名对象,用于验证签名。   数字证书:从文件中读取数字证书,生成文件输入流,输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录   一个Java+ajax写的登录实例,附有JAVA源文件,JAVA新手朋友可以学习一下。 JAVA+JSP的聊天室 8个目标文件 简单 JavaScript万年历 显示出当前时间及年份,还可以选择年份及月份和日期 Java编写的HTML浏览器 一个目标文件 摘要:Java源码,网络相关,浏览器   Java编写的HTML浏览器源代码,一个很简单甚至不算是浏览器的HTML浏览器,使用方法:   可直接输入文件名或网络地址,但必需事先连入网络Java编写的山寨QQ,多人聊天+用户在线 21个目标文件 摘要:JAVA源码,媒体网络,山寨QQ,Java聊天程序   Java编写的山寨QQ,多人聊天+用户在线,程序分服务端和客户端,典型C/S结构,   当用户发送第一次请求的时候,验证用户登录,创建一个该qq号和服务器端保持通讯连接得线程,启动该通讯线程,通讯完毕,关闭Scoket。   QQ客户端登录界面,中部有三个JPanel,有一个叫选项卡窗口管理。还可以更新好友列表,响应用户双击的事件,并得到好友的编号,把聊天界面加入到管理类,设置密码保护等。 Java编写的网页版魔方游戏 内容索引:JAVA源码,游戏娱乐,魔方,网页游戏   Java编写的网页版魔方游戏,编译后生成.class文件,然后用HTML去调用,不过运行时候需要你的浏览器安装有运行Class的插件。Java代码实现部分,比较有意思,也具参考性。像坐标控制、旋转矩阵、定时器、生成图像、数据初始化、矩阵乘法、坐标旋转、判断是否是顺时针方向排列、鼠标按下、放开时的动作等,都可在本源码中得以体现。 Java编写的显示器显示模式检测程序 2个目标文件 内容索引:JAVA源码,系统相关,系统信息检测   用JAVA编写了一个小工具,用于检测当前显示器也就是显卡的显示模式,比如分辨率,色彩以及刷新频率等。 Java波浪文字制作方法及源代码 1个目标文件 摘要:Java源码,初学实例,波浪文字   Java波浪文字,一个利用Java处理字符的实例,可以设置运动方向参数,显示文本的字符数组,高速文本颜色,显示字体的 FontMetrics对象,得到Graphics实例,得到Image实例,填充颜色数组数据,初始化颜色数组。 Java吃豆子游戏源代码 6个目标文件 内容索引:JAVA源码,游戏娱乐,JAVA游戏源码   JAVA编写的吃豆子游戏,类似疯狂坦克一样,至少界面有点像。大家可以看截图。 Java网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java调色板面板源代码 1个目标文件 摘要:Java源码,窗体界面,调色板   使用Java语言编写的一款用于反映颜色变化的面板,也就是大家熟悉的颜色调色板演示程序。原理是初始化颜色选择按钮,然后为颜色选择按钮增加事件处理事件,最后实例化颜色选择器。 Java二进制IO类与文件复制操作实例 16个目标文件 内容索引:Java源码,初学实例,二进制,文件复制   Java二进制IO类与文件复制操作实例,好像是一本书的例子,源代码有的是独立运行的,与同目录下的其它代码文件互不联系,这些代码面向初级、中级Java程序员。 Java访问权限控制源代码 1个目标文件 摘要:Java源码,文件操作,权限控制   Java访问权限控制,为Java操作文件、写入文件分配合适的权限,定义写到文件的信息、定义文件,输出到c:/hello.txt、写信息到文件、关闭输出流。 Java绘制图片火焰效果 1个目标文件 摘要:Java源码,图形操作,火焰效果   Java绘制图片火焰效果,源代码相关注释:前景和背景Image对象、Applet和绘制火焰的效果的Image对象、Applet和绘制火焰的效果的Graphics对象、火焰效果的线程、Applet的高度,图片到图片装载器、绘制火焰效果的X坐标,Y坐标、得到X坐标,Y坐标值、绘制火焰效果Image…… Java加密解密工具集 JCT v1.0源码包 5个目标文件 内容索引:JAVA源码,综合应用,JCT,加密解密   WDSsoft的一款免费源代码 JCT 1.0,它是一个Java加密解密常用工具包。 Java局域网通信——飞鸽传书源代码 28个目标文件 内容索引:JAVA源码,媒体网络,飞鸽传书   Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和客户端 2个目标文件,如题。 Java目录监视器源程序 9个目标文件 内容索引:JAVA源码,综合应用,目录监视   用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,日期选择   Java语言开发的简洁实用的日期选择控件,源码文件功能说明:   [DateChooser.java] Java 日期选择控件(主体类) [public]   [TablePanel.java] 日历表格面板   [ConfigLine.java] 控制条类   [RoundBox.java] 限定选择控件   [MonthMaker.java] 月份表算法类   [Pallet.java] 调色板,统一配色类 Java扫雷源码 Java生成自定义控件源代码 2个目标文件 Java实现HTTP连接与浏览,Java源码下载 1个目标文件 摘要:Java源码,网络相关,HTTP   Java实现HTTP连接与浏览,Java源码下载,输入html文件地址或网址,显示页面和HTML源文件,一步步的实现过程请下载本实例的Java源码代码中包括丰富的注释,对学习有帮助。 Java实现的FTP连接与数据浏览程序 1个目标文件 摘要:Java源码,网络相关,FTP   Java实现的FTP连接与数据浏览程序,实现实例化可操作的窗口。   部分源代码摘录:   ftpClient = new FtpClient(); //实例化FtpClient对象   String serverAddr=jtfServer.getText(); //得到服务器地址   String user=jtfUser.getText(); //得到用户名   String pass=jtfPass.getPassword().toString(); //得到密码   ftpClient.openServer(serverAddr); //连接到服务器   ftpClient.login(user,pass); //在服务器上注册   InputStream is=ftpClient.list(); //得到服务器目录与文件列表输入流   StringBuffer info=new StringBuffer(); //实例化StringBuffer对象,用于输出信息   int ch;   while ((ch=is.read())>=0){ //未读完列表,则继续   info.append((char)ch); //增加信息   …… Java实现的点对点短消息发送协议(smpp)开发包源码 70个目标文件,如题。 Java实现的放大镜效果附有源文件 1个目标文件 内容索引:Java源码,初学实例,放大镜,哈哈镜   Java实现的放大镜效果附有源文件,编译环境最近出了问题,因此没有测试,也没有抓到图,希望各位自行测试一下。 Java实现跟踪鼠标运行坐标的源码 1个目标文件 内容索引:JAVA源码,系统相关,鼠标钩子   Java编写的一个小程序,用以实现跟踪鼠标的运行位置(坐标),程序很小,很实用,暂时抓不到截图。 Java实现移动的遮照效果 1个目标文件 摘要:Java源码,初学实例,遮照效果   Java实现移动的遮照效果,先实现椭圆形的遮罩,实例化媒体加载器,最后在创建缓冲区中的图像形成遮罩效果,而且它是会移动的。 Java鼠标拖拽功能 1个目标文件 Netbeans平台下实现的JAVA 拖放效果的一个实例,包括源代码和所需的图片资源。 (运行后会全屏显示窗体,没有关闭按钮,可使用快捷键Ctrl + F4关闭~) Java图片倒影效果实例源码 1个目标文件 摘要:Java源码,图形操作,图片倒影   Java图片倒影效果实例源码,有意思也很有参考价值的一个Java图像处理小程序,先读取图像然后进行图像重绘,产生倒影的效果,仅供参考。 java网络五子棋的源代码 7个目标文件 AVA网络五子棋,实现基本功能,无多余花哨内容,适合大学课程设计学习使用,内含完整可运行源代码及运行指导视频,Eclipse环境。 Java文件选择器源代码 2个目标文件 摘要:Java源码,文件操作,文件选择器   Java文件选择器源代码 Java文字跑马灯与信息窗口 1个目标文件 摘要:Java源码,文件操作,跑马灯   Java文字跑马灯效果与信息窗口,将一段文字按照滚动的方式显示出来,在网页设计中比较常见,本例使用Java实现文字跑马灯效果。 Java写的ATM机取款模拟程序 9个目标文件 内容索引:JAVA源码,综合应用,ATM,JAVA,模拟   基于Java技术,只实现了ATM机的取款过程,有兴趣和有能力的高手可以再加以改进,这样就更完美了。可作为Java初学者《面向对象》编程的一个很好范例。     在这个程序中,用到以下JAVA类库:      dataAccess(数据操作)类:包含用于数据库的相关操作   atmScreen(ATM屏幕)类:主要创建各操作窗体和相应按钮事件   Account(用户帐户)类:包含用户信息的验证、余额查询和扣钱等方法   readCard(读卡机)类:包含读取用户卡号和退卡(退出系统)   moneyMac(取钱机)类:只用于打印清单   Atm(系统)类:系统主方法的开始,负责调用其他类 Java写的巨型LCD液晶时钟显示屏 8个目标文件 内容索引:JAVA源码,综合应用,电子钟,LCD,液晶   一个巨型LCD数字电子时钟,用JAVA写的,运行截图如上示。它还带有右键菜单,LCD颜色可以变换等。 Java用GZIP压缩解压文件 1个目标文件 摘要:Java源码,文件操作,GZIP   Java使用GZIP压缩解压文件,如果你正在使用Java开发文件压缩或解压的功能,那么Gzip应该来说是比较值得参考的,那么本实例或许正是您需要的,Java基于Gzip的文件压缩与解压程序源代码,供源码下载者参考。 Java用Zip压缩多个文件实例源码 1个目标文件 摘要:Java源码,文件操作,压缩文件   Java用Zip压缩多个文件,一个Java文件操作小实例,用ZIP同进压缩多个文件,实际是打开文件读取,然后再利用ZipEntry实例化待压缩的条目列表,将ZIP条目列表写入输出流,从源文件得到文件输入流,写入缓冲数据等。 Java游戏使命的召唤源码 5个目标文件 内容索引:JAVA源码,游戏娱乐,Java游戏源码,使命的召唤   Java游戏使命的召唤源码,又名:Call of Duty,MFORMA超精典游戏,有兴趣的可以重新编译学习一下。 Java源码的仿QQ聊天程序 如题 Java中的Blowfish对称密钥加密算法类和实例 2个目标文件 内容索引:Java源码,算法相关,Blowfish,JAVA加密,对称密钥算法   JAVA中的Blowfish加密属于对称密钥算法,如果知道密钥,则可以对加密后的数据解密,但如果如果不知道密钥,基本上是不可能的,因此,这就决定了它的先天不足,就是带来的密钥的管理问题。 Java中的SSL及HTTPS协议实例源码 1个目标文件 摘要:Java源码,网络相关,HTTPS协议   Java中的SSL及HTTPS协议实例源码,使用SSL套接字的HTTPS服务器端,接受客户端的一个连接,并返回Hello,world.   本例中使用8080端口创建SSL服务器套接字,返回缺省的SocketFactory对象,生成PrintWriter对象,用于输出信息。 Java自定义光标程序源码 1个目标文件 摘要:Java源码,窗体界面,自定义光标   Java自定义光标程序源码,将光标定义成其它形式的图标,利用ToolKit对象得到图像,实例化自定义光标对象,增加组件等。 Jav动画图标源码(显示GIF图像) 1个目标文件 摘要:Java源码,图形操作,动画图标   Jav动画图标源码(显示GIF图像),学习如何连续加载GIF图像从而生成动画图标,先创建一个用于显示动画图标的数组,创建构造函数,初始化数组,重载组件绘制方法,实例化Timer对象显示动画,增加组件到窗口上。 JSP树型菜单 DTree html+js实现 Notebook源码Java记事本 2个目标文件 摘要:Java源码,文字字符,Java记事本   Java记事本:Notebook源码下载,虽然是个功能不多的记事本,但通过这个Java程序你可以学习到不少小技巧呢!比如创建界面、安装各种监听器,在窗体上布局主菜单,保存用户编辑的文件,以及如何使用"另存为"对话框保存文件,如何新建和打开一个文档等,源代码内包括了相当多的注释,是Java新手学习JAVA编程的好范例。   当用户按下窗口的“关闭”时,会自动调用此方法。 PDF分割与合并源代码 3个目标文件 能把一个PDF分割成多个单页的PDF,把多个PDF合并成一个PDF。 QR解码的Java实现程序 25个目标文件 内容索引:JAVA源码,媒体网络,QR解码,Java   用Java实现QR解码的源程序代码。 编译原理--LR(1)分析表构造(JAVA) 8个目标文件 如题 传奇私服登录器Java版附源代码 2个目标文件,如题。 单机版java五子棋V1.1 5个目标文件 完整的单机版java五子棋游戏,主要是判定胜负的算法,使用数组进行遍历,很容易理解。 很强的Java加密解密算法源码 3个目标文件 内容索引:Java源码,算法相关,JAVA算法,加密解密   很强的JAVA加密、解密算法源码,一共有6种算法可供选择,另外还可以直接生成ZIP压缩文件。 简单的注册与登录功能 6个目标文件 一个简单的用户登录界面 要连接数据库的 有登陆 注册功能 仅供初学者学习 简单模拟的J2ME潜艇大战源代码 4个目标文件 内容索引:JAVA源码,游戏娱乐,J2ME手机游戏,潜艇大战   简单模拟的J2ME手机游戏潜艇大战的源代码,界面有些粗糙,不过基本功能都实现了,发弹,躲闪等,可以帮助新手提高J2ME技术。 局域网广播系统java源码 如题 java游戏 可实现网上对战和人机对战 7个目标文件 雷电游戏JAVA版源程序 8个目标文件 这是一个简单的Java仿雷电游戏(源码见包内),共设置有三关三个小BOSS。采用有GreenJVM发布,因此可以运行在未装载JRE的Windows系统之上。 网络蚂蚁Java版 14个目标文件 用Java实现的网络蚂蚁,功能强大,方便上传下载,断点续传等操作 网页浏览器 如题 java学生成绩系统(图形界面) 14个目标文件 如题。 java学生课程管理系统 6个目标文件 如题。 一个较初级的EJB商业应用的例子 如题 一个支持servlet的web服务器 14个目标文件 如题。 用Java加密类实现DES、RSA及SHA的加密算法 9个目标文件 如题。 用java写的SFTP代码java实现的Tftp(Sftp)客户端与服务器程序。在这个程序中用到了一个“状态”的机制:客户端与服务器端在开始运行时有相同的状态,通过用户的输入或程序内部运行的机制,程序可以转到不同的状态,在不同的状态下程序实现了不同的功能。 用jdom解析xml 1个目标文件 要使用jdom解析xml文件,需要下载jdom的包,我使用的是jdom-1.1。解压之后,将lib文件夹下的.jar文件以及build文件夹下的jdom.jar拷贝到工程文件夹下,然后就可以使用jdom操作xml文件了。 copass源代码 Compass是一个强大的,事务的,高性能的对象/搜索引擎映射(OSEM:object/search engine mapping)与一个Java持久层框架. Compass实现了通过注册Hibernate的相关事件实现了数据的实时索引.. DataBuffer在Java中使用ADO.NET 本源码的作者对ADO.Net有着相当深厚的感情,有着对JAVA中DataBuffer类库运用的出色理解。它是开源项目javadesktop中的一个子项目,通过它你可以真正的理解DataSet、DataTable、DataRow……类库的使用。对这方面不太熟的开发者绝对有帮助。 HAHA CHAT Java仿QQ聊天程序源代码 iCHAT聊天室基于Java 内容索引:JAVA源码,媒体网络,iChat,Java聊天室   iChat LE 1.1版源码,一个比较大型的JAVA聊天室程序,可能要用到DLL文件,压缩包内已经有了,一年前的程序。 IP定位器 J2ME黑白棋游戏手机版 v2.1完整源码 J2ME冒险游戏CASPER源代码 J2me月光战机游戏源码 JasperReports 报表类库v3.5 Java24点游戏逼真图形版代码 Java 3D魔方游戏源码及中文注释包 内容索引:JAVA源码,游戏娱乐,JAVA3D,魔方游戏   很强的JAVA 3D魔方游戏,这是源码及中文注释包,作者的开发文档和思路都在里面,对学习很有帮助! allin.dev 重载了ListView,更好的实现! copass源代码 Java (Jsp)制作简单的表单程序 java Socket通信实现 Java 版的酒店系统,貌似完整 Java 电梯模拟程序 v2.0 Java 飞机订票 Java 高考信息管理系统 SQL数据库 java 一个消息发布网站 Java+MsSQL超市进销存(毕业设计及答辩资料) Java+sqlserver2000做的员工管理系统 Java+SQL信用卡管理系统源代码 Java+XML日程提醒系统 Java+XML写的RSS阅读器 JAVA版Swing星际争霸游戏源代码 Java半透明图片实现的步骤及源代码 JAVA帮助视图组件库 Help GUI 1.1源代码 Java毕业论文:搜索引擎系统附源代码 JAVA毕业设计_员工管理系统含文档 Java毕业设计一款J2me飞行射击游戏 Java仓库管理系统,Access数据库 Java超市管理,SQL2000+源代码 Java超市进销存系统 Java大型CERP进销存系统 Java电子相册源码 Java赌神游戏网络版源代码 JAVA短信网关平台(值得一看) Java多用户聊天室程序(毕业设计Java仿Vista界面风格的登录窗口 Java仿千千静听音乐播放器源代码 Java火影忍者游戏源代码 Java机车狂飙源代码 JAVA开发的打字软件源程序 Java开发的简单WEB服务器源码 Java聊天程序(JBuilder) Java聊天软件Visual Chat v1.91源码 Java模仿的MSN聊天软件 Java企业人事管理系统源码 JAVA轻量级的MVC框架应用实例 Java软件度量源码 Java声音播放程序源代码 JAVA实现CLDC与MIDP底层编程的代码 JAVA实现超级玛丽 Java实现的视频播放程序源码 Java手机短信项目源码 Java手机游戏大富翁源代码+注释 Java手机与计算机互发彩信源码 Java坦克大战网络对战版源代码 Java跳棋(基于SWT) Java通讯录手机版源码 Java图片翻折,将图像压扁 Java图书馆管理系统源程序 JAVA图书馆管理系统源码 Java图像文件的缩小与放大 Java推箱子游戏(50关+音效) JAVA网络抓包程序 Java文件切割器源代码 java项目源码在线相册系统 Java写的天气预报软件 Java写的图片幻灯片切换特效 Java写的一个mp3播放器 Java学生信息管理系统源码Java用的在线地图浏览模块 Java游戏沙丘城堡源代码 Java游戏中斜视角编辑器及引擎源代码 Java约瑟夫环演示Applet源码 Java中的EJB编程实例代码 Java转换xml JLoading Java版的Mp3下载工具 JSP 动态数据菜单 JSP 学生管理系统(全部代码+数据库) jsp+servlet+javabean+mysql党员信息管理系统 jsp高校科研项目管理系统 JSP开发的项目跟踪系统 jsp生产管理系统 msn聊天程序Java仿真代码 P2P--多用户在线聊天室Java源码) P2P源码 Azureus 2.5.0.2(JAVA) Skype 4.0 Java源码及开发文档 SnakeScript Java游戏脚本引擎 v1.1 Tsinghua IP 清华大学学生写的一个有关IP的Java程序 Visualvm 基于JAVA的CPU硬件资源管理器源程序 weblogic+j2ee构建音乐网站(原代码+数据库) web综合教学管理系统 YOYOPlayer 基于Java网络播放器源代码 宾馆管理系统 餐饮门户 超市购物系统 车间管控 打地鼠游戏 单位固定资产登记管理系统JAVA版 电子书店管理系统 分离SQL Server数据库 基于BS结构的Java可视化工作流定制软件 基于J2ME的Java游戏梦幻炸弹人源程序 基于JAVA的ICQ系统 基于Java的mp3播放器源代码 基于JAVA的日程提醒簿 基于Java的小型人事管理系统,带数据库 基于Java的邮件服务器源程序 基于MVC的Java资源管理器 v2.0 季风进销存管理系统(JSP版) 家庭多媒体播放器 开源Winzip压缩工具Java源码 客户管理系统 Alfresco Content Management 乐趣大型购物系统 类似QQ的聊天软件JAVA源码(附设计文档) 连接postsql数据库的java代码 泡泡堂战车游戏JAVA源码 配置ODBC数据源 企业进销存管理系统 轻松商城系统 手机游戏J2ME毕业设计 书籍管理系统 网络电视源代码TV-Browser 蜀山剑侠传游戏J2ME手机版源代码 网上书店 物业管理系统毕业设计+源码 销售预测系统PDP系统 选修课程管理系统V1.2.3 阳光酒店管理系统 一款Java网络格斗游戏源码 用iText类库制作PDF文档 用JAVA做的聊天软件,有安装程序和源代码 在Servlet中连接数据库的Java代码 中国移动业务管理系统源码(SSH框架)
基于Socket的网络聊天室Java课程设计的开发背景是为了让学生在深入学习Java网络编程的同时,了解和掌握Socket编程的基本原理和实践应用。 随着互联网的发展,人们的在线社交需求不断增加,对实时交流的要求也越来越高。而网络聊天室正是实现实时交流的一种常见方式。通过这个课程设计,学生能够学习如何利用Java编程语言来实现一个基于Socket的网络聊天室。 这个课程设计主要包括以下几个方面:首先,学生需要了解Socket编程的基本概念和工作原理,掌握Socket的建立和连接、数据的传输等操作。其次,学生需要学习如何使用Java提供的Socket相关类和方法来实现一个简单的网络聊天室,包括服务端和客户端的实现以及实时消息的发送和接收。然后,学生需要学习如何处理多客户端的并发连接以及消息的处理,确保聊天室的正常运行和用户体验。最后,学生还需要学习一些网络安全知识,如如何防止非法用户的入侵和攻击等。 通过这个课程设计,学生能够在实践中巩固和加深对Java网络编程的理解和掌握,提高自己的问题解决能力和编程实践能力。同时,这个课程设计还能够培养学生的团队协作意识和沟通能力,因为网络聊天室是一个涉及多个用户的项目,需要团队成员之间的配合和协作才能顺利完成。总之,基于Socket的网络聊天室Java课程设计能够为学生提供一个全面的实践平台,帮助他们掌握网络编程的基本原理和应用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值