简易SVN

svn项目总结

这里写图片描述
这里写图片描述

此次svn项目主要功能:
客户端:
加载服务器文件,从服务器上更新文件到本地,提交本地文件到服务器上
服务器端:
客户端用户列表(每有一个IP连接上或断开服务器,列表都会更新),客户端操作记录(每个客户端的操作都会显示在文本框中,但这些记录没有保存在本地),服务器接收客户端的操作信息,相应做更新文件或发送消息的操作。
技术点主要就是socket的运用,以及文件版本控制原理,下面有一些关键点:
既然是socket项目,当然先建立socket连接才是一切开始的首要:

//客户端
socket = new Socket(getIP(), getPort());
ct = new ClientThread(socket);

//服务器
ServerSocket ss = new ServerSocket(7979);
while (true) {
    // 等待客户端的请求
    System.out.println("=======");
    Socket s = ss.accept();
    ServerThread st = new ServerThread(s);
    new Thread(st).start();
    // 输出谁连接上了
    jta1.append(s.getInetAddress() + "连接成功" + "\n");
    // jta2.append(s.getInetAddress());
    String suser = s.getInetAddress().toString();
    user.add(suser);
    System.out.println(s.getInetAddress() + "连接成功");
}

封装从socket获取的输入输出流

InputStream ins = s.getInputStream();
OutputStream ous = s.getOutputStream();
dis = new DataInputStream(ins);
dos = new DataOutputStream(ous);
oos = new ObjectOutputStream(ous);
ois = new ObjectInputStream(ins);

使用ObjectOutputStream,ObjectInputStream发送读取对象信息

Message message = new Message(0, null);
ct.oos.writeObject(message);
ct.oos.flush();
Map m1 = (Map) ct.ois.readObject();

客户端读取本地文件并发送到服务器上

File file = new File(path);
FileInputStream fis = new FileInputStream(file);
// 获取文件输出流可读字节数
int byteNumber = fis.available();
byte[] count = new byte[byteNumber];
fis.read(count);
DataOutputStream dos = new DataOutputStream(ct.os);
System.out.println("获取数据输出流");
dos.writeInt(byteNumber);
System.out.println("count=" + count.length);
dos.write(count, 0, byteNumber);
// dos.write(count);
dos.flush();

服务器接收客户端发来的文件数据并保存到本地

int len = dis.readInt();
System.out.println(len + "=====================");
byte[] all = new byte[len];
dis.read(all);
System.out.println("文件" + new String(all));
System.out.println("=========" + all + "==========");
// System.out.print(all.toString());
FileOutputStream fos = new FileOutputStream("D:\\server\\" + bookname);
DataOutputStream fos1 = new DataOutputStream(new BufferedOutputStream(fos));
fos1.write(all);
fos1.flush();

判断读出来的对象类型

if ((obj2 = ct.ois.readObject()) instanceof Map) {
    System.out.println("接收到Map");
    serverMap = (Map<String, FileVersionInfo>) obj2;
}

svn的关键点就是版本控制,这里是存放文件版本信息的实体类FileVersionInfo,可以在客户端和服务器之间传递。为了在传递中不发生错误,传输过程中的类都会实现Serializable接口。

public class FileVersionInfo implements Serializable {
    private static final long serialVersionUID = 1L;

    public int version;
    public Date modifyDate;
    public String modifyUser;
    public int fileStatus;
    public List<FileVersionInfo> hisFileVersionInfo;

    public FileVersionInfo(int version, Date modifyDate, String modifyUser,
            int fileStatus, List<FileVersionInfo> hisFileVersionInfo) {
        this.version = version;
        this.modifyDate = modifyDate;
        this.modifyUser = modifyUser;
        this.fileStatus = fileStatus;
        this.hisFileVersionInfo = hisFileVersionInfo;
    }

    public FileVersionInfo(int version, Date modifyDate, String modifyUser,
            List<FileVersionInfo> hisFileVersionInfo) {
        this.version = version;
        this.modifyDate = modifyDate;
        this.modifyUser = modifyUser;
        this.hisFileVersionInfo = hisFileVersionInfo;
    }

}

这里在客户端使用了一个存放文件版本信息的类,用来加载更新本地文件信息并保存。
文件信息类ClientFileInfo

public class ClientFileInfo implements Serializable {
    private static final long serialVersionUID = 1L;

    private static Map<String, FileVersionInfo> allFileInfo;
    private static ClientFileInfo cfi;

    private ClientFileInfo(Map<String, FileVersionInfo> map) {
        this.allFileInfo = map;
    }

    public synchronized static Map<String, FileVersionInfo> getFileInfo() {
        if (cfi == null) {
            cfi = new ClientFileInfo(new HashMap<String, FileVersionInfo>());
        }
        return cfi.allFileInfo;
    }

    public static void serialize() throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
                new File("C:/ClientFileInfo.txt")));
        oos.writeObject(ClientFileInfo.getFileInfo());
    }

    public static void deserialize() throws FileNotFoundException, IOException,
            ClassNotFoundException {

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                new File("C:/ClientFileInfo.txt")));
        allFileInfo = (Map) ois.readObject();
    }

    public static void init(String path, String modifyUser) {
        System.out.println("path=" + path);
        if (!"".equals(path)) {
            File file = new File(path);
            File[] files = file.listFiles();
            FileVersionInfo fvi;
            for (int i = 0; i < files.length; i++) {
                long time = files[i].lastModified();
                Date date = new Date(time);
                // SimpleDateFormat sdf=new
                // SimpleDateFormat("YYYY-mm-dd hh-MM-ss");
                // String modifyDate=sdf.format(date);
                List<FileVersionInfo> hisFile = new ArrayList<FileVersionInfo>();
                // 初始版本为1,文件状态为提交
                fvi = new FileVersionInfo(1, date, modifyUser, 2, hisFile);
                hisFile.add(fvi);
                fvi = new FileVersionInfo(1, date, modifyUser, 2, hisFile);

                allFileInfo.put(files[i].getName(), fvi);
            }
        } else {
            JOptionPane.showMessageDialog(null, "请输入有效路径!!");
        }

    }

    public static void addFile(String path, String name) {
        File file = new File(path);
        File[] files = file.listFiles();
        FileVersionInfo fvi;
        for (int i = 0; i < files.length; i++) {
            if (!allFileInfo.containsKey(files[i].getName())) {
                long time = files[i].lastModified();
                Date date = new Date(time);
                // SimpleDateFormat sdf=new
                // SimpleDateFormat("YYYY-mm-dd hh-MM-ss");
                // String modifyDate=sdf.format(date);
                List<FileVersionInfo> hisFile = new ArrayList<FileVersionInfo>();
                // 初始版本为1,文件状态为提交
                fvi = new FileVersionInfo(1, date, name, 2, hisFile);
                hisFile.add(fvi);
                fvi = new FileVersionInfo(1, date, name, 2, hisFile);

                allFileInfo.put(files[i].getName(), fvi);
            }
        }
    }
}

ServerFileInfo是用来保存服务器端文件信息的类,与客户端ClientFileInfo有点不一样,只是用来保存数据和获取,不提供修改功能。

文件状态修改类TellFileStatus
四个状态之间的转变的理解是svn的关键。
简要说下本地文件状态修改的逻辑:客户端的文件信息与服务器发来的信息做对比,与服务器相同则客户端状态改为一致,如果本地修改了文件是需要提交的,而服务器上又有其他的客户端提交过该文件,导致服务器上的该文件版本比本地大需要更新,则产生冲突,如果两者版本相同则比较它们的最后修改时间,因为可能本地修改了文件,服务器,没修改,本地最后修改时间会大于服务器,此时文件状态为提交,如果小于的话,就是有其他的客户端也提交了同名文件,修改了服务器文件的时间和版本,则文件状态要改为更新状态。
这里还有一点补充:
如果文件产生冲突,在现有主流的版本控制软件中,是有合并这一方法的,但是我这里没有解决这个冲突问题。

public class TellFileStatus {

    Map m1 = ClientFileInfo.getFileInfo();
    boolean flag = true;

    public TellFileStatus(Map serverMap, String path) {
        Set set1 = m1.keySet();
        Iterator clientIter = set1.iterator();
        File file = new File(path);
        // 保存客户端key值
        List<String> list = new ArrayList<String>();
        // 保存更新状态的key值
        List<String> _list = new ArrayList<String>();
        while (clientIter.hasNext()) {
            flag = false;
            String key = (String) clientIter.next();
            FileVersionInfo value = (FileVersionInfo) m1.get(key);
            if (value.fileStatus == 0) {
                _list.add(key);
            } else
                list.add(key);
        }
        for (int i = 0; i < _list.size(); i++) {
            m1.remove(_list.get(i));
        }
        Set set = serverMap.keySet();
        Iterator serverIter = set.iterator();
        int i = 0;
        while (serverIter.hasNext()) {
            flag = true;
            String _key = (String) serverIter.next();
            FileVersionInfo _value = (FileVersionInfo) serverMap.get(_key);
            System.out.println("clientkey=" + list.get(i) + ",serverkey="
                    + _key);
            if (_key.equals(list.get(i))) {
                compare((FileVersionInfo) serverMap.get(_key),
                        (FileVersionInfo) m1.get(list.get(i)), path + "\\"
                                + list.get(i));
                break;
            }
        }
        Set set2 = serverMap.keySet();
        Iterator serverIter1 = set2.iterator();
        while (serverIter1.hasNext()) {
            String _key = (String) serverIter1.next();
            FileVersionInfo _value = (FileVersionInfo) serverMap.get(_key);
            if (!m1.containsKey(_key)) {
                _value.fileStatus = 0;
                m1.put(_key, _value);
            }
        }
        // if (!flag) {// 如果服务器Map为空,迭代本地Map,文件状态为新增
        // while (clientIter.hasNext()) {
        // key = (String) clientIter.next();
        // FileVersionInfo value = (FileVersionInfo) m1.get(key);
        // New(value);// 改变文件状态
        // }
        // }

    }

    /**
     * 对比客户端和服务器端文件的相关信息
     * 
     * @param serverFileVersionInfo
     * @param clientFileVersionInfo
     * @param path
     */
    void compare(FileVersionInfo serverFileVersionInfo,
            FileVersionInfo clientFileVersionInfo, String path) {
        File file = new File(path);

        long time = file.lastModified();
        Date lastClientmotifydate = new Date(time);
        int CurrentlyServervision = serverFileVersionInfo.version;
        int LocalClientvision = clientFileVersionInfo.version;
        Date LocalClientmotifyDate = clientFileVersionInfo.modifyDate;
        if (LocalClientmotifyDate.getTime() != lastClientmotifydate.getTime() &&LocalClientvision < CurrentlyServervision) {
            conflict(clientFileVersionInfo);
            // 冲突
        } else {

        if (LocalClientvision == CurrentlyServervision) {
            System.out.println("LocalClientmotifyDate=" + LocalClientmotifyDate + ",,,lastClientmotifydate=" + lastClientmotifydate);

        if (LocalClientmotifyDate.getTime() == lastClientmotifydate.getTime()) {
            System.out.println("进入一致");
            same(clientFileVersionInfo);// 与服务器文件一致,本地未更新,不操作
        } else {
            submit(clientFileVersionInfo);
        }
    } else {
            update(clientFileVersionInfo);
    }
}
}

    static void same(FileVersionInfo m) {// 一致
        m.fileStatus = 1;
    }

    void submit(FileVersionInfo m) {// 提交
        m.fileStatus = 2;
    }

    void update(FileVersionInfo m) {// 更新
        m.fileStatus = 0;
    }

    void conflict(FileVersionInfo m) {// 冲突
        m.fileStatus = 3;
    }

    void New(FileVersionInfo m) {// 冲突
        m.fileStatus = 4;
    }
}

封装了一个消息类Message,用于告知服服务器应该发送哪种消息给客户端

public class Message implements Serializable {
    private static final long serialVersionUID = 1L;
    public int type;
    public List<String> filename;

    public Message(int type, List<String> filename) {
        this.type = type;
        this.filename = filename;
    }

    public int getType() {
        return this.type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public List<String> getFilename() {
        return this.filename;
    }

    public void setFilename(List<String> filename) {
        this.filename = filename;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值