草庐IT

java - Erlang 服务器,Java 客户端 - TCP 消息被拆分?

coder 2023-09-18 原文

正如标题所说,我有一个用 Erlang 编写的服务器,一个用 Java 编写的客户端,它们通过 TCP 进行通信。我面临的问题是 gen_tcp:recv 显然不知道何时收到来自客户端的“完整”消息,因此将其“拆分”为多条消息。

这是我正在做的一个例子(不完整的代码,试图只保留相关部分):

代码

二郎服务器

-module(server).
-export([start/1]).

-define(TCP_OPTIONS, [list, {packet, 0}, {active, false}, {reuseaddr, true}].

start(Port) ->
   {ok, ListenSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
   accept(ListenSocket).

accept(ListenSocket) ->
    {ok, Socket} = gen_tcp:accept(ListenSocket),
    spawn(fun() -> loop(Socket) end),
    accept(ListenSocket).

loop(Socket) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Data} ->
            io:format("Recieved: ~s~n", [Data]),
            loop(Socket);
        {error, closed} ->
            ok
    end.

Java 客户端

public class Client {
    public static void main(String[] args) {
        Socket connection = new Socket("localhost", Port);
        DataOutputStream output = new DataOutputStream(connection.getOutputStream());
        Scanner sc = new Scanner(System.in);

        while(true) {
            output.writeBytes(sc.nextLine());
        }
    }
}

结果

客户端

Hello!

服务器

Received: H
Received: el
Received: lo!

我一直在搜索,如果我理解正确的话,TCP 不知道消息的大小,你需要手动设置某种分隔符。

但我没有明白的是,如果我改为用 Erlang 编写客户端,消息似乎永远不会分开,如下所示:

Erlang 客户端

-module(client).
-export([start/1]).

start(Port) ->
    {ok, Socket} = gen_tcp:connect({127,0,0,1}, Port, []),
    loop(Socket).

loop(Socket) ->
    gen_tcp:send(Socket, io:get_line("> ")),
    loop(Socket).

结果

客户端

Hello!

服务器

Received: Hello!

这让我想知道它是否可以在 Java 端修复?我在服务器端尝试了几种不同的输出流、写入方法和套接字设置的组合,但没有解决问题。

另外,网络上有大量 Erlang(聊天)服务器示例,它们不做任何分隔符的事情,尽管这些通常在两端都是用 Erlang 编写的。然而,他们似乎假设消息的接收与发送一样。这只是不好的做法,还是当客户端和服务器都用 Erlang 编写时,是否存在一些关于消息长度的隐藏信息?

如果需要进行定界符检查,我很惊讶找不到关于该主题的太多信息。如何以实用的方式完成?

提前致谢!

最佳答案

This makes me wonder if it is something that can be fixed on the Java side?

不,绝对不是。不管您为什么没有碰巧看到 Erlang 客户端的问题,如果您没有在协议(protocol)中加入任何类型的“消息边界”指示,您将无法可靠地检测到整个消息。我强烈怀疑如果您使用 Erlang 客户端发送非常的消息,您仍然会看到拆分的消息。

你应该:

  • 使用某种“消息结束”序列,例如如果不会出现在您的消息中,则为 0 字节。
  • 在每条消息前加上消息的长度。

除此之外,您目前还不能清楚地区分字节和文本。例如,您的 Java 客户端当前静默忽略每个 char 的前 8 位。与其使用 DataOutputStream,我建议只使用 OutputStream,然后对每条消息:

  • 使用特定编码将其编码为字节数组,例如

    byte[] encodedText = text.getBytes(StandardCharsets.UTF_8);
    
  • 将长度前缀写入流(可能是 7 位编码整数,或者可能只是固定宽度,例如 4 字节)。 (实际上,坚持使用 DataOutputStream 会使更简单一些。)

  • 写入数据

在服务器端,您应该通过读取长度来“读取消息”,然后读取指定的字节数。

您无法回避 TCP 是基于流 的协议(protocol)这一事实。如果你想要一个基于消息的协议(protocol),你真的必须自己把它放在首位。 (当然,我确信有有用的库可以做到这一点——但你不应该把它留给 TCP 并寄希望于。)

关于java - Erlang 服务器,Java 客户端 - TCP 消息被拆分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23724112/

有关java - Erlang 服务器,Java 客户端 - TCP 消息被拆分?的更多相关文章

  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 - 具有身份验证的私有(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..

  3. 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/

  4. 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

  5. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

  6. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  7. ruby-on-rails - 在 Rails 中调试生产服务器 - 2

    您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除

  8. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  9. ruby - 分布式事务和队列,ruby,erlang,scala - 2

    我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和

  10. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

随机推荐