java仿qq聊天系统 多人聊天室

14 篇文章 1 订阅
1 篇文章 0 订阅

目录

项目介绍

项目截图

服务器与客户端

新用户注册

注册新账号成功 

进入聊天室

多人在线

选择发送文件

文件接收提醒

 项目代码参考

服务器入口程序

 服务器请求处理

原理解析

服务器多人网络连接:

如何实现窗口抖动

获取源码


项目介绍

使用 java swing 开发多人聊天室,分为服务端和客户端,属于BS架构。

功能包括:注册、登录、单聊、群聊、窗口抖动、发送文件、选头像。

服务器:可以看到全部已注册用户的列表,用户登录之后也可以在服务器看到在线信息。

客户端:可以注册新用户,注册时可以填写昵称、密码、性别、头像。

登录成功之后会进入聊天室,在聊天室可以看到其他在线用户,也可以选择某个具体用户进行单聊。

也可以给其他用户发送窗口抖动。

也可以给其他用户发送文件。

项目截图

服务器与客户端

新用户注册

可以选择头像

注册新账号成功 

进入聊天室

多人在线

选择发送文件

文件接收提醒

 项目代码参考

服务器入口程序

这是服务器启动的主程序,在这里监听了网络端口,循环接收客户端的连接请求。


package server;

import java.io.IOException;
import java.net.*;

import javax.swing.*;

import server.controller.RequestProcessor;
import server.ui.ServerInfoFrame;

/** 服务器入口程序 */
public class ServerMain {
	public static void main(String[] args) {
		try {//设置外观样式
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); 
		} catch (Exception e) {
			e.printStackTrace();
		}
		int port = Integer.parseInt(DataBuffer.configProp.getProperty("port"));
		//初始化服务器套节字
		try {
			DataBuffer.serverSocket = new ServerSocket(port);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		new Thread(new Runnable() {//启动新线程进行客户端连接监听
			public void run() {
				try {
					while (true) {
						// 监听客户端的连接
						Socket socket = DataBuffer.serverSocket.accept();
						System.out.println("客户来了:" 
								+ socket.getInetAddress().getHostAddress()
								+ ":" + socket.getPort());
						
						//针对每个客户端启动一个线程,在线程中调用请求处理器来处理每个客户端的请求
						new Thread(new RequestProcessor(socket)).start();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}).start();
		
		
		//启动服务器监控窗体
		new ServerInfoFrame(); 
	}
}

 服务器请求处理

服务器对收到的消息进行处理,例如注册、登录、窗口震动、发送文件、接收文件、聊天、拒收文件,以及其他类型的消息都在这进行处理。


package server.controller;

import java.io.*;
import java.net.Socket;
import java.text.*;
import java.util.concurrent.CopyOnWriteArrayList;
import server.*;
import server.model.service.UserService;
import common.model.entity.*;

/** 服务器端请求处理器 */
public class RequestProcessor implements Runnable{
	private Socket currentClientSocket;  //当前正在请求服务器的客户端Socket
	
	public RequestProcessor(Socket currentClientSocket){
		this.currentClientSocket = currentClientSocket;
	}
	
	public void run() {
		boolean flag = true; //是否不间断监听
		try{
			OnlineClientIOCache currentClientIOCache = new OnlineClientIOCache(
					new ObjectInputStream(currentClientSocket.getInputStream()), 
					new ObjectOutputStream(currentClientSocket.getOutputStream()));
			while(flag){ //不停地读取客户端发过来的请求对象
				//从请求输入流中读取到客户端提交的请求对象
				Request request = (Request)currentClientIOCache.getOis().readObject();
				System.out.println("Server读取了客户端的请求:" + request.getAction());

				String actionName = request.getAction();   //获取请求中的动作
				if(actionName.equals("userRegiste")){      //用户注册
					registe(currentClientIOCache, request);
				}else if(actionName.equals("userLogin")){  //用户登录
					login(currentClientIOCache, request);
				}else if("exit".equals(actionName)){       //请求断开连接
					flag = logout(currentClientIOCache, request);
				}else if("chat".equals(actionName)){       //聊天
					chat(request);
				}else if("shake".equals(actionName)){      //振动
					shake(request);
				}else if("toSendFile".equals(actionName)){ //准备发送文件
					toSendFile(request);
				}else if("agreeReceiveFile".equals(actionName)){ //同意接收文件
					agreeReceiveFile(request);
				}else if("refuseReceiveFile".equals(actionName)){ //拒绝接收文件
					refuseReceiveFile(request);
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	/** 拒绝接收文件 */
	private void refuseReceiveFile(Request request) throws IOException {
		FileInfo sendFile = (FileInfo)request.getAttribute("sendFile");
		Response response = new Response();  //创建一个响应对象
		response.setType(ResponseType.REFUSERECEIVEFILE);
		response.setData("sendFile", sendFile);
		response.setStatus(ResponseStatus.OK);
		//向请求方的输出流输出响应
		OnlineClientIOCache ocic = DataBuffer.onlineUserIOCacheMap.get(sendFile.getFromUser().getId());
		this.sendResponse(ocic, response);
	}

	/** 同意接收文件 */
	private void agreeReceiveFile(Request request) throws IOException {
		FileInfo sendFile = (FileInfo)request.getAttribute("sendFile");
		//向请求方(发送方)的输出流输出响应
		Response response = new Response();  //创建一个响应对象
		response.setType(ResponseType.AGREERECEIVEFILE);
		response.setData("sendFile", sendFile);
		response.setStatus(ResponseStatus.OK);
		OnlineClientIOCache sendIO = DataBuffer.onlineUserIOCacheMap.get(sendFile.getFromUser().getId());
		this.sendResponse(sendIO, response);
		
		//向接收方发出接收文件的响应
		Response response2 = new Response();  //创建一个响应对象
		response2.setType(ResponseType.RECEIVEFILE);
		response2.setData("sendFile", sendFile);
		response2.setStatus(ResponseStatus.OK);
		OnlineClientIOCache receiveIO = DataBuffer.onlineUserIOCacheMap.get(sendFile.getToUser().getId());
		this.sendResponse(receiveIO, response2);
	}
	
	/** 客户端退出 */
	public boolean logout(OnlineClientIOCache oio, Request request) throws IOException{
		System.out.println(currentClientSocket.getInetAddress().getHostAddress()
				+ ":" + currentClientSocket.getPort() + "走了");

		User user = (User)request.getAttribute("user");
		//把当前上线客户端的IO从Map中删除
		DataBuffer.onlineUserIOCacheMap.remove(user.getId());
		//从在线用户缓存Map中删除当前用户
		DataBuffer.onlineUsersMap.remove(user.getId());
			
		Response response = new Response();  //创建一个响应对象
		response.setType(ResponseType.LOGOUT);
		response.setData("logoutUser", user);
		oio.getOos().writeObject(response);  //把响应对象往客户端写
		oio.getOos().flush();
		currentClientSocket.close();  //关闭这个客户端Socket
		
		DataBuffer.onlineUserTableModel.remove(user.getId()); //把当前下线用户从在线用户表Model中删除
		iteratorResponse(response);//通知所有其它在线客户端
		
		return false;  //断开监听
	}
	/** 注册 */
	public void registe(OnlineClientIOCache oio, Request request) throws IOException {
		User user = (User)request.getAttribute("user");
		UserService userService = new UserService();
		
		userService.addUser(user);
		
		Response response = new Response();  //创建一个响应对象
		response.setStatus(ResponseStatus.OK);
		response.setData("user", user);
		
		oio.getOos().writeObject(response);  //把响应对象往客户端写
		oio.getOos().flush();
		
		//把新注册用户添加到RegistedUserTableModel中
		DataBuffer.registedUserTableModel.add(new String[]{
			String.valueOf(user.getId()),
			user.getPassword(),
			user.getNickname(),
			String.valueOf(user.getSex())
		});
	}
	
	/** 登录 */
	public void login(OnlineClientIOCache currentClientIO, Request request) throws IOException {
		String idStr = (String)request.getAttribute("id");
		String password = (String) request.getAttribute("password");
		UserService userService = new UserService();
		User user = userService.login(Long.parseLong(idStr), password);
		
		Response response = new Response();  //创建一个响应对象
		if(null != user){
			if(DataBuffer.onlineUsersMap.containsKey(user.getId())){ //用户已经登录了
				response.setStatus(ResponseStatus.OK);
				response.setData("msg", "该 用户已经在别处上线了!");
				currentClientIO.getOos().writeObject(response);  //把响应对象往客户端写
				currentClientIO.getOos().flush();
			}else { //正确登录
				DataBuffer.onlineUsersMap.put(user.getId(), user); //添加到在线用户
				//设置在线用户
				response.setData("onlineUsers", 
						new CopyOnWriteArrayList<User>(DataBuffer.onlineUsersMap.values()));
				
				response.setStatus(ResponseStatus.OK);
				response.setData("user", user);
				currentClientIO.getOos().writeObject(response);  //把响应对象往客户端写
				currentClientIO.getOos().flush();
				
				//通知其它用户有人上线了
				Response response2 = new Response();
				response2.setType(ResponseType.LOGIN);
				response2.setData("loginUser", user);
				iteratorResponse(response2);
				
				//把当前上线的用户IO添加到缓存Map中
				DataBuffer.onlineUserIOCacheMap.put(user.getId(),currentClientIO);
				
				//把当前上线用户添加到OnlineUserTableModel中
				DataBuffer.onlineUserTableModel.add(
						new String[]{String.valueOf(user.getId()), 
										user.getNickname(), 
										String.valueOf(user.getSex())});
			}
		}else{ //登录失败
			response.setStatus(ResponseStatus.OK);
			response.setData("msg", "账号或密码不正确!");
			currentClientIO.getOos().writeObject(response);
			currentClientIO.getOos().flush();
		}
	}
	
	/** 聊天 */
	public void chat(Request request) throws IOException {
		Message msg = (Message)request.getAttribute("msg");
		Response response = new Response();
		response.setStatus(ResponseStatus.OK);
		response.setType(ResponseType.CHAT);
		response.setData("txtMsg", msg);
		
		if(msg.getToUser() != null){ //私聊:只给私聊的对象返回响应
			OnlineClientIOCache io = DataBuffer.onlineUserIOCacheMap.get(msg.getToUser().getId());
			sendResponse(io, response);
		}else{  //群聊:给除了发消息的所有客户端都返回响应
			for(Long id : DataBuffer.onlineUserIOCacheMap.keySet()){
				if(msg.getFromUser().getId() == id ){	continue; }
				sendResponse(DataBuffer.onlineUserIOCacheMap.get(id), response);
			}
		}
	}
	
	/** 发送振动 */
	public void shake(Request request)throws IOException {
		Message msg = (Message)request.getAttribute("msg");
		
		DateFormat df = new SimpleDateFormat("HH:mm:ss");
		StringBuffer sb = new StringBuffer();
		sb.append(" ").append(msg.getFromUser().getNickname())
			.append("(").append(msg.getFromUser().getId()).append(") ")
			.append(df.format(msg.getSendTime())).append("\n  给您发送了一个窗口抖动\n");
		msg.setMessage(sb.toString());
		
		Response response = new Response();
		response.setStatus(ResponseStatus.OK);
		response.setType(ResponseType.SHAKE);
		response.setData("ShakeMsg", msg);
		
		OnlineClientIOCache io = DataBuffer.onlineUserIOCacheMap.get(msg.getToUser().getId());
		sendResponse(io, response);
	}
	
	/** 准备发送文件 */
	public void toSendFile(Request request)throws IOException{
		Response response = new Response();
		response.setStatus(ResponseStatus.OK);
		response.setType(ResponseType.TOSENDFILE);
		FileInfo sendFile = (FileInfo)request.getAttribute("file");
		response.setData("sendFile", sendFile);
		//给文件接收方转发文件发送方的请求
		OnlineClientIOCache ioCache = DataBuffer.onlineUserIOCacheMap.get(sendFile.getToUser().getId());
		sendResponse(ioCache, response);
	}
	
	/** 给所有在线客户都发送响应 */
	private void iteratorResponse(Response response) throws IOException {
		for(OnlineClientIOCache onlineUserIO : DataBuffer.onlineUserIOCacheMap.values()){
			ObjectOutputStream oos = onlineUserIO.getOos();
			oos.writeObject(response);
			oos.flush();
		}
	}
	
	/** 向指定客户端IO的输出流中输出指定响应 */
	private void sendResponse(OnlineClientIOCache onlineUserIO, Response response)throws IOException {
		ObjectOutputStream oos = onlineUserIO.getOos();
		oos.writeObject(response);
		oos.flush();
	}
}

原理解析

服务器多人网络连接:

首先来分析一下要实现的流程

  •  首先建立一个服务器端,构建serversocket并绑定端口
  •  创建socket客户端,连接到指定ip以及其端口
  •  然后使用accept阻塞接收socket发出的连接请求
  • 接收到的socket对象需要使用thread单独处理,
  • 每个用户分配单独的线程进行读写处理
  •  根据输入流和输出流进行两者数据的通信
  •  获取连接后的socket客户端的输入流和输出流

值得一提是:该socket是同步阻塞的,因此在socket客户端需要进行创建一个线程,来分别进行向服务器输出,和接收服务器传输的数据。

如何实现窗口抖动

窗口的左上角是原点,我们首先记录起始坐标,计算出向左上角移动的距离和右下角移动的距离,通过循环语句控制窗口位移,最终将窗口恢复到起始坐标。

  • 4
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程大玩家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值