与Java服务器通信断开的管道错误&多线程环境中的C#客户端

时间:2015-09-20 04:47:51

标签: java multithreading network-programming

我希望能找到有关我烦人问题的任何帮助。

我有一个带有c#

的java和客户端程序的TCP服务器程序 这两者之间的分组协议简单地由4字节长度组成。身体ASCII数据。

问题是C#客户端面临FormatException,它来自解析在长度字节上失败。如果我从客户端查看错误,那么客户端正在尝试解析正文中不是长度标题的某个地方。 但显然,Server不会发送损坏的数据包。

同时,在服务器上,每当发生这种问题时,我都会发现一个Broken pipe错误。

不幸的是,此错误并非总是发生,并且无法重新创建问题情况。这让我很难找到这个问题的确切原因

请参阅下面的服务器端代码

public class SimplifiedServer {

private Map<InetAddress, DataOutputStream> outMap;
private Map<InetAddress,DataInputStream> inMap;

protected void onAcceptNewClient(Socket client) {
    DataOutputStream out = null;
    DataInputStream in = null;
    try {
        out = new DataOutputStream(client.getOutputStream());
        in = new DataInputStream(client.getInputStream());
    } catch (IOException e) {
        e.printStackTrace();
    }
    outMap.put(client.getInetAddress(), out);
    inMap.put(client.getInetAddress(), in);

}

    public void writeToAll(String packet) {
    outMap.forEach((key, out) -> {

        try {
            byte[] body = packet.getBytes("UTF-8");
            int len = body.length;
            if (len > 9999) {
                throw new IllegalArgumentException("packet length is longer than 10000, this try will be neglected");
            }

            String lenStr = String.format("%04d%s", len, packet);
            byte[] obuf = lenStr.getBytes();

            synchronized (out) {
                out.write(obuf);
                out.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    });

}


public void listenClient(Socket client) {
    try {
        DataOutputStream out = outMap.get(client.getInetAddress());
        DataInputStream in = inMap.get(client.getInetAddress());

        while (true) {

            byte[] received = SimplePacketHandler.receiveLpControlerData(in);

            byte[] lenBytes = new byte[4];

            for( int i = 0 ; i < 4 ; i ++){
                lenBytes[i] = in.readByte();
            }

            String lenString = new String(lenBytes);
            int length = Integer.parseInt(lenString);
            byte[] data = new byte[length];

            for ( int i = 0 ; i < length ; i ++){
                data[i] = in.readByte();
            }

            if ( data == null ){
                System.out.println("NetWork error, closing socket :" + client.getInetAddress());
                in.close();
                out.close();
                outMap.remove(client.getInetAddress());
                inMap.remove(client.getInetAddress());
                return;
            }

            doSomethingWithData(out, data);

        }

    } catch (NumberFormatException e) {
        e.printStackTrace();
    } catch ( Exception e ) {
        e.printStackTrace();
    } finally {
        try {
            System.out.println(client.getRemoteSocketAddress().toString() + " closing !!! ");
            // remove stream handler from map
            outMap.remove(client.getInetAddress());
            inMap.remove(client.getInetAddress());

            //close socket.
            client.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

}

这是客户端代码

 public class ClientSide
{
    public TcpClient client;


    public String ip;
    public int port;
    public NetworkStream ns;
    public BinaryWriter writer;
    public BinaryReader reader;

    public Boolean isConnected = false;
    public System.Timers.Timer t;
    public String lastPacketSucceeded = String.Empty;


    public ClientSide(String ip, int port)
    {
        this.ip = ip;
        this.port = port;
        client = new TcpClient();

    }

    public bool connect()
    {
        try
        {
            client.Connect(ip, port);
        }
        catch (SocketException e)
        {
            Console.WriteLine(e.ToString());
            return false;
        }


        Console.WriteLine("Connection Established");

        reader = new BinaryReader(client.GetStream());
        writer = new BinaryWriter(client.GetStream());
        isConnected = true;


        return true;
    }

    public void startListen()
    {
        Thread t = new Thread(new ThreadStart(listen));
        t.Start();
    }

    public void listen()
    {

        byte[] buffer = new byte[4];
        while (true)
        {               

            try
            {
                reader.Read(buffer, 0, 4);
                String len = Encoding.UTF8.GetString(buffer);
                int length = Int32.Parse(len);
                byte[] bodyBuf = new byte[length];
                reader.Read(bodyBuf, 0, length);

                String body = Encoding.UTF8.GetString(bodyBuf);
                doSomethingWithBody(body);
            }
            catch (FormatException e)
            {
                 Console.WriteLine(e.Message);

            }

        }

    }

    public void writeToServer(String bodyStr)
    {
        byte[] body = Encoding.UTF8.GetBytes(bodyStr);
        int len = body.Length;
        if (len > 10000)
        {
            Console.WriteLine("Send Abort:" + bodyStr);
        }
        len = len + 10000;
        String lenStr = Convert.ToString(len);

        lenStr = lenStr.Substring(1);

        byte[] lengthHeader = Encoding.UTF8.GetBytes(lenStr);


        String fullPacket = lenStr + bodyStr;
        byte[] full = Encoding.UTF8.GetBytes(fullPacket);

        try
        {
            writer.Write(full);
        }
        catch (Exception)
        {
            reader.Close();
            writer.Close();
            client.Close();

            reader = null;
            writer = null;
            client = null;
            Console.WriteLine("Send Fail" + fullPacket);

        }
        Console.WriteLine("Send complete " + fullPacket);

    }
}

考虑到重建问题是不可能的,我猜这个问题来自多线程问题。但我找不到任何进一步的线索来解决这个问题。

如果你们需要更多信息来解决这个问题,请告诉我。

任何帮助都将非常感谢,提前感谢。

2 个答案:

答案 0 :(得分:1)

关闭另一侧的连接会导致管道损坏。很可能C#客户端有一个错误,导致格式异常导致它关闭连接,从而导致服务器端的管道损坏。请参阅what is the meaning of Broken pipe Exception?

检查此读取的返回值:

            byte[] bodyBuf = new byte[length];
            reader.Read(bodyBuf, 0, length);

根据Microsoft文档BinaryReader.Read https://msdn.microsoft.com/en-us/library/ms143295%28v=vs.110%29.aspx

  

[返回值为]读入缓冲区的字节数。如果许多字节不可用,这可能小于请求的字节数,如果到达流的末尾,则可能为零。

如果读取的长度小于字节长度,则下次使用最后一条消息中间某处的数据解析长度。

答案 1 :(得分:0)

当客户端(浏览器)关闭连接时,会发生这些中断管道异常,但服务器(您的标记)继续尝试写入流。

当有人在浏览器中单击“后退”,“停止”等,并且在请求完成之前它与服务器断开连接时,通常会发生这种情况。有时,它可能会发生,例如,Content-Length标头不正确(并且浏览器将其值视为true)。

通常,这是一个非事件,没什么好担心的。但是如果你在开发环境中看到它们,当你知道你没有中断浏览器时,你可能会多挖一点以找出原因。

WLS服务器将尝试从Web容器中过滤掉日志中的这些异常,因为它是由客户端(浏览器)操作引起的,我们无法对其执行任何操作。但服务器并没有抓住所有这些。

参考:: https://community.oracle.com/thread/806884