0

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

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

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

-server 以一次性质询消息(13 字节的随机内容消息)作为响应

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

-server 将从客户端获得的哈希与他计算的哈希进行比较

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

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

有时虽然数据库服务器对身份验证响应正常(取决于质询消息的值)!然后 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 以身份验证失败消息作为响应。

谢谢你的时间!

4

2 回答 2

2

您对问题的猜测是什么?

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

我可以看到一些问题:

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

    您应该使用普通的、朴素的1输入流进行读取和写入,并且应该从预分配的byte[].

  • 这很糟糕:

    for(int i=0; i<1024; i++)
        msgBuf[i] = (byte) msgBufChars[i];
    
    1. 如果您读取的字符不在范围内0 ... 255,则当您将它们塞入msgBuf.

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

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

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


1 - 只需使用 Socket API 提供的流对象。 DataXxxStream是不必要的,因为readandwrite方法只是调用。在这种情况下,我什至不会使用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();
}
于 2013-05-26T02:05:42.980 回答
1

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

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

byte [] msgBuf;
char [] msgBufChars;    

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

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

于 2013-05-26T02:06:25.703 回答