在线更新客户列表

时间:2014-06-20 02:53:19

标签: java multithreading client-server rsa public-key

我正在java中编写一个监听特定端口的服务器。不同的客户端定期向服务器发送消息。

消息包含客户端的相应公钥。

服务器有一个公钥列表。

如果列表中没有新接收的公钥,服务器会将其添加到那里。

我已经完成了比较公钥和编写列表中不存在的公钥的部分。

多线程服务器的代码(此处服务器实际侦听两个不同的端口):

while(!isStopped()){
        System.out.println("Server is working");
        try {
                serverSocket = ServerSocketChannel.open();
                serverSocket.configureBlocking(false);
                serverSocket.bind(new InetSocketAddress(serverPortRequest));
                serverSocket.register(selector, SelectionKey.OP_ACCEPT);
                serverSocket = ServerSocketChannel.open();
                serverSocket.configureBlocking(false);
                serverSocket.bind(new InetSocketAddress(serverPortAccept));
                serverSocket.register(selector, SelectionKey.OP_ACCEPT);

                while(selector.isOpen()){
                    selector.select();
                    Set<SelectionKey> readyKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = readyKeys.iterator();
                    while(iterator.hasNext()){
                        SelectionKey key = (SelectionKey) iterator.next();
                        if(key.isAcceptable()){
                            SocketChannel client = serverSocket.accept();
                            (new Thread(new WorkerRunnable(client,"Multithreaded Server"))).start();
                        }
                    }
                }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}

在WorkerRunnable线程中,我检查客户端的公钥是否已经在服务器列表中:

if(!isPresent(userPK)) {
        FileOutputStream pk;
        try {
            pk = new FileOutputStream("contactsPK/contact"+Server.pkCounter++);
            pk.write(userPK);
            pk.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

此处userPK是一个byte数组,用于存储从客户端发送的消息中获取的公钥。

如何定期检查(例如30秒)客户端列表中的客户端是否未向服务器发送消息?我想从服务器的存储公钥列表中删除该客户端,即其公钥。

1 个答案:

答案 0 :(得分:2)

这是一个非常好的问题!

你几乎想要实现你想要实现的目标,你只需要小心几个陷阱!

您可以使用以下命令启动代码:

 while(!isStopped()){
 ...
 }

这是确保您始终在端口上进行侦听的一种方法,但您还要做的是为该进程创建互斥锁。你可以在这里阅读:

http://en.wikipedia.org/wiki/Mutual_exclusion

这样做可以确保在您运行此代码时没有其他进程可以访问处理器。有了这个,不幸的是,当你开始扩展你的代码并添加功能时,你会看到这个循环会阻止你解耦你的代码,确保健壮性和增加功能(我会回到这个后来!)。首先,您要做的是为您的应用程序使用Web框架,这可以使用多种不同的选项来完成。以下是两种可以帮助您做出决定的资源:

http://zeroturnaround.com/rebellabs/the-curious-coders-java-web-frameworks-comparison-spring-mvc-grails-vaadin-gwt-wicket-play-struts-and-jsf/

http://en.wikipedia.org/wiki/Comparison_of_web_application_frameworks#Java

我建议从Spring MVC框架开始,它是目前最通用和最广泛使用的,你可以在线获得足够的资源来帮助你入门。

一旦您选择了框架,您就会想要创建一个MVC架构Web应用程序,这里有几个资源:

http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller http://blog.codinghorror.com/understanding-model-view-controller/

您的视图不会包含太多内容,实际上它可以将公钥传递给您的后端(您的控制器和模型)。

通过上面提到的,您可以摆脱所有循环的代码并等待从端口听到ping,您可以简单地指定要在Spring MVC配置文件中侦听的端口。

现在为有趣的部分!你问:

 "How do i check at regular intervals (say 30 seconds) that a client, from the list of clients, has not sent the server a message? I would like to delete that client i.e. its public key from the list of stored public keys of the server."

这可以通过你后端的两个商店实现:redis - 基于会话的密钥库,以及你风味的简单关系数据库。

您的算法可以简单地如下:

 1. Every time your view forwards the information of a request to your backend, add it to your redis database, in the form <id, key>

 2. Simply create a thread to run every 30 seconds to do:
     2a. get the list of id's from redis
     2b. perform a left outer join on table a and b, where A is your sql table of ids, and B is the list of ids from redis.  A left outer join returns all the values from an inner join plus all values in the left table that do not match to the right table. Here is a resource for SQL Joins: http://blog.codinghorror.com/a-visual-explanation-of-sql-joins/
     2c. The result of the join is your final answer, be sure to store this in your db.
     2d. purge all keys from redis (this is a one-liner)

您可以使用ScheduledExecutorService创建一个在后端每30秒运行一次的线程,如下所示:

 ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
 exec.scheduleAtFixedRate(new Runnable() {
 @Override
 public void run() {
     // 2a from above: (get the list of id's from redis)
     // 2b from above (perform a left outer join on table a and b, where A is your sql table of ids, and B is the list of ids from redis.  A left outer join returns all the values from an inner join plus all values in the left table that do not match to the right table.)
     // 2c from above (The result of the join is your final answer, be sure to store this in your db)
     // 2d from above (purge all keys from redis (this is a one-liner)
}
}, 0, 30, TimeUnit.SECONDS);

由于这是一项非常具体且可重复的任务,因此您还有其他一些与其相关的权衡。您可以创建一个crontab作业,该作业可以执行执行上述操作的脚本,甚至可以使用内置于许多关系数据库功能中的触发器。两者的主要缺点是,如果您选择这样做,您将失去代码可见性。

我上面也提到了关于去耦的问题。当您开始编写可扩展的代码时,耦合(http://en.wikipedia.org/wiki/Coupling_(computer_programming)是一个非常重要的概念。您希望尽可能保持代码松散耦合(http://en.wikipedia.org/wiki/Loose_coupling),因为它将进行调试和制作以OOP方式写作对你来说更容易。