【问题标题】:difference between Java TCP Sockets and C TCP Sockets while trying to connect to JDBC尝试连接到 JDBC 时 Java TCP 套接字和 C TCP 套接字之间的区别
【发布时间】:2013-05-21 05:48:05
【问题描述】:

我的问题是 C 套接字看起来与 Java 套接字的行为不同。我有一个 C 代理,我在工作负载生成器(用 Java 编写的 oltp 基准测试客户端)和 Postgres DB 的 JDBC 连接器之间对其进行了测试。 这很有效,可以将数据从一个转发到另一个,因为它应该。我们需要让这个代理在 Java 中工作,所以我使用了 java.net 中的普通 ServerSocket 和 Socket 类,但我无法让它工作。 Postgres 会返回一个认证错误信息,假设客户端没有发送正确的密码。

以下是 JDBC 协议的身份验证工作原理:

-client 发送请求以连接到指定数据库名称和用户名的数据库

-服务器以一次性质询消息(13 字节的随机内容消息)进行响应

-client 将此消息与用户密码连接并执行 md5 哈希

-server 比较从客户端得到的哈希值和他计算的哈希值

[执行此过程是为了避免重放攻击(如果客户端仅发送其密码的 md5 哈希,则攻击者可以重放此消息,假装他是客户端)]

所以我用 tcpdump 检查了数据包,它们看起来是正确的!大小完全符合其应有的大小,因此内容可能已损坏(??)

虽然数据库服务器有时会响应 ok 进行身份验证(取决于质询消息的值)!!然后 oltp 客户端发送了几个查询,但它在一段时间内崩溃了...... 我猜可能和编码有关,所以我尝试了 C 使用的编码(US-ANSII),但还是一样。

我在 C 和 Java 中都使用固定大小的字符或字节数组发送数据!

我真的没有更多的想法,因为我尝试了很多案例......

您对问题的猜测是什么?

这是一个有代表性的代码,可以帮助你更清楚地了解:

byte [] msgBuf;
char [] msgBufChars;
while(fromInputReader.ready()){
    msgBuf = new byte[1024];
    msgBufChars = new char[1024];
    // read data from one party
    int read = fromInputReader.read(msgBufChars, 0, 1024);
    System.out.println("Read returned : " + read);
    for(int i=0; i<1024; i++)
        msgBuf[i] = (byte) msgBufChars[i];
    String messageRead = new String(msgBufChars);
    String messageToWrite = new String(msgBuf);
    System.out.println("message read : "+messageRead);
    System.out.println("message to write : "+new String(messageToWrite));
    // immediatelly write data to other party (write the amount of data we read (read value) )
    // there is no write method that takes a char [] as a parameter, so pass a byte []
    toDataOutputStream.write(msgBuf, 0, read);
    toDataOutputStream.flush();
}

一开始有几个消息交换,然后 Postgres 以身份验证失败消息作为响应。

感谢您的宝贵时间!

【问题讨论】:

  • 不要使用Reader进行输入,使用InputStream处理原始字节,这样就不会通过字符串编码/解码改变任何字节

标签: java c sockets tcp proxy


【解决方案1】:

您对问题的猜测是什么?

这与 C 与 Java 套接字无关。这与糟糕的 Java 代码有关。

我可以看到一些问题:

  • 您在应该是二进制流的情况下使用阅读器。这将导致数据从字节(来自 JDBC 客户端)转换为字符,然后再转换回字节。根据读者使用的字符集,这可能具有破坏性。

    您应该使用简单、朴素的1 输入流进行读取和写入,并且应该从预先分配的byte[] 读取/写入/写入。

  • 这太可怕了:

    for(int i=0; i<1024; i++)
        msgBuf[i] = (byte) msgBufChars[i];
    
    1. 如果您读取的字符不在0 ... 255 范围内,那么当您将它们填充到msgBuf 时,您就是在弄乱它们。

    2. 假设您实际上有 1024 个字符。

  • 您正在使用ready() 方法来决定何时停止阅读内容。这几乎肯定是错误的。阅读该方法的 javadoc(并考虑一下),您应该明白为什么它是错误的。 (提示:如果代理的读取速度快于客户端的传输速度会怎样?)

    您应该使用while(true),然后如果read 告诉您它已到达流的末尾,则退出循环;即如果它返回-1 ...


1 - 只需使用 Socket API 提供的流对象。 DataXxxStream 是不必要的,因为 readwrite 方法只是调用。在这种情况下,我什至不会使用 BufferedXxxStream 包装器,因为您已经在使用字节数组进行自己的缓冲了。


我会这样写代码:

byte [] buffer = new byte[1024];  // or bigger
while(true) {
    int nosRead = inputStream.read(buffer);
    if (nosRead < 0) {
        break;
    }

    // Note that this is a bit dodgy, given that the data you are converting is
    // binary.  However, if the purpose is to see what embedded character data 
    // looks like, and if the proxy's charset matches the text charset used by 
    // the client-side JDBC driver for encoding data, this should achieve that.
    System.out.println("Read returned : " + nosRead);
    System.out.println("message read : " + new String(buffer, 0, nosRead));

    outputStream.write(buffer, 0, nosRead);
    outputStream.flush();
}

【讨论】:

  • 非常好的答案,谢谢!我将每个 char 类型转换为一个字节,因为它们具有相同的大小(每个 1 个字节),但你是对的,可能会丢失一些信息。老实说,我没有注意到 DataInputStream 提供了一种读取字节 [] 的读取方法(一开始我曾经一次读取一个整数,而不是固定大小的缓冲区),这就是我进行转换的原因从 char 到 byte... 因此,使用 DataInputStream 读取和 DataOutputStream 写入固定大小的 byte[] 它完美地工作!非常感谢您的 cmets!
  • 更新了我的回答,指出使用 DataInputStream 和 DataOutputStream 是不必要的......尽管它可能是无害的。
【解决方案2】:

C 套接字看起来与 Java 套接字的行为不同。

不可能。 Java 套接字只是 C 套接字上的一个非常薄的层。你的这种思路走错了路。

byte [] msgBuf;
char [] msgBufChars;    

为什么要写入字节时要读取字符?不要使用Readers,除非您知道输入是文本。

并且不要调用 ready()。很少有正确的用途,这不是其中之一。只是阻止。

【讨论】:

  • 你是对的,但是我进行了深度数据包检查,没有发现数据包内容有任何问题!这就是为什么我认为 Java 需要调整一些设置才能以所需格式传输数据包的原因。感谢 cmets!
猜你喜欢
  • 2016-08-15
  • 1970-01-01
  • 1970-01-01
  • 2013-06-01
  • 2011-08-31
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多