`
icarusliu
  • 浏览: 237332 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java实现的聊天工具(部分功能完成)

    博客分类:
  • java
 
阅读更多

准备换工作了,下一份工作也已经搞定。从学校毕业,浑浑噩噩的做了一年测试,终于是要转向自己喜欢的开发了。浪费了一年时间终于再次回到轨道上,希望没有落后太多。

打发业余时间,想要一个聊天工具,于是便开始做了。这是初步的成果,采用客户端和服务器的模式。服务器端比较简单,主要保存有一个在线用户列表,每个客户端登录,则会向服务器登记,同时服务器会返回当前所有的在线用户,由客户端显示在界面当中。

主要界面如下:


 

文件传输:


 

当前实现的功能主要是文本聊天和文件传输功能,接着主要想实现类型QQ的图片发送及语音、视频聊天功能。图片发送功能其实已经完成,但在公司电脑上无法拷贝回家,因此此处上传的代码中没有图片传输功能。

没有在界面上花太多功夫,因此界面很粗糙,准备是相关的功能完成之后再对界面进行优化。

 

下图是Client在Eclipse下面的结构:


 

主要包括有三个包:

client: 存储客户端相关的类

common: 存储客户端和服务器端共用的类

common_ui: 存储我自己所做的公用类,其中主要是界面相关的类。主要是自己为了方便在不同的工程下复用相关代码而建的类。

 

引用的Jar包主要有三个,实际上在该聊天软件中仅使用了miglayout这个包来进行相关布局,另外两个包是在common_ui中有使用Jfreechart而引用的,在该工程中未使用。

 

附件中有Server及Client的源代码,如果Server启动失败请修改com/liuqi/chart/common/uitl/Constants中的服务器IP地址及端口。相关的Jar包请自行下载。

 

以下对几个主要使用的类进行说明:

一 服务器端:


1. UserCache: 在线用户缓存器,存储服务器上的在线用户列表,采用单例模式,同时保证线程安全。

 2. Server:在Constants中所规定的IP上启动监听,接收服务器的消息并进行处理。当前主要处理两种消息,登录和离线。登录时通知所有在线用户该用户登录,离线时通知所有用户该用户离线。


 

二 客户端:

客户端也在相应的IP和端口上进行监听,收取来自其它用户和服务器的消息。

1. 消息分发:

ClientServer类:使用SwingWorker来不断的在后台监听相关消息进行处理:

 

package com.liuqi.chart.client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;

import javax.swing.SwingWorker;

import com.liuqi.chart.client.ui.FileTransfferDialog;
import com.liuqi.chart.client.ui.MainFrame;
import com.liuqi.chart.client.util.MessageCache;
import com.liuqi.chart.common.bean.Message;
import com.liuqi.chart.common.bean.User;
import com.liuqi.chart.common.tran.TransfferImpl;
import com.liuqi.chart.common.tran.bean.TranObject;
import com.liuqi.chart.common.tran.bean.TranObjectType;
import com.liuqi.chart.common.util.Constants;

/**
 * 用户机器上的消息接收服务器,应当使用单独的线程来进行处理
 * 
 * @author 67
 * 
 */
public class ClientServer extends SwingWorker<Object, TranObject> {
	private ServerSocket serverSocket;
	private MainFrame frame;
	
	private TransfferImpl transffer = new TransfferImpl();

	public ClientServer(MainFrame frame, String ip) throws IOException {
		this.frame = frame;
		InetAddress address = InetAddress.getByName(ip);

		serverSocket = new ServerSocket(Constants.CLIENT_SERVER_PORT, 0,
				address);
	}

	/**
	 * 对传输的对象进行处理 一般用户传输过来的消息类型有: 文本消息 传输文件请求
	 * 
	 * @param object
	 */
	public void process(List<TranObject> list) {
		for (TranObject object : list) {
			switch (object.getType()) {
			case MESSAGE: {
				// 发送的是来自其它用户的消息,则将其添加到消息缓存中去等待处理
				Object o = object.getObject();
				if (o != null && o instanceof Message) {
					Message message = (Message) o;
					MessageCache.getInstance().add(message);
				}
				break;
			}
			case LOGIN: {
				// 表明接收到的是来自服务器的消息,有某个用户登录,需要在用户列表中表现出来
				Object o = object.getObject();
				if (o != null && o instanceof User) {
					User user = (User)o;
					frame.getCenterPanel().addUser(user);
				}
				
				break;
			}
			case LOGOUT: {
				//表明是接收到的来自服务器某个用户退出登录的消息 
				Object o = object.getObject();
				if(o!=null&&o instanceof User){
					User user = (User)o;
					frame.getCenterPanel().deleteUser(user);
				}
				
				break;
			}
			}
		}
	}

	@Override
	protected Object doInBackground() throws Exception {
		while (!isCancelled()) {
			try {
				Socket socket = serverSocket.accept();// 从Socket中取得所传输的对象
				TranObject object;
				try {
					object = transffer.get(socket);
					if (object != null) {
						publish(object);
						
						//如果是文件传输请求,首先弹出是否接收的对话框,如果是,则在相应端口启动文件接收线程,然后再回应准备OK的消息,否则返回否
						if(object.getType().equals(TranObjectType.FILE)){
							FileTransfferDialog dialog = new FileTransfferDialog(object,socket);
							dialog.setVisible(true);
						}
					}
				} catch (ClassNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		return null;
	}
}
 

 

 

 

2. 文本消息处理:

  MessageCache:接收到的消息队列,来自不同用户的消息被保存在这个队列中等待处理。

  MessageProcessor:系统启动后不断的从MessageCahce中取得消息来进行处理,如果队列中无消息则等待,队列中有消息进行则被唤醒进行处理。

package com.liuqi.chart.client.util;

import java.util.List;

import javax.swing.SwingWorker;

import com.liuqi.chart.client.ui.ChartDialog;
import com.liuqi.chart.common.bean.Message;
import com.liuqi.chart.common.bean.TextMessage;

import com.liuqi.common.log.LLogger;

/**
 * 消息处理器
 * @author 67
 *
 */
public class MessageProcessor extends SwingWorker<Object,Message>{
	private boolean isStopped = false;
	
	public MessageProcessor(){
		LLogger.info("消息处理器启动完毕!");
	}
	
	/**
	 * 不断从消息缓存中取得消息然后进行处理
	 */
	@Override
	public Object doInBackground(){
		while(!isStopped()){
			//没有被停止的时候不断的处理消息 
			Message message = MessageCache.getInstance().get();
			if(message!=null){
				publish(message);
			}
		}
		
		return null;
	}

	/**
	 * 处理消息 
	 * @param message
	 */
	@Override
	public void process(List<Message> list){
		for(Message message: list){
			//将消息显示在对应的聊天窗口中
			ChartDialog dialog = ChartDialogCache.getInstance().get(message.getFromUser());
			if(dialog!=null){
				dialog.appendMessage(dialog.getUser(),((TextMessage)message).getMessage());
				dialog.setVisible(true);
			}
		}
	}
	
	public boolean isStopped() {
		return isStopped;
	}

	public void setStopped(boolean isStopped) {
		this.isStopped = isStopped;
	}
}
 

 

3. 文件传输:

TranFileCache:文件传输队列,当有新的文件发送请求时,需要被发送的文件及进度面板被存入该队列。

SendFileWorker:不断的从文件传输队列中取得文件来进行传输,没有文件传输请求则等待;有新请求则被唤醒:

 

package com.liuqi.chart.client.tran;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Calendar;
import java.util.List;

import javax.swing.SwingWorker;

import com.liuqi.chart.client.ui.ChartDialog;
import com.liuqi.chart.client.ui.tran.TranOneFilePanel;
import com.liuqi.chart.client.util.Cache;
import com.liuqi.chart.common.bean.User;
import com.liuqi.chart.common.tran.bean.TranObject;
import com.liuqi.chart.common.tran.bean.TranObjectType;

/**
 * 文件传输工具
 * 先与目的取得连接,取得一个socket,对方有回应后再发送数据
 * 
 * @author 67
 *
 */
public class SendFileWorker extends SwingWorker<Object,String>{
	private ChartDialog dialog;
	private TranFileCache cache = TranFileCache.getInstance();
	
	private static final String START = "start";
	private static final String REJECT = "reject";
	private static final String END = "end";
	
	public SendFileWorker(ChartDialog dialog){
		this.dialog = dialog;
	}
	
	@Override
	protected Object doInBackground() throws Exception {
		while(!isCancelled()){
			TranOneFilePanel panel = cache.get();
			File file = panel.getFile();
			
			InetSocketAddress address = new InetSocketAddress(dialog.getUser().getIp(),dialog.getUser().getPort());
			Socket socket = new Socket();
			try{
				socket.connect(address);
				OutputStream output = socket.getOutputStream();
				InputStream input = socket.getInputStream();
				
				//第一步,先向对方发送文件传输请求
				ObjectOutputStream oo = new ObjectOutputStream(output);
				TranObject<File> to = new TranObject<File>(TranObjectType.FILE);
				to.setFromUser(Cache.getInstance().getNowUser());
				to.setToUser(dialog.getUser());
				to.setObject(file);
				oo.writeObject(to);
				oo.flush();
				
				ObjectInputStream oi = new ObjectInputStream(input);
				TranObject<User> object = (TranObject<User>)oi.readObject();
				
				if(object.getObject()==null){
					publish(REJECT);
				}else{
					publish(START);
					
					InetSocketAddress address2 = new InetSocketAddress(object.getObject().getIp(),object.getObject().getPort());
					Socket ss = new Socket();
					
					ss.connect(address2);
					OutputStream so = ss.getOutputStream();
					InputStream fi = new FileInputStream(file);
					
					long fileLength = file.length();
					long nowLength = 0;
					long oneofhundred = fileLength/100;
					
					byte[] b = new byte[1024];
					int oldrake = 0;
					int newrake = 0;
					
					int race = 0;//速度
					long starttime = Calendar.getInstance().getTimeInMillis()/1000;//开始时间,按秒计算 
					long time = 0;
					while(fi.read(b)!=-1){
						so.write(b);
						nowLength += b.length;
						
						newrake = (int)(nowLength / oneofhundred);
						
						if(newrake!=oldrake){
							time = Calendar.getInstance().getTimeInMillis()/1000 - starttime;
							if(time==0){
								time = 1;
							}
							race = (int)((nowLength/1024) / time);
							publish(newrake + "");
							oldrake = newrake;
						}
					}
					
					publish(END);
					
					so.flush();
					so.close();
					fi.close();
				}
			}catch(Exception ex){
				//连接失败
				
			}
		}
		
		return null;
	}

	protected void process(List<String> list){
		for(String str: list){
//			dialog.appendMessage(str);
			if(str.equals(START)){
				//开始传输
				dialog.appendMessage("对方已经接收!");
			}else if(str.equals(REJECT)){
				dialog.appendMessage("对方拒绝接收!");
				dialog.getRightPanel().getTranFilePanel().del(cache.getNowPanel());
			}else if(str.equals(END)){
				dialog.appendMessage("传输完成!");
				dialog.getRightPanel().getTranFilePanel().del(cache.getNowPanel());
			}else{
				int rake = Integer.valueOf(str);
				cache.getNowPanel().getPb().setValue(rake);
			}
		}
	}
}

 

 

 

FileTransfferDialog: 接收消息处理对话框,选择文件的保存方式。在ClientServer中有文件传输请求时被调用。


 

选择完处理方式后,给发送者发送接收或者拒绝回应,同时启动文件接收器来进行接收:

 

TransfferImpl transffer = new TransfferImpl();
		
		TranObject<User> retO = new TranObject<User>(TranObjectType.FILE);
		User user = new User();
		user.setIp(Cache.getInstance().getNowUser().getIp());
		user.setPort(Constants.CLIENT_FILE_TRANSPORT_PORT);
		retO.setObject(user);
		
		if(e.getSource().equals(cancelButton)){//拒绝接收,返回的对象中Object为null
			retO.setObject(null);
		}
		
		String path = "D:\\" + object.getObject().getName();
		
		if(e.getSource().equals(saveAsButton)){
			//保存到指定目录 
			chooser.showSaveDialog(null);
			File file = chooser.getSelectedFile();
			if(file.isDirectory()){
				path = file.getPath() + object.getObject().getName();
			}else{
				path = file.getPath();
			}
		}
		
		try {
			transffer.tran(socket, retO);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		if(!e.getSource().equals(cancelButton)){
			//在不是取消的时候执行
			worker.setPath(path);
			worker.execute();
			
		}

 

 ReceiveFileWorker: 文件接收器

 

package com.liuqi.chart.client.tran;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;

import javax.swing.SwingWorker;

import com.liuqi.chart.client.util.Cache;
import com.liuqi.chart.common.bean.User;
import com.liuqi.chart.common.tran.TransfferImpl;
import com.liuqi.chart.common.tran.bean.TranObject;
import com.liuqi.chart.common.tran.bean.TranObjectType;
import com.liuqi.chart.common.util.Constants;

public class ReceiveFileWorker extends SwingWorker<Object,String>{
	private TranObject<File> object;
	private String path;//保存目录,默认保存到D盘 
	
	public ReceiveFileWorker(TranObject<File> object){
		this.object = object;
		
		path = "D:\\" + object.getObject().getName();
	}
	
	public void setPath(String path){
		this.path = path;
	}
	
	@Override
	protected Object doInBackground() throws Exception {
		//启动文件接收线程,并返回给用户,其中FromUser的端口使用文件接收端口
		try{
			File file = (File)object.getObject();
			
			ServerSocket serverSocket = new ServerSocket();
			serverSocket.bind(new InetSocketAddress(Cache.getInstance().getNowUser().getIp()
					,Constants.CLIENT_FILE_TRANSPORT_PORT));
			
			//启动接收
			Socket retSocket = serverSocket.accept();
			InputStream input = retSocket.getInputStream();
			
			OutputStream output = new FileOutputStream(new File(path));//保存到默认目录 
			publish("文件传输开始,文件大小:" + file.length());
			
			byte[] b = new byte[1024];
			double nowSize = 0;
			while(input.read(b)!=-1){
				output.write(b);
				nowSize ++;
//				publish("已传输:" + nowSize + "KB");
			}
			
			publish("文件传输结束");
			
			output.flush();
			output.close();
			
			//关闭文件接收
			retSocket.close();
			serverSocket.close();
		}catch(Exception ex){
			ex.printStackTrace();
		}
		
		
		return null;
	}
	
	protected void process(List<String> list){
		for(String str: list){
			System.out.println(str);
		}
	}
}

 

 

 

 

 

 

 

 

  • 大小: 22 KB
  • 大小: 348.2 KB
  • 大小: 356.5 KB
  • 大小: 7.3 KB
  • 大小: 8.8 KB
  • 大小: 11.8 KB
  • 大小: 24.3 KB
分享到:
评论

相关推荐

    基于tcp-java的聊天工具

    本篇文章将深入探讨如何使用Java实现基于TCP的聊天工具,包括其基本原理、设计模式以及关键代码实现。 1. **TCP协议基础** TCP是互联网协议栈中的重要组成部分,提供全双工通信,确保数据的有序和无损传输。它通过...

    java聊天工具、Java项目、聊天工具

    "java聊天工具"是一个典型的Java应用场景,指的是使用Java技术构建的在线聊天应用程序,它允许用户通过互联网进行实时通信。在这个Java项目中,我们可以探讨几个关键的知识点: 1. **Java网络编程**:Java聊天工具...

    中软java聊天工具

    《中软Java聊天工具:Swing技术在C/S架构下的应用》 在信息技术日新月异的今天,各种通信工具层出不穷,其中Java语言凭借其跨平台的优势,常被用于开发各种客户端应用程序,如我们今天要讨论的“中软Java聊天工具”...

    java编写的简单聊天工具

    【Java编写的简单聊天工具】是一个基于Java编程语言开发的小型应用程序,用于实现用户之间的文本通信。这个项目可能是在学习了张龙老师的课程后实践所学知识的一个成果。由于描述中提到该工具并未集成数据库功能,...

    java做的简单局域网聊天工具

    聊天工具通常包括消息发送、接收、用户身份验证、群组聊天等功能,这些都需要通过Java的网络库和API来实现。 【局域网聊天客户端.jar】是供用户交互的部分,它连接到服务器并允许用户发送和接收消息。客户端可能...

    java局域网聊天工具

    综上所述,Java局域网聊天工具的实现涉及到Java基础、网络编程、GUI设计、多线程、数据序列化等多个方面,通过这些技术,开发者可以构建出功能完备、易于使用的实时通信应用。客户端工程文件的分析和理解有助于...

    用java实现的聊天室

    1. **网络编程**:Java中的`Socket编程`是实现聊天室的基础。每个用户通过创建一个Socket连接到服务器,发送和接收数据。`ServerSocket`类用于在服务器端监听客户端的连接请求,而`Socket`类则代表客户端和服务器...

    JAVA聊天工具 开题报告

    设计完成后,将得到一个稳定、可靠的聊天工具,具备文字聊天、文件传输等核心功能,满足大学生的日常网络交流需求。此外,通过本次设计,可加深对JAVA编程、网络通信、数据库管理和多线程技术的理解,提升实际项目...

    基于JAVA的聊天工具开发

    在此项目中,开发者 Solomon1558 旨在构建一个即时通讯(IM)工具,通过JAVA实现客户端(Client)和服务器端(Server)的双向通信。 在网络编程部分,项目采用了C/S(客户端/服务器)架构。基于TCP的Socket通信是...

    QQ聊天工具的设计与实现论文(java)

    QQ聊天工具的设计与实现是基于Java技术的一种网络通讯软件,其目标是创建一个跨平台、简单易用且具备基本聊天功能的应用。论文的核心内容涵盖了以下几个关键知识点: 1. **Java技术**:Java作为一种多平台支持的...

    简单的java聊天工具

    Java聊天工具是一款基于Java Socket编程实现的简单网络通信应用,主要由客户端(Client)和服务器端(Server)两部分组成。这两个部分分别由两个独立的Java文件实现,它们通过TCP/IP协议进行数据交换,实现用户之间...

    java聊天工具gxd.pdf

    它提供了丰富的类库,如Swing和JavaFX用于构建图形用户界面,Socket类和ServerSocket类用于网络通信,以及线程机制支持并发处理,这些都是实现聊天工具的关键技术。 1.3 SOCKET编程 Socket是网络通信的基础,它为...

    Java 套接字(Socket)实现的简单局域网点对点聊天工具

    在本项目中,"Java 套接字(Socket)实现的简单局域网点对点聊天工具"是一个基于Java的本地网络通信应用,允许用户通过输入IP地址直接进行聊天。这个工具监听4700端口,这是应用程序之间建立连接并交换数据的特定通道...

    java实现聊天室程序

    【Java实现聊天室程序】是Java编程领域中的一个基础实例,旨在帮助开发者提升对Java语言的理解和实际应用能力。在本实例中,我们将探讨如何利用Java来构建一个基本的聊天室系统,它允许用户进行实时的文本交流。下面...

    java模拟qq聊天

    在实现聊天功能时,我们需要确保数据的可靠传输,这通常通过TCP协议来实现,因为它提供面向连接的服务,能确保数据的顺序和完整性。 "聊天"部分,我们需要设计一个消息格式,可能是包含发送者、接收者和消息内容的...

    用JAVA编写的聊天小程序

    “JAVA”标签明确了该项目是使用Java语言开发的,而“聊天工具”则告诉我们这是一个具有交互性的应用程序,可以允许用户之间进行实时通信。在Java中,实现这样的工具可能涉及到网络编程、多线程和GUI设计等核心概念...

    eclipse实现的聊天室

    当客户端通过Socket连接到服务器后,它们可以通过输入输出流交换数据,实现聊天功能。 其次,多线程是聊天室的关键组成部分。为了让服务器同时处理多个客户端的连接,每个客户端连接都会被分配到一个新的线程中。...

    java+swing聊天室

    在这个项目中,我们将探讨如何利用Java Swing来实现一个简单的聊天室功能。 首先,我们要理解Java Swing的基本概念。Swing是一个轻量级的GUI工具包,它提供了许多组件,如按钮(JButton)、文本框(JTextField)、...

    Java实现webQQ聊天完整代码

    在本项目中,"Java实现webQQ聊天完整代码" 是一个基于Java技术栈构建的Web应用程序,用于实现在线QQ聊天功能。这个项目非常适合初学者学习和作为毕业设计使用,因为它涵盖了多个关键的Java Web开发技术。 首先,让...

Global site tag (gtag.js) - Google Analytics