jjzjj

java - 在服务于 Socket 连接的两个线程之间共享公共(public)数据

coder 2024-03-31 原文

我在 SO 上看到很多类似的问题,但几乎没有一个在图片中有 Socket 。所以请花点时间阅读问题。

我有服务器应用程序(使用 ServerSocket)监听请求,当客户端尝试连接时,会创建新线程来为客户端提供服务(并且服务器返回监听模式以接收新请求).现在,我需要根据其他客户端发送到服务器的内容来响应一个客户端。

示例:

  • ServerSocket 监听传入连接。
  • 客户端 A 连接,创建新线程为 A 服务。
  • 客户端 B 连接,创建新线程为 B 服务。
  • A 向服务器发送消息“Hello from A”。
  • 将此消息作为对客户端 B 的响应发送。

我是整个“线程间通信”的新手。显然,上面提到的情况听起来很简单,但我描述这个是为了获得提示,因为我将在客户端之间交换大量数据,将服务器保持为中间。

此外,如果我想将共享对象限制在例如 10 个特定客户端,该怎么办?这样,当第 11 个客户端连接到服务器时,我创建了新的共享对象,它将用于在第 11、12、13 ...... 到第 20 个客户端之间交换数据。对于每组 10 个客户端,依此类推。

我尝试了什么:(我猜是愚蠢的)

  • 我有一个 public 类,该类的对象应该作为 public static 共享,这样我就可以将它用作全局对象而无需实例化它,例如 MyGlobalClass .SharedMsg.
  • 那行不通,我无法将一个线程中收到的数据发送到另一个线程。

我知道有一个明显的锁定问题,因为如果一个线程正在写入一个对象,则在第一个线程完成写入之前,其他线程无法访问它。

那么解决这个问题的理想方法是什么?

更新

由于我为传入连接请求创建线程的方式,我无法理解如何在线程之间共享相同的对象,因为如上所述使用全局对象不起作用。

以下是我如何监听传入连接并动态创建服务线程。

// Method of server class
public void startServer()
{
    if (!isRunning)
    {
        try
        {
            isRunning = true;
            while (isRunning)
            {
                try
                {
                    new ClientHandler(mysocketserver.accept()).start();
                }
                catch (SocketTimeoutException ex)
                {
                    //nothing to perform here, go back again to listening.
                }
                catch (SocketException ex)
                {
                    //Not to handle, since I'll stop the server using SocketServer's close() method, and its going to throw SocketException anyway.
                }
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
    else
        System.out.println("Server Already Started!");
}

还有 ClientHandler 类。

public class ClientHandler extends Thread
{
    private Socket client = null;
    private ObjectInputStream in = null;
    private ObjectOutputStream out = null;

    public ClientHandler(Socket client)
    {
        super("ClientHandler");
        this.client = client;
    }

    //This run() is common for every Client that connects, and that's where the problem is.
    public void run()
    {
        try
        {
            in = new ObjectInputStream(client.getInputStream());
            out = new ObjectOutputStream(client.getOutputStream());

            //Message received from this thread.
            String msg = in.readObject().toString();
            System.out.println("Client @ "+ client.getInetAddress().getHostAddress() +" Says : "+msg);


            //Response to this client.
            out.writeObject("Message Received");

            out.close();
            in.close();
            client.close();
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
}

我相信我创建动态线程来服务每个连接的客户端的方式,使用 Global 对象共享相同的数据源是不可能的,因为上面的 run() 的主体正是对于每个连接的客户端都是一样的,因此这个相同的方法既是消费者又是生产者。我应该进行哪些修复才能为每个连接创建动态线程并仍然共享同一个对象。

最佳答案

您可能需要一个队列用于每个客户端之间的通信。每个队列都是将数据从一个客户端推送到另一个客户端的“管道”。

你会像这样使用它(伪代码):

Thread 1:
Receive request from Client A, with message for Client B
Put message on back of concurrent Queue A2B
Respond to Client A.

Thread 2:
Receive request from Client B.
Pop message from front of Queue A2B
Respond to Client B with message.

您可能还希望它是通用的,因此您有一个 AllToB 队列,许多客户端(因此有许多线程)可以写入。

注意类别:ConcurrentLinkedQueue , ArrayBlockingQueue .

如果你想限制消息的数量,ArrayBlockingQueue 及其容量构造函数可以让你做到这一点。如果不需要阻塞功能,可以使用方法 offerpoll 而不是 puttake.

我不担心共享队列,这会使问题变得更加复杂。仅当您知道需要解决内存使用问题时才这样做。

编辑:根据您的更新:

如果您需要在所有动态创建的实例之间共享一个实例,您可以:

  1. 创建一个静态实例。
  2. 将其传递给构造函数。

示例 1:

public class ClientHandler extends Thread
{
  public static final Map<ClientHandler, BlockingQueue<String>> messageQueues 
    = new ConcurrentHashMap<>();

  <snip>

  public ClientHandler(Socket client)
  {
    super("ClientHandler");
    this.client = client;
    // Note: Bad practice to reference 'this' in a constructor.
    // This can throw an error based on what the put method does.
    // As such, if you are to do this, put it at the end of the method.
    messageQueues.put(this, new ArrayBlockingQueue<>());
  }

  // You can now access this in the run() method like so:
  // Get messages for the current client.
  // messageQueues.get(this).poll();
  // Send messages to the thread for another client.
  // messageQueues.get(someClient).offer(message);

一些注意事项:

  • messageQueues 对象实际上应该包含某种客户端标识符,而不是短暂的对象引用。
  • 更可测试的设计会将 messageQueues 对象传递到构造函数中以允许模拟。
  • 我可能会建议为 map 使用包装器类,这样您就可以使用 2 个参数调用 offer,而不必担心 map 语义。

关于java - 在服务于 Socket 连接的两个线程之间共享公共(public)数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10943841/

有关java - 在服务于 Socket 连接的两个线程之间共享公共(public)数据的更多相关文章

  1. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  2. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  3. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  6. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  7. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  8. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  9. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  10. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

随机推荐