如果服务器在写入 OutputStream 时关闭,SSLSocket 客户端不会抛出异常
SSLSocket client doesn't throw exception if server is shutdown when writing to OutputStream
编辑:
我更改了代码,以便在传输后检查文件的完整性。
但是现在当我在 dos.write(buffer, 0, count)
处的客户端中放置一个断点,杀死服务器,然后恢复客户端代码执行时,它无限期地挂在 serverMD5[i] = dataInputStream.readByte()
。
即使用户现在知道传输不成功(应用程序挂起并需要重新启动),这又一次没有按照我的预期进行(抛出 IOException)。
原始 post 更改代码:
我创建了一个 android 客户端,它使用 SSLSocket 连接到服务器并发送一些数据。
这是相关的客户端和服务器代码
客户:
try {
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket();
sslsocket.connect(new InetSocketAddress(SERVER_IP, UPLOAD_PORT), 2000);
OutputStream outputStream = sslsocket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeInt(DEVICE_ID);
dataOutputStream.writeLong(FILE_LENGTH);
MessageDigest md = MessageDigest.getInstance("MD5");
DigestOutputStream dos = new DigestOutputStream(outputStream, md);
InputStream readingsInputStream = new FileInputStream(FILE_NAME);
int count;
byte[] buffer = new byte[10 * 1024];
while ((count = readingsInputStream.read(buffer)) > 0) {
dos.write(buffer, 0, count);
}
readingsInputStream.close();
byte[] md5 = md.digest();
byte[] serverMD5 = new byte[16];
DataInputStream dataInputStream = new DataInputStream(sslsocket.getInputStream());
for (int i = 0;i<16;i++) {
serverMD5[i] = dataInputStream.readByte();
if (md5[i] != serverMD5[i]) throw new Exception("MD5 mismatch");
}
sslsocket.close();
} catch (Exception e) {
...
}
服务器:
try {
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
InputStream inputStream = sslSocket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
int deviceID = dataInputStream.readInt();
long fileLength = dataInputStream.readLong();
MessageDigest md = MessageDigest.getInstance("MD5");
DigestInputStream dis = new DigestInputStream(inputStream, md);
OutputStream readingsOutputStream = new FileOutputStream("Device"+deviceID+".txt", false);
int count;
byte[] buffer = new byte[1];
do {
count = dis.read(buffer);
readingsOutputStream.write(buffer, 0, count);
fileLength -= count;
} while (fileLength > 0);
readingsOutputStream.close();
byte[] md5 = md.digest();
DataOutputStream md5OutputStream = new DataOutputStream(sslSocket.getOutputStream());
for (int i = 0;i<16;i++) md5OutputStream.writeByte(md5[i]);
sslSocket.close();
} catch (Exception e) {
...
}
通常这一切都按预期工作,但是当我在客户端的第 dos.write(buffer, 0, count)
行放置断点然后在到达断点时终止服务器时出现问题。
在客户端继续执行代码后,它没有抛出异常,只是遍历了这个代码块的其余部分,让我相信文件已成功写入服务器。
当然情况并非如此,因为在写入 OutputStream 之前服务器已关闭。这会导致服务器上出现一个空的 DeviceX.txt
(X 是设备编号)文件。
这是一个大问题,因为用户可能认为数据已成功传输并从设备中删除它(由于应用程序的性质,发送的数据在某个时间点被删除)。
由于我设法产生了这个错误,我认为它也有可能在现实世界中发生。这是我第一次使用套接字,我不知道如何解决这个问题。
此外,如果有人注意到此代码块可能出现的任何其他问题(结果不符合预期但未抛出异常的另一种情况),请告诉我。
看起来您的客户端和服务器之间需要双向通信。当客户端完成上传后,您可以让客户端发送传输结束 (ascii 0x4) 字符(或您的 client/server 认为是 "special" 序列的任何其他字符)。当服务器收到转换结束字符时,服务器可以用它收到的字节数进行回复。在客户端,等待响应,如果达到超时,则告诉用户出了点问题。
这是TCP的正常操作。抛开 SSL,您的发送被缓冲在套接字发送缓冲区中,并在 send()
函数返回后异步传输。因此 send()
函数不可能立即检测到对等中断。如果继续发送,TCP 对未决数据的重试最终将失败并导致 后续 发送失败,在 Java 和 IOException: connection reset
的情况下。
inputStream.read(deviceIDbuffer);
您不能假设 read()
会填满缓冲区。你应该在这里使用DataInputStream.readInt()
。
编辑:
我更改了代码,以便在传输后检查文件的完整性。
但是现在当我在 dos.write(buffer, 0, count)
处的客户端中放置一个断点,杀死服务器,然后恢复客户端代码执行时,它无限期地挂在 serverMD5[i] = dataInputStream.readByte()
。
即使用户现在知道传输不成功(应用程序挂起并需要重新启动),这又一次没有按照我的预期进行(抛出 IOException)。
原始 post 更改代码:
我创建了一个 android 客户端,它使用 SSLSocket 连接到服务器并发送一些数据。 这是相关的客户端和服务器代码
客户:
try {
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket();
sslsocket.connect(new InetSocketAddress(SERVER_IP, UPLOAD_PORT), 2000);
OutputStream outputStream = sslsocket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeInt(DEVICE_ID);
dataOutputStream.writeLong(FILE_LENGTH);
MessageDigest md = MessageDigest.getInstance("MD5");
DigestOutputStream dos = new DigestOutputStream(outputStream, md);
InputStream readingsInputStream = new FileInputStream(FILE_NAME);
int count;
byte[] buffer = new byte[10 * 1024];
while ((count = readingsInputStream.read(buffer)) > 0) {
dos.write(buffer, 0, count);
}
readingsInputStream.close();
byte[] md5 = md.digest();
byte[] serverMD5 = new byte[16];
DataInputStream dataInputStream = new DataInputStream(sslsocket.getInputStream());
for (int i = 0;i<16;i++) {
serverMD5[i] = dataInputStream.readByte();
if (md5[i] != serverMD5[i]) throw new Exception("MD5 mismatch");
}
sslsocket.close();
} catch (Exception e) {
...
}
服务器:
try {
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
InputStream inputStream = sslSocket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
int deviceID = dataInputStream.readInt();
long fileLength = dataInputStream.readLong();
MessageDigest md = MessageDigest.getInstance("MD5");
DigestInputStream dis = new DigestInputStream(inputStream, md);
OutputStream readingsOutputStream = new FileOutputStream("Device"+deviceID+".txt", false);
int count;
byte[] buffer = new byte[1];
do {
count = dis.read(buffer);
readingsOutputStream.write(buffer, 0, count);
fileLength -= count;
} while (fileLength > 0);
readingsOutputStream.close();
byte[] md5 = md.digest();
DataOutputStream md5OutputStream = new DataOutputStream(sslSocket.getOutputStream());
for (int i = 0;i<16;i++) md5OutputStream.writeByte(md5[i]);
sslSocket.close();
} catch (Exception e) {
...
}
通常这一切都按预期工作,但是当我在客户端的第 dos.write(buffer, 0, count)
行放置断点然后在到达断点时终止服务器时出现问题。
在客户端继续执行代码后,它没有抛出异常,只是遍历了这个代码块的其余部分,让我相信文件已成功写入服务器。
当然情况并非如此,因为在写入 OutputStream 之前服务器已关闭。这会导致服务器上出现一个空的 DeviceX.txt
(X 是设备编号)文件。
这是一个大问题,因为用户可能认为数据已成功传输并从设备中删除它(由于应用程序的性质,发送的数据在某个时间点被删除)。
由于我设法产生了这个错误,我认为它也有可能在现实世界中发生。这是我第一次使用套接字,我不知道如何解决这个问题。
此外,如果有人注意到此代码块可能出现的任何其他问题(结果不符合预期但未抛出异常的另一种情况),请告诉我。
看起来您的客户端和服务器之间需要双向通信。当客户端完成上传后,您可以让客户端发送传输结束 (ascii 0x4) 字符(或您的 client/server 认为是 "special" 序列的任何其他字符)。当服务器收到转换结束字符时,服务器可以用它收到的字节数进行回复。在客户端,等待响应,如果达到超时,则告诉用户出了点问题。
这是TCP的正常操作。抛开 SSL,您的发送被缓冲在套接字发送缓冲区中,并在 send()
函数返回后异步传输。因此 send()
函数不可能立即检测到对等中断。如果继续发送,TCP 对未决数据的重试最终将失败并导致 后续 发送失败,在 Java 和 IOException: connection reset
的情况下。
inputStream.read(deviceIDbuffer);
您不能假设 read()
会填满缓冲区。你应该在这里使用DataInputStream.readInt()
。