这个学习项目的客户端业务流程基本是我写的,由于界面是用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的,但这个确实是我们实际的效果图)
登陆注册界面:
登陆成功后的界面
聊天界面
文件传输界面:
当然还有远程监控的,这里没办法做截图,但实际是做了这个功能的,总体原理就是被控制方不停地发截图,服务器做转发,转发给控制方,还有鼠标键盘等监听也是不停的发送转发。
基本就是这些,全部客户端+服务器的代码太多,我这里只给出我的部分代码,剩下的所有代码我会打包成资源,方便大家的共同学习交流。