【问题标题】:Java TCP Client-send blocked?Java TCP 客户端发送被阻止?
【发布时间】:2011-08-15 08:27:42
【问题描述】:

我正在编写一个与 C 服务器对话的 java TCP 客户端。 我必须在两者之间交替发送和接收。 这是我的代码。

  1. 服务器将二进制 msg(len) 的长度发送给客户端(java)
  2. 客户端发送“ok”字符串
  3. 服务器发送二进制文件,客户端分配一个 'len' 字节的字节数组来接收它。
  4. 它再次发回“ok”。

步骤 1. 有效。我得到“len”值。然而,客户端被“发送阻塞”并且服务器等待接收数据。

谁能看一下。

在我定义的 try 块中:

            Socket echoSocket = new Socket("192.168.178.20",2400);
            OutputStream os = echoSocket.getOutputStream();       
            InputStream ins = echoSocket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(ins));

            String fromPU = null;


            if( (fromPU = br.readLine()) !=  null){
            System.out.println("Pu returns  as="+fromPU);  

            len = Integer.parseInt(fromPU.trim());
            System.out.println("value of len from PU="+len);

            byte[] str = "Ok\n".getBytes();
            os.write(str, 0, str.length);
            os.flush();

            byte[] buffer = new byte[len];
            int bytes;
            StringBuilder curMsg = new StringBuilder();
            bytes =ins.read(buffer);
            System.out.println("bytes="+bytes); 
            curMsg.append(new String(buffer, 0, bytes));            
            System.out.println("ciphertext="+curMsg); 
                    os.write(str, 0, str.length);
            os.flush();
            }

更新:

这是我的代码。目前,双方都没有接收或发送阻塞。但是,无论是缓冲阅读器还是数据输入流阅读器,我都无法发送 ok msg。在服务器端,我得到了大量的字节而不是 2 个字节。

            Socket echoSocket = new Socket("192.168.178.20",2400);
            OutputStream os = echoSocket.getOutputStream();   
            InputStream ins = echoSocket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(ins));
            DataInputStream dis = new DataInputStream(ins);
            DataOutputStream dos = new DataOutputStream(os);
            if( (fromPU = dis.readLine()) !=  null){
            //if( (fromPU = br.readLine()) !=  null){
            System.out.println("PU Server returns length as="+fromPU);      
            len = Integer.parseInt(fromPU.trim());
            byte[] str = "Ok".getBytes();
            System.out.println("str.length="+str.length);
            dos.writeInt(str.length);
            if (str.length > 0) {
                    dos.write(str, 0, str.length);
                 System.out.println("sent ok");
            }
            byte[] buffer = new byte[len];
            int bytes;
            StringBuilder curMsg = new StringBuilder();
            bytes =ins.read(buffer);
            System.out.println("bytes="+bytes); 
                curMsg.append(new String(buffer, 0, bytes));            
                System.out.println("binarytext="+curMsg); 

            dos.writeInt(str.length);
            if (str.length > 0) {
                    dos.write(str, 0, str.length);
                 System.out.println("sent ok");
            }

【问题讨论】:

  • “客户端发送被阻止”是什么意思?你试过调试吗?

标签: java sockets tcp


【解决方案1】:

几个想法:

        len = Integer.parseInt(fromPU.trim());

您应该根据有意义的最大值检查给定的大小。您的服务器不太可能向客户端发送 2 GB 的消息。 (也许会,但可能会有更好的设计。:) 您通常不想分配远程客户端要求分配多少内存。这是轻松进行远程拒绝服务攻击的秘诀。

        BufferedReader br = new BufferedReader(new InputStreamReader(ins));
        /* ... */
        bytes =ins.read(buffer);

也许你的BufferedReader 吸收了太多数据? (服务器是否在继续之前等待Ok?)您确定在附加BufferedReader 对象后允许您从底层InputStreamReader 对象中读取吗?

请注意,在接下来的两周内,TCP 可以免费以 10 字节块的形式传送您的数据 :) -- 因为封装、不同的硬件等等使得很难判断最终将使用的数据包的大小两个同行,大多数应用程序正在寻找特定数量的数据,而是使用类似这样的代码填充他们的缓冲区(从 Unix 环境中的高级编程中偷来的,一本好书;可惜代码在C和你的代码是Java,但原理是一样的):

ssize_t             /* Read "n" bytes from a descriptor  */
readn(int fd, void *ptr, size_t n)
{
    size_t      nleft;
    ssize_t     nread;

    nleft = n;
    while (nleft > 0) {
        if ((nread = read(fd, ptr, nleft)) < 0) {
            if (nleft == n)
                return(-1); /* error, return -1 */
            else
                break;      /* error, return amount read so far */
        } else if (nread == 0) {
            break;          /* EOF */
        }
        nleft -= nread;
        ptr   += nread;
    }
    return(n - nleft);      /* return >= 0 */
}

要指出的是,填充缓冲区可能需要对 read() 进行一次、十次或一百次调用,并且您的代码必须能够适应网络功能的微小变化。

【讨论】:

  • 谢谢萨诺德。一旦我修复了这个错误,我将限制字节长度。
【解决方案2】:

在流周围使用BufferedReader,然后尝试从流中读取二进制数据是个坏主意。如果服务器实际上一口气发送了所有数据,并且BufferedReader 已经读取了二进制数据以及它返回的行,我不会感到惊讶。

您能控制协议吗?如果是这样,我建议您更改它以将数据长度发送为 binary (例如固定的 4 个字节),这样您就不需要弄清楚如何在文本和二进制之间切换(即基本上是一种痛苦)。

如果你不能这样做,你可能需要一次读取一个字节,直到你看到代表\n的字节,然后将你读到的内容转换成文本,解析它,然后将其余部分作为一个块读取。这有点低效(一次读取一个字节而不是一次读取一个缓冲区),但我想此时读取的数据量非常小。

【讨论】:

  • +1:另一种方法可能是将 DataInputStream 与 readLine() 和 readFully() 一起使用。虽然 readLine() 并不理想,但它可能会做 OP 想要的。
  • @Peter:可能。它已被弃用是有原因的,但这是一个好主意。我个人希望协议设计者停止以这种方式混合文本和二进制文件。 (当然,可以将文本封装在已知大小的二进制块中。那是不同的。)
  • 恕我直言,除非您非常清楚自己在做什么,否则您当然不应该混合使用它们。即只有在您完全理解文本和二进制文件的要求并根据需要记录协议之后。
  • 谢谢大家。由于缓冲阅读器可能是一个坏主意。我现在已经更改了代码以使用 DataInput Stream。但是,服务器没有收到“ok”(3 个字节),但似乎收到了 905 个字节!(就像上面 Jon Skeet 和下面提到的 Sarnold 一样?)我将更新贴上代码。是的,我控制着协议。所以也许我可以将长度更改为字节..
  • @pimmling:您是否尝试过我在答案最后一段中的建议?
猜你喜欢
  • 2018-09-19
  • 1970-01-01
  • 1970-01-01
  • 2018-04-29
  • 1970-01-01
  • 2019-03-12
  • 1970-01-01
  • 2015-12-26
  • 1970-01-01
相关资源
最近更新 更多