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