通信项目阶段总结-IM聊客户端

这个学习项目的客户端业务流程基本是我写的,由于界面是用java的swing写的,非常难配置,所以很多都是直接使用的QQ的界面,不过既然能够分成模块,那么前台后台自然是无关的。下面给出我的业务流程代码供大家学习交流:

这里用的是xmpp协议,因此客户端和服务器两段都分别都有一个相同的解析该协议的方法,这是应该事先沟通好的


这一块是用单例模式封装的客户端,保证同一时间只能有一个socket流,同样的,ObjectOutputStream和ObjectInputStream流也要保证只有一个。这样不管在哪想获取到其中任何一个流,都保证不会拿错:


package util;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;

public class MyClient {
	/**
	 * 单例模式保证只能拿到一个实例对象
	 * 
	 * @author 86119
	 *
	 */

	private MyClient() {
	}

	private static MyClient mc;
	private Socket client;
	private ObjectOutputStream oos;
	private ObjectInputStream ois;

	public ObjectOutputStream getOos() {
		return oos;
	}

	public ObjectInputStream getOis() {
		return ois;
	}

	// 包装保证同一时间只能有一个实例对象存在
	public static synchronized MyClient getInstance() {
		if (mc == null) {
			mc = new MyClient();
		}
		return mc;
	}

	// 实例对象的方法
	public void getConnection() {
		try {
			if (client == null) {
				client = new Socket("localhost", 9898);
				System.out.println("客户端启动成功");
				OutputStream ous = client.getOutputStream();
				InputStream ins = client.getInputStream();
				// 包装数据流
				oos = new ObjectOutputStream(ous);
				ois = new ObjectInputStream(ins);
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 关闭流
	public void closeConnection() {
		try {
			oos.close();
			ois.close();
			client.close();
			client = null;
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

接下来是两个工具类,这里分别是自定义的工具类和流程工具类。
package util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Tool {
	// 发送文件的方法
	public static void sendFile(File file, ObjectOutputStream oos) throws IOException {

		FileInputStream fis = new FileInputStream(file);
		// 统计写入次数作为协议传输
		long length = file.length() / 2048;
		int last = (int) file.length() % 2048;
		// 写入协议
		oos.writeLong(length);
		oos.flush();
		oos.writeInt(last);
		oos.flush();
		byte[] bytes = new byte[2048];
		while (length > 0) {
			fis.read(bytes);
			oos.write(bytes);
			oos.flush();
			length--;
		}
		if (last > 0) {
			bytes = new byte[last];
			fis.read(bytes);
			oos.write(bytes);
			oos.flush();
		}
		fis.close();
	}

	// 接收文件方法
	public static void getFile(ObjectInputStream ois, FileOutputStream fos) throws IOException {

		// 传输过程
		long length = ois.readLong();
		int last = ois.readInt();
		byte[] bytes = new byte[2048];
		while (length > 0) {
			ois.readFully(bytes);
			fos.write(bytes);
			fos.flush();
			length--;
		}
		if (last > 0) {
			bytes = new byte[last];
			ois.readFully(bytes);
			fos.write(bytes);
			fos.flush();
		}
		fos.close();
	}
}


package util;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import javax.swing.JFileChooser;

import projo.UserPojo;

public class FlowTool {
	/**
	 * 注册流程方法
	 * 
	 * @param username
	 *            用户名
	 * @param password
	 *            密码
	 * @param telphone
	 *            手机号码
	 * @param mail
	 *            邮箱号
	 * @throws IOException
	 *             流传输过程中可能抛出的异常
	 */
	public static String getType(String str, String s) {

		int s1 = str.indexOf("<" + s + ">") + s.length() + 2;
		int s2 = str.indexOf("</" + s + ">");
		String snew = str.substring(s1, s2);

		return snew;
	}

	public static boolean registFlow(String username, String password, String nickname, String sex, String age,
			String realname, String telphone, String mail, String heah) throws IOException {
		MyClient.getInstance().getConnection();
		ObjectOutputStream oos = MyClient.getInstance().getOos();
		ObjectInputStream ois = MyClient.getInstance().getOis();
		String str = "<msg><type>regist</type><name>" + username + "</name><pwd>" + password + "</pwd><nickname>"
				+ nickname + "</nickname><sex>" + sex + "</sex><age>" + age + "</age><realname>" + realname
				+ "</realname><telnum>" + telphone + "</telnum><email>" + mail + "</email><heah>" + heah
				+ "</heah></msg>";
		// 当合法的情况下发送xmpp协议字符串
		oos.writeUTF(str);
		oos.flush();
		System.out.println("信息已发送");
		// 获取注册结果
		str = ois.readUTF();
		if ("yes".equals(str)) {
			return true;
		}
		return false;
	}

	/**
	 * 登录流程方法
	 * 
	 * @param logoncommand
	 *            获取到的用户名/邮箱/手机号
	 * @param password
	 *            获取到的密码
	 * @return 登录的结果,是否成功
	 * @throws IOException
	 */

	public static String logonFlow(String username, String password) throws IOException {
		// 获取连接和对象流
		MyClient.getInstance().getConnection();
		String str = "<msg><type>login</type><username>" + username + "</username><pwd>" + password + "</pwd></msg>";
		MyClient.getInstance().getOos().writeUTF(str);
		MyClient.getInstance().getOos().flush();
		// 获取返回结果
		str = MyClient.getInstance().getOis().readUTF();
		return str;

	}

	/**
	 * 发送消息
	 * 
	 * @param str
	 *            要发送的消息内容
	 * @return 是否发送成功
	 * @throws IOException
	 *             流传输过程中可能发生的异常
	 */
	public static void sendWords(String str, String username, String sender, String time) throws IOException {

		String snew = "<msg><type>words</type><name>" + username + "</name><word>" + str + "</word><time>" + time
				+ "</time><sender>" + sender + "</sender></msg>";
		MyClient.getInstance().getOos().writeUTF(snew);
		MyClient.getInstance().getOos().flush();

	}

	/**
	 * 请求服务器发送初始消息
	 * 
	 * @param file
	 * @param oos
	 * @throws IOException
	 */
	public static void request(String name, String sender) {
		System.out.println("12");
		String snew = "<msg><type>request</type><name>" + name + "</name><sender>" + sender + "</sender></msg>";
		try {
			MyClient.getInstance().getOos().writeUTF(snew);
			MyClient.getInstance().getOos().flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public static void sendEvent(MouseEvent e, String name) {
		try {
			String m = "<msg><type>event</type><name>" + name + "</name></msg>";
			MyClient.getInstance().getOos().writeUTF(m);
			MyClient.getInstance().getOos().flush();
			MyClient.getInstance().getOos().writeObject(e);
			MyClient.getInstance().getOos().flush();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	public static void sendkeyEvent(KeyEvent e, String name) {
		try {
			String m = "<msg><type>event</type><name>" + name + "</name></msg>";
			MyClient.getInstance().getOos().writeUTF(m);
			MyClient.getInstance().getOos().flush();
			MyClient.getInstance().getOos().writeObject(e);
			MyClient.getInstance().getOos().flush();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	public static void send(String s) {
		try {

			MyClient.getInstance().getOos().writeUTF(s);
			MyClient.getInstance().getOos().flush();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	// 修改个人信息
	public static void sendChange(UserPojo user) {
		String s = "<msg><type>change</type></msg>";
		try {
			MyClient.getInstance().getOos().writeUTF(s);
			MyClient.getInstance().getOos().flush();
			MyClient.getInstance().getOos().writeObject(user);
			MyClient.getInstance().getOos().flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 发送文件
	 * 
	 * @throws IOException
	 *             可能出现的io异常
	 */
	public static void sendFileFlow(String username, String sender, File file) throws IOException {
		ObjectOutputStream oos = MyClient.getInstance().getOos();
		// 先发送协议内容
		String type = "file";
		String str = "<msg><type>" + type + "</type><name>" + username + "</name><sender>" + sender + "</sender></msg>";
		oos.writeUTF(str);
		oos.flush();
		// 把文件数组发过去
		oos.writeObject(file);
		oos.flush();

	}

	/**
	 * 接收文件
	 * 
	 * @throws IOException
	 *             可能出现的异常
	 * @throws ClassNotFoundException
	 */
	public static void getFileFlow(String filename) throws IOException, ClassNotFoundException {

		JFileChooser jfc = new JFileChooser();
		// 只选择文件夹
		jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		jfc.showSaveDialog(null);
		File newfile = jfc.getSelectedFile();
		// 创建文件输出流
		FileOutputStream fos = new FileOutputStream(newfile.getAbsolutePath() + "//" + filename);
		Tool.getFile(MyClient.getInstance().getOis(), fos);

	}

	// 发送远程请求
	public static void sendyuncheng(String ss, String sender, String name) {
		String s = "<msg><type>jk</type><con>" + ss + "</con><sender>" + sender + "</sender><name>" + name
				+ "</name></msg>";
		try {
			MyClient.getInstance().getOos().writeUTF(s);
			MyClient.getInstance().getOos().flush();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}


接下来还有一个客户端线程,因为是涉及到 客户端-服务器-客户端 这样的流程,那么随时可能收到各种不同的消息,因此给一个线程类用来不停的接受消息并解析,解析完毕后按照不同的结果进行不同的处理,有的会再次返回确认消息,总之,就是实际通信的两个客户端的代码都是按照这个类的流程来实现,因此这个类也是很关键的。

package util;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.JOptionPane;

import QQ_UI.ConFrame;
import QQ_UI.MFrame;
import QQ_UI.MainFrame;
import projo.MsPojo;
import projo.UserPojo;

/**
 * 线程用来接收消息
 * 
 * @author 86119
 *
 */
public class ClientThread extends Thread {
	// 存储所有组件的链表
	public MainFrame mf;
	public ConFrame con;
	public Robot robot;
	public boolean f = true;
	public MFrame mb;

	{
		try {
			robot = new Robot();
		} catch (AWTException e) {

			e.printStackTrace();
		}
	}

	public ClientThread(MainFrame mf) {
		this.mf = mf;
	}

	public void run() {
		// 拿到对象输入输出流
		con = new ConFrame(mf);
		ObjectOutputStream oos = MyClient.getInstance().getOos();
		ObjectInputStream ois = MyClient.getInstance().getOis();
		try {
			while (true) {
				// 拿到协议
				String str = ois.readUTF();
				// 获取类型
				String type = getType(str, "type");
				// 更新在线列表
				if ("updalist".equals(type)) {
					ArrayList<UserPojo> list = (ArrayList<UserPojo>) ois.readObject();
					ArrayList<UserPojo> newlist = new ArrayList<UserPojo>();
					for (int i = 0; i < list.size(); i++) {
						if ("1".equals(list.get(i).getState()) && !(list.get(i).getNickname().equals(mf.username))) {
							newlist.add(list.get(i));
						}
					}
					con.updataFrame(newlist);
				}
				// 收消息
				else if ("words".equals(type)) {
					String sender = getType(str, "sender");
					String word = getType(str, "word");
					String time = getType(str, "time");
					String name = getType(str, "name");
					con.appeadChat(word, sender, time, name);
				}
				// 开始接收文件
				else if ("filestart".equals(type)) {
					String filename = getType(str, "filename");
					FlowTool.getFileFlow(filename);
					// 接收对方反馈是否接收文件
				} else if ("file return".equals(type)) {
					String sender = getType(str, "sender");
					if ("yes".equals(getType(str, "result"))) {
						for (int i = 0; i < mf.list.size(); i++) {
							if (sender.equals(mf.list.get(i).name)) {
								String message = "<msg><type>filestart</type><name>" + mf.list.get(i).chatname
										+ "</name><sender>" + mf.list.get(i).name + "</sender><filename>"
										+ mf.list.get(i).file.getName() + "</filename></msg>";
								oos.writeUTF(message);
								oos.flush();
								Tool.sendFile(mf.list.get(i).file, oos);
							}
						}
					} else if ("no".equals(getType(str, "result"))) {
						JOptionPane.showMessageDialog(mf, "对方拒绝接收你的文件");
					}

				} else if ("file".equals(type)) {

					String sender = getType(str, "sender");
					File file = (File) ois.readObject();
					type = "file return";
					// 如果同意接受
					int n = JOptionPane.showConfirmDialog(mf, sender + "发送+" + file.getName() + "给您,是否接受", "系统消息",
							JOptionPane.YES_NO_OPTION);
					if (n == 0) {
						String result = "yes";
						// 反馈结果
						str = "<msg><type>" + type + "</type><result>" + result + "</result><sender>" + sender
								+ "</sender><name>" + mf.username + "</name></msg>";
						oos.writeUTF(str);
						oos.flush();
					} else {
						// 反馈结果
						String result = "no";
						str = "<msg><type>" + type + "</type><result>" + result + "</result><name>" + mf.username
								+ "</name><sender>" + sender + "</sender></msg>";
						oos.writeUTF(str);
						oos.flush();
					}
				}
				// 初始聊天窗口
				else if ("request".equals(type)) {
					ArrayList<MsPojo> list = (ArrayList<MsPojo>) ois.readObject();
					con.setchat(list);

				} else if ("change".equals(type)) {
					String result = getType(str, "result");
					con.prompt(result);
				}
				// 聊天记录
				else if ("jilu".equals(type)) {
					String chatname = getType(str, "chatname");
					ArrayList<MsPojo> list = (ArrayList<MsPojo>) ois.readObject();
					con.jilu(chatname, list);
				} else if ("jk".equals(type)) {
					String sender = getType(str, "sender");
					String con = getType(str, "con");
					String p;
					if (con.equals("1")) {
						p = "请求被控制";
					} else {
						p = "请求控制";
					}
					int n = JOptionPane.showConfirmDialog(mf, sender + p + ",是否接受", "系统消息", JOptionPane.YES_NO_OPTION);
					if (n == 0) {
						FlowTool.send("<msg><type>yzjk</type><result>yes</result><name>" + sender + "</name><con>" + con
								+ "</con><msg>");
					} else {
						FlowTool.send("<msg><type>yzjk</type><result>no</result><con>" + con + "</con><name>" + sender
								+ "</name><msg>");
					}
				} else if ("yzjk".equals(type)) {
					String result = getType(str, "result");
					String name = getType(str, "name");
					String con = getType(str, "con");
					if (result.equals("yes")) {
						if (con.equals("1")) {
							Thread.sleep(100);
							image(name);
						} else {
							mb = new MFrame(name);
						}
					}

				} else if ("jks".equals(type)) {
					String con = getType(str, "con");
					String name = getType(str, "name");
					if (con.equals("1")) {
						mb = new MFrame(name);
					} else {
						Thread.sleep(100);
						image(name);
					}

				} else if ("jk return".equals(type)) {
					f = false;
				} else if ("starimage".equals(type)) {
					int i = MyClient.getInstance().getOis().readInt();
					byte[] b = new byte[i];
					MyClient.getInstance().getOis().readFully(b);
					ByteArrayInputStream in = new ByteArrayInputStream(b);
					mb.setBi(ImageIO.read(in));

				} else if ("starevent".equals(type)) {
					Object object = ois.readObject();
					if (object instanceof KeyEvent) {
						KeyEvent key = (KeyEvent) object;
						int id = key.getID();
						if (id == KeyEvent.KEY_PRESSED) {
							robot.keyPress(key.getKeyCode());
						} else if (id == KeyEvent.KEY_RELEASED) {
							robot.keyRelease(key.getKeyCode());
						}

					} else if (object instanceof MouseEvent) {
						MouseEvent mouse = (MouseEvent) object;
						int id = mouse.getID();
						if (id == MouseEvent.MOUSE_PRESSED) {
							int buttonNum = mouse.getButton();
							if (buttonNum == MouseEvent.BUTTON1) {
								robot.mousePress(InputEvent.BUTTON1_MASK);
							} else if (buttonNum == MouseEvent.BUTTON2) {
								robot.mousePress(InputEvent.BUTTON2_MASK);
							} else if (buttonNum == MouseEvent.BUTTON3) {
								robot.mousePress(InputEvent.BUTTON3_MASK);
							}
						} else if (id == MouseEvent.MOUSE_RELEASED || id == MouseEvent.MOUSE_CLICKED) {
							int buttonNum = mouse.getButton();
							if (buttonNum == MouseEvent.BUTTON1) {
								robot.mouseRelease(InputEvent.BUTTON1_MASK);
							} else if (buttonNum == MouseEvent.BUTTON2) {
								robot.mouseRelease(InputEvent.BUTTON2_MASK);
							} else if (buttonNum == MouseEvent.BUTTON3) {
								robot.mouseRelease(InputEvent.BUTTON3_MASK);
							}
						} else if (id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED) {

							robot.mouseMove(mouse.getX(), mouse.getY());
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	// 不断的画截图传过去
	public void image(final String name) {
		new Thread() {
			public void run() {
				try {
					String s = "<msg><type>Image</type><name>" + name + "</name></msg>";
					while (f) {
						System.out.println(f);
						Dimension dimen = Toolkit.getDefaultToolkit().getScreenSize();
						Rectangle r = new Rectangle(0, 0, dimen.width, dimen.height);
						ByteArrayOutputStream bai = new ByteArrayOutputStream();
						MyClient.getInstance().getOos().writeUTF(s);
						MyClient.getInstance().getOos().flush();
						BufferedImage bi = robot.createScreenCapture(r);
						ImageIO.write(bi, "jpeg", bai);
						byte[] bytes = bai.toByteArray();
						MyClient.getInstance().getOos().writeInt(bytes.length);
						MyClient.getInstance().getOos().flush();
						MyClient.getInstance().getOos().write(bytes);
						MyClient.getInstance().getOos().flush();
						Thread.sleep(10);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}.start();

	}

	// 字符串解析
	public static String getType(String str, String s) {
		int s1 = str.indexOf("<" + s + ">") + s.length() + 2;
		int s2 = str.indexOf("</" + s + ">");
		String snew = str.substring(s1, s2);
		return snew;
	}

}


注意一下,这里有个链表里的类型是(UserPojo)这个类型不是系统自带的,是服务器定义的用户类,我这边是采取直接导包的方式导入进来的,否则调用不到,这个类里也设计到一些前台的处理,算是一个与前台有交互的类,第二次做这种集体分工的项目,经验可能不是很够,做的相对也不是很好,但感觉对自己的提升还是蛮大的,尤其是对通信的原理和过程,有了一个更加深入的理解。以下是效果图:(这里不是QQ的截图,之前说过前台的那位童鞋图片是截的QQ的,但这个确实是我们实际的效果图)


登陆注册界面:

登陆成功后的界面

通信项目阶段总结-IM聊客户端_第1张图片

聊天界面

文件传输界面:

当然还有远程监控的,这里没办法做截图,但实际是做了这个功能的,总体原理就是被控制方不停地发截图,服务器做转发,转发给控制方,还有鼠标键盘等监听也是不停的发送转发。

基本就是这些,全部客户端+服务器的代码太多,我这里只给出我的部分代码,剩下的所有代码我会打包成资源,方便大家的共同学习交流。

















你可能感兴趣的:(通信项目阶段总结-IM聊客户端)