微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从Android发送文件时从WCF服务重置连接

如何解决从Android发送文件时从WCF服务重置连接

我正在解决连接重置问题,希望有人可以帮助我进行诊断。我们有一个WCF服务,该服务具有接收文件sqlite数据库)并将其保存到服务器的功能。定期,客户端在上传文件时会收到连接重置异常。

我们从未能够在开发中重现此问题。它仅在生产中发生过,即使在那时也不一致。这将在客户一天发生,但第二天不会发生。我们已经从客户端上手动取下了相同的文件,并将其移至开发中的文件中,以调试将其发送到同一生产服务器,只是使其成功而没有问题。它将在某些生产服务器上发生,但在其他生产服务器上不会发生-再次不一致。

文件相对较小,通常在500kb至10mb之间。

这是服务功能

[OperationContract]
[WebInvoke(Method = "POST",UriTemplate = "SendData",ResponseFormat = Webmessageformat.Json,RequestFormat = Webmessageformat.Json)]
public GenericObject SendData(Stream data)
{
    GenericObject go = new GenericObject();
    
    try
    {
        string fileName = "SomePathToFile.db";

        FileStream fs = new FileStream(fileName,FileMode.Create);

        int offset = 0;
        byte[] buffer = new byte[16*1024];
        
        while ((offset = databaseStream.Read(buffer,buffer.Length)) != 0)
            fs.Write(buffer,offset);
        
        fs.Close();
        
        go.Item = "Send successful";
    }
    catch (Exception ex)
    {
        go.Error = "Error sending data\n\n" + ex.Message;
        
        //Do some error logging
        ...
    }
    
    return go;
}

它只是获取文件流并将其本地保存到服务器。如果出现问题,它将返回一个通用的JSON对象,该对象包含成功消息或错误消息。注意:客户端不会取回对象,并且服务器上也不会记录任何异常。

这是Android端发送文件功能(我们添加了一些重试逻辑以尝试减少错误):

public void sendData(String serviceURL,int attemptNumber)
{

    //Declare all streams/disposable objects so that we can reset them if an error occurs
    FileInputStream fs = null;
    DataOutputStream os = null;
    InputStreamReader isr = null;
    BufferedReader in = null;
    HttpURLConnection conn = null;

    boolean connectionResetError = false;

    File tempFile = null;

    try
    {
        //Do some stuff to write the db file to the cache directory for temporary storage
        tempFile = new File(getCacheDir(),"SomeFile.db");

        ...
        

        //Open the cache copy of the file for reading
        fs = new FileInputStream(tempFile);

        //Establish URL to the service
        URL url = new URL(serviceURL);

        // Open an HTTP  connection to the URL
        conn = (HttpURLConnection) url.openConnection();
        conn.setDoInput(true);      // Allow Inputs
        conn.setDoOutput(true);     // Allow Outputs
        conn.setUseCaches(false);   // Don't use a Cached copy
        conn.setChunkedStreamingMode(1024);
        conn.setRequestMethod("POST");
        
        //Tried both of these options based on other Stack Overflow suggestions
        //conn.setRequestProperty("Connection","Keep-Alive");
        conn.setRequestProperty("Connection","close"); 

        //Open a stream on the connection to write the file
        os = new DataOutputStream(conn.getoutputStream());

        int bytesRead,bytesAvailable,bufferSize;
        byte[] buffer;
        int maxBufferSize = 1 * 1024;

        //Determine file size and if a buffer is needed
        bytesAvailable = fs.available();
        bufferSize = Math.min(bytesAvailable,maxBufferSize);
        buffer = new byte[bufferSize];

        // Read from the file into the buffer
        bytesRead = fs.read(buffer,bufferSize);

        //Continue reading while there is still more to be read from the file
        while (bytesRead > 0)
        {
            //write the buffer to the connection
            os.write(buffer,bufferSize);

            //See if there is more to be read from the file and then read
            bytesAvailable = fs.available();
            bufferSize = Math.min(bytesAvailable,maxBufferSize);
            bytesRead = fs.read(buffer,bufferSize);
        }

        //Get the response and message from the connection
        int serverResponseCode = conn.getResponseCode();
        String serverResponseMessage = conn.getResponseMessage();

        //Flush the output stream
        os.flush();

        if (serverResponseCode == HttpURLConnection.HTTP_OK)
        {
            //If response is "OK",read the data that was sent

            //Open a reader for the response and buffer it
            isr = new InputStreamReader(conn.getInputStream());
            in = new BufferedReader(isr);

            //String to hold the response
            String responseString;
            StringBuilder sb = new StringBuilder();

            //Read the response
            while ((responseString = in.readLine()) != null)
                sb.append(responseString);

            //Return the response that was read
            jResponse = new JSONObject(sb.toString());
        }
        else
        {
            //If any other response is received,return the message
            response.putString("Error",serverResponseMessage);
        }

    }
    catch(Exception ex)
    {
        //An error occurred

        //Check to see if it's the connection reset error and if we've tried less than 3 times
        if(ex instanceof java.net.socketException && ex.getMessage().equals("Connection reset") && attemptNumber < 3)
        {
            //If so,make a note so we can try again
            connectionResetError = true;
        }
        else
        {
            //If not,just pass along the error
            response.putString("Error",ex.getMessage());
        }
    }
    finally
    {
        //dispose of everything before we quit or try again

        try { if (fs != null) fs.close(); } catch(IOException e){}
        try { if (in != null) in.close(); } catch(IOException e){}
        try { if (isr != null) isr.close(); } catch(IOException e){}
        try { if (os != null) os.close(); } catch(IOException e){}
        try { if (conn != null) conn.disconnect(); } catch(Exception e){}
        
        if(tempFile != null && tempFile.exists())
            tempFile.delete();
    }

    //If we're going to try again,do so and increment the attempt number
    if(connectionResetError)
    {
        sendData(serviceURL,attemptNumber+1);
    }
}

我们已经在其中一台服务器上设置了Wireshark,以进行调试,但是我对它并不足够了解,以使其有意义。我所知道的是,我看到了请求,正在传输一些数据,然后进行了重置。

Beginning of transfer

End of transfer

您可能有的任何建议或调试指南都将非常有帮助。

解决方法

异常的连接重置通常仅表示该连接已关闭。这可能是由网络问题引起的。建议您在配置文件中设置超时以避免此问题:

<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding openTimeout="00:10:00"
                 closeTimeout="00:10:00"
                 sendTimeout="00:10:00"
                 receiveTimeout="00:10:00">
        </binding>
      </wsHttpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

您可以在客户端启用WCF跟踪,以从错误客户端收集信息。 有关WCF跟踪的详细信息,请参阅本文:

https://docs.microsoft.com/en-us/dotnet/framework/wcf/diagnostics/tracing/configuring-tracing?redirectedfrom=MSDN

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。