简体   繁体   English

Java Socket InputStream读取丢失的字节

[英]Java Socket InputStream read missing bytes

I got a very strange behaviour by reading bytes from the input stream of a socket. 通过从套接字的输入流中读取字节,我得到了一个非常奇怪的行为。

In my project clients does requests to a service. 在我的项目中,客户确实对服务提出了要求。 For each request a new connection will be established. 对于每个请求,将建立一个新的连接。

At first the bytes are send that tells the service what kind of request will follow. 首先,发送字节,告知服务将遵循哪种请求。

Then the request itself is send. 然后,请求本身被发送。

The service receives the bytes and proceeds the request. 服务接收字节并继续请求。 This does work for at least 95% of all request. 这确实可以满足所有请求的至少95%。 For the remaining 5% there is a strange behaviour that i can not figure out. 对于剩余的5%,我无法弄清一个奇怪的行为。

The bytes are not all the bytes that got sended. 字节不是发送的所有字节。 But the most strange matter on this topic is that the missing bytes are not at the start or at the end of the stream. 但是,关于此主题的最奇怪的事情是丢失的字节不在流的开头或结尾。 They are spread through the entire stream. 它们遍布整个流。

Sadly i can not provide the full code here cause it is work related. 可悲的是我不能在这里提供完整的代码,因为它与工作有关。 But i can provide the test code that shows the issue itself. 但是我可以提供显示问题本身的测试代码。

To figure out what is going on i wrote 2 classes. 为了弄清楚发生了什么,我写了2节课。 One derives from java.net.Socket and the other one from java.net.ServerSocket . 一个从java.net.Socket派生,另一个从java.net.ServerSocket派生。

Here the code: 这里的代码:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;



public class DebugSocket extends Socket
{
    private class InputStreamWrapper extends InputStream
    {
        private int
            availables,
            closes,
            marksupporteds,
            resets;

        private List<Integer>
            marks   = new ArrayList<Integer>(),
            reads   = new ArrayList<Integer>();

        private List<Long>
            skips   = new ArrayList<Long>();


        @Override
        public int available() throws IOException
        {
            availables++;
            return DebugSocket.this.origininput.available();
        }

        @Override
        public void close() throws IOException
        {
            closes++;
            DebugSocket.this.origininput.close();
        }

        @Override
        public synchronized void mark(int readlimit)
        {
            marks.add(readlimit);
            DebugSocket.this.origininput.mark(readlimit);
        }

        @Override
        public boolean markSupported()
        {
            marksupporteds++;
            return DebugSocket.this.origininput.markSupported();
        }

        @Override
        public synchronized void reset() throws IOException
        {
            resets++;
            DebugSocket.this.origininput.reset();
        }

        @Override
        public int read() throws IOException
        {
            int read = DebugSocket.this.origininput.read();

            reads.add(read);

            if ( read != -1 )
            {
                DebugSocket.this.inputdebugbuffer.write(read);
            }

            return read;
        }

        @Override
        public int read(byte[] b) throws IOException
        {
            int read = DebugSocket.this.origininput.read(b);

            DebugSocket.this.inputdebugbuffer.write(b, 0, read);

            return read;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException
        {
            int read = DebugSocket.this.origininput.read(b, off, len);

            DebugSocket.this.inputdebugbuffer.write(b, off, read);

            return read;
        }

        @Override
        public long skip(long n) throws IOException
        {
            long skipped = DebugSocket.this.origininput.skip(n);

            skips.add(skipped);

            return skipped;
        }
    }

    private class OutputStreamWrapper extends OutputStream
    {
        private int
            flushes,
            closes;


        @Override
        public void close() throws IOException
        {
            closes++;
            DebugSocket.this.originoutput.close();
        }

        @Override
        public void flush() throws IOException
        {
            flushes++;
            DebugSocket.this.originoutput.flush();
        }

        @Override
        public void write(int b) throws IOException
        {
            DebugSocket.this.outputdebugbuffer.write(b);
            DebugSocket.this.originoutput.write(b);
            DebugSocket.this.originoutput.flush();
        }

        @Override
        public void write(byte[] b) throws IOException
        {
            DebugSocket.this.outputdebugbuffer.write(b);
            DebugSocket.this.originoutput.write(b);
            DebugSocket.this.originoutput.flush();
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException
        {
            DebugSocket.this.outputdebugbuffer.write(b, off, len);
            DebugSocket.this.originoutput.write(b, off, len);
            DebugSocket.this.originoutput.flush();
        }
    }


    private static final Object
        staticsynch = new Object();

    private static long
        idcounter   = 0;


    private final long
        id;

    private final ByteArrayOutputStream
        inputdebugbuffer,
        outputdebugbuffer;

    private final InputStream
        inputwrapper;

    private final OutputStream
        outputwrapper;

    private InputStream
        origininput;

    private OutputStream
        originoutput;


    public InputStream getInputStream() throws IOException
    {
        if ( origininput == null )
        {
            synchronized ( inputdebugbuffer )
            {
                if ( origininput == null )
                {
                    origininput = super.getInputStream();
                }
            }
        }

        return inputwrapper;
    }

    public OutputStream getOutputStream() throws IOException
    {
        if ( originoutput == null )
        {
            synchronized ( outputdebugbuffer )
            {
                if ( originoutput == null )
                {
                    originoutput    = super.getOutputStream();
                }
            }
        }

        return outputwrapper;
    }


    public DebugSocket()
    {
        id                  = getNextId();
        inputwrapper        = new InputStreamWrapper();
        outputwrapper       = new OutputStreamWrapper();
        inputdebugbuffer    = new ByteArrayOutputStream();
        outputdebugbuffer   = new ByteArrayOutputStream();
    }


    private static long getNextId()
    {
        synchronized ( staticsynch )
        {
            return ++idcounter;
        }
    }
}
import java.io.IOException;
import java.net.ServerSocket;


public class DebugServerSocket extends ServerSocket
{
    public DebugServerSocket() throws IOException
    {
        super();
    }

    public DebugSocket accept() throws IOException
    {
        DebugSocket s = new DebugSocket();

        implAccept(s);

        return s;
    }
}

The class DebugSocket takes notification of each interaction with the InputStream as well as OutputStream DebugSocket类获取与InputStream以及OutputStream的每次交互的通知

Now when the issue occurs i always can see that bytes are missing. 现在,当问题发生时,我总是可以看到字节丢失。

Here an example: 这里是一个例子:

The client send 1758 bytes. 客户端发送1758字节。 I got the 23 top bytes from the member outputdebugbuffer in the DebugSocket . 我从部件23个字节outputdebugbufferDebugSocket

Bytes: 0,0,0,0,0,0,0,2,0,0,6,-46,31,-117,8,0,0,0,0,0,0,0,-83

The server received 227 Bytes. 服务器收到227个字节。 For debug issues i always do read the input stream till i get a -1, so that all bytes got proceeded. 对于调试问题,我始终会读取输入流,直到得到-1,这样所有字节才能进行。 Now the 16 leading bytes on serverside that i got from the member inputdebugbuffer in the DebugSocket . 现在,16个领先的服务器端字节,我从成员获得inputdebugbufferDebugSocket

Bytes: 0,0,0,6,-46,31,-117,8,0,0,0,0,0,0,0,-83

As shown there are 7 bytes missing. 如图所示,缺少7个字节。 the first 8 bytes are a long value this one i changed to a byte value for debugging. 前8个字节是一个长值,我将其更改为字节值以进行调试。 So i figured that the first byte is always correct. 所以我认为第一个字节总是正确的。

If it were a failure in the code no request would be proceeded but as i said before this happens only to 5% of all connections at best. 如果代码失败,那么将不会进行任何请求,但是正如我之前所说,充其量只有全部连接的5%。

Got anyone an idea whats going on here? 有人知道这里发生了什么吗?

I also used the DataInputStream and DataOutputStream to send the data. 我还使用了DataInputStreamDataOutputStream来发送数据。 I always flush after each write operation as you can see in the OutputStreamWrapper of the DebugSocket . 正如您在DebugSocketOutputStreamWrapper中看到的那样,我总是在每次写操作之后DebugSocket

Do i miss something here? 我在这里想念什么吗?

If some other code is required i will try to post it. 如果需要其他一些代码,我会尝试将其发布。

PS The service is multi threaded and processes 100 request parallel. PS服务是多线程的,并行处理100个请求。 Also the clients are multi threaded and do 20 requests parallel. 另外,客户端是多线程的,并并行执行20个请求。 As said each request uses its one connection and closes this one right after the request got proceeded. 如前所述,每个请求使用其一个连接,并在请求进行后立即关闭该连接。

I hope someone got an idea on this matter. 我希望有人对此事有所了解。

Edit: 编辑:

There is no main method to show that does anything like asked in the comments but here the the code blocks of the client and the server that are used. 没有主要方法可以显示执行注释中要求的操作,但是此处显示了所使用的客户端和服务器的代码块。

Client: (Run parallel in 20 threads) 客户端:(在20个线程中并行运行)

    public void sendRequest(long _requesttype, byte[] _bytes)
    {
        Socket              socket  = null;
        DataInputStream     input   = null;
        DataOutputStream    output  = null;
        InputStream         sinput  = null;
        OutputStream        soutput = null;

        try
        {
            socket  = new DebugSocket();

            socket.connect(serveraddress);

            sinput  = socket.getInputStream();
            soutput = socket.getOutputStream();

            input   = new DataInputStream(sinput);
            output  = new DataOutputStream(soutput);

            output.writeLong(_requesttype);

            output.flush();
            soutput.flush();

            output.write(_bytes);

            output.flush();
            soutput.flush();

            // wait for notification byte that service had received all data.
            input.readByte();
        }
        catch (IOException ex)
        {
            LogHelper.log(ex);
        }
        catch (Error err)
        {
            throw err;
        }
        finally
        {
            output.flush();
            soutput.flush();

            input.close();
            output.close();

            finishSocket(socket);
        }
    }

Server: (Run in a thread for each request. Up to 100 threads) 服务器:(针对每个请求在一个线程中运行。最多100个线程)

    public void proceedRequest(DebugSocket _socket)
    {
        DataInputStream     input   = null;
        DataOutputStream    output  = null;
        InputStream         sinput  = null;
        OutputStream        soutput = null;

        try
        {
            sinput  = _socket.getInputStream();
            soutput = _socket.getOutputStream();

            input   = new DataInputStream(sinput);
            output  = new DataOutputStream(soutput);

            RequestHelper.proceed(input.readLong(), input, output);

            // send notification byte to the client.
            output.writeByte(1);

            output.flush();
            soutput.flush();
        }
        catch (IOException ex)
        {
            LogHelper.log(ex);
        }
        catch (Error err)
        {
            throw err;
        }
        finally
        {
            output.flush();
            soutput.flush();

            input.close();
            output.close();
        }
    }

In the server code the readLong() already fails cause of the missing bytes. 在服务器代码中, readLong()已经由于缺少字节而失败。

My guess is there is a bug somewhere else in the code. 我的猜测是代码中其他地方有错误。 I copied the DebugSocket class from the question and created a MCVE (see below). 我从问题中复制了DebugSocket类并创建了MCVE (请参见下文)。 It works fine, I was unable to reproduce the "server cannot read long-value" problem. 它工作正常,我无法重现“服务器无法读取长值”问题。 Try modifying the code below to include more of the your own code until you can reproduce the problem, that should give you an idea where to look for the underlying cause. 尝试修改下面的代码以包含更多自己的代码,直到可以重现该问题为止,这应该使您知道在哪里寻找根本原因。

import java.io.*;
import java.net.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

public class TestDebugSocket implements Runnable, Closeable {

    public static void main(String[] args) {

        TestDebugSocket m = new TestDebugSocket();
        try {
            m.run();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            m.close();
        }
    }

    final int clients = 20;
    final boolean useDebugSocket = true;
    final byte[] someBytes = new byte[1758];
    final ThreadPoolExecutor tp = (ThreadPoolExecutor) Executors.newCachedThreadPool();
    final AtomicLong clientId = new AtomicLong();
    final ConcurrentLinkedQueue<Closeable> closeables = new ConcurrentLinkedQueue<Closeable>();
    final long maxWait = 5_000L;
    final CountDownLatch serversReady = new CountDownLatch(clients);
    final CountDownLatch clientsDone = new CountDownLatch(clients);

    ServerSocket ss;
    int port;

    @Override public void run()  {

        try {
            ss = useDebugSocket ? new DebugServerSocket() : new ServerSocket();
            ss.bind(null);
            port = ss.getLocalPort();
            tp.execute(new SocketAccept());
            for (int i = 0; i < clients; i++) {
                ClientSideSocket css = new ClientSideSocket();
                closeables.add(css);
                tp.execute(css);
            }
            if (!clientsDone.await(maxWait, TimeUnit.MILLISECONDS)) {
                System.out.println("CLIENTS DID NOT FINISH");
            } else {
                System.out.println("Finished");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close();
        }
    } 

    @Override public void close() {

        try { if (ss != null) ss.close(); } catch (Exception ignored) {}
        Closeable c = null;
        while ((c = closeables.poll()) != null) {
            try { c.close(); } catch (Exception ignored) {}
        }
        tp.shutdownNow();
    }

    class DebugServerSocket extends ServerSocket {

        public DebugServerSocket() throws IOException {
            super();
        }

        @Override public DebugSocket accept() throws IOException {

            DebugSocket s = new DebugSocket();
            implAccept(s);
            return s;
        }
    }

    class SocketAccept implements Runnable {

        @Override public void run() {
            try {
                for (int i = 0; i < clients; i++) {
                    SeverSideSocket sss = new SeverSideSocket(ss.accept());
                    closeables.add(sss);
                    tp.execute(sss);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class SeverSideSocket implements Runnable, Closeable {

        Socket s;
        public SeverSideSocket(Socket s) {
            this.s = s;
        }
        @Override public void run() {

            Long l = -1L;
            byte[] received = new byte[someBytes.length];
            try {
                DataInputStream in = new DataInputStream(s.getInputStream());
                DataOutputStream out = new DataOutputStream(s.getOutputStream());
                serversReady.countDown();
                if (!serversReady.await(maxWait, TimeUnit.MILLISECONDS)) {
                    System.out.println("CLIENTS DID NOT CONNECT ON TIME TO SERVER");
                }
                l = in.readLong();
                in.readFully(received);
                out.writeByte(1);
                out.flush();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // write to console at end to prevent synchronized socket I/O
                System.out.println("received long: " + l);
                close();
            }
        }

        @Override public void close() {
            TestDebugSocket.close(s);
            s = null;
        }
    }

    class ClientSideSocket implements Runnable, Closeable {

        Socket s;

        @SuppressWarnings("resource")
        @Override public void run() {

            Long l = -1L;
            Byte b = -1;
            try {
                s = useDebugSocket ? new DebugSocket() : new Socket();
                s.connect(new InetSocketAddress(port));
                DataInputStream in = new DataInputStream(s.getInputStream());
                DataOutputStream out = new DataOutputStream(s.getOutputStream());
                l = clientId.incrementAndGet();
                out.writeLong(l);
                out.write(someBytes);
                out.flush();
                b = in.readByte();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("long send: " + l + ", result: " + b);
                close();
                clientsDone.countDown();
            }
        }

        @Override public void close() {
            TestDebugSocket.close(s);
            s = null;
        }
    }

    static void close(Socket s) {

        try { if (s != null) s.close(); } catch (Exception ignored) {}
    }
}

Ok im done with all possible ways to locate the cause. 好的,我用所有可能的方法来找到原因。 From my experience with socket programming and parallel processing i can say that there is no bug in the code itself. 根据我在套接字编程和并行处理方面的经验,我可以说代码本身没有错误。 Sniffers as well tell me that. 嗅探者也告诉我。 Something on my machine is messing with the transmission. 我机器上的东西弄乱了变速器。

I deactivated all i could think of (firewall/antivir/malware scanner) but no effect. 我停用了所有我能想到的功能(防火墙/ antivir /恶意软件扫描程序),但没有任何效果。

Got someone an idea what else could mess with tcp packages? 有人知道tcp软件包还会引起什么混乱?

Edit: 编辑:

Ok i got it. 好,我知道了。 AVG 2014 is messing. AVG 2014混乱不堪。 Jetzt deactivating the components did not work. Jetzt停用组件无效。 In Options->Settings there is a menu point were you can deactivate the AVG-Protection. 在“选项”->“设置”中,有一个菜单点可以禁用AVG-Protection。

Got someone knowledge on this topic? 有人对此主题有知识吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM