我以字節數組格式發送多個命令,並根據在典型的客戶機/服務器通信中發送的命令接收響應。我的Java應用程序是在Windows 7上運行的客戶端,我知道要發送什麼命令以及我希望在響應中收到哪些命令。但是,我沒有任何控制權或對服務器源代碼有任何瞭解。通過Java寫入時首字節丟失SSL Socket
我遇到的問題是在發送的第二個命令或之後的任何命令之後,數組的第一個字節被丟棄。當我發送第一個命令時,我得到了正確的響應,因爲第一個字節沒有被丟棄。當發送下一個命令或之後的任何命令時,第一個字節被丟棄,服務器不響應,因爲該命令的格式不正確,服務器無法識別。
我通過Java SSLSocket DataOutputStream發送這些命令,當然我正在接收DataInputStream上的響應。我首先與服務器握手並在握手成功後繼續。在這一點上是當我發送的第一個命令和接收十六進制:
Sending: 01 03 03
Receive: 01 0e fd 85 02 09 01 01 04 01 06
正在發送的下一個命令這裏顯示的響應:
Sending: 01 48 65 6c 6c 6f
但是,這是我沒有收到迴應從服務器。
打印出javax.net.debug輸出時,我可以看到第一個字節'01'確實丟失或丟失了。
Padded plaintext before ENCRYPTION: len = 32
0000: 48 65 6C 6C 6F FE 57 F9 4A 29 13 8F 2B AB 71 A3 Hello.W.J)..+.q.
0010: 16 12 29 FF D5 DE 12 48 8B 06 06 06 06 06 06 06 ..)....H........
main, WRITE: TLSv1 Application Data, length = 32
[Raw write]: length = 37
0000: 17 03 01 00 20 34 42 ED 88 FC 41 2D 13 1A FD BA .... 4B...A-....
0010: 64 0E 9D C7 FE 11 76 96 48 09 A6 BC B2 BC 0E FA d.....v.H.......
0020: C8 5B 79 4B 82 .[yK.
下面是我的源代碼:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.TrustManager;
import java.security.KeyStore;
public class SSLSocketTest
{
private SSLSocket sslSocket = null;
private SSLSocketFactory sslSocketFactory = null;
private String ipAddress = "192.168.100.99";
private int port = 9999;
DataOutputStream dataOS = null;
DataInputStream dataIS = null;
private boolean handshakeSuccessful = false;
public static void main(String[] args)
{
SSLSocketTest sslSocketTest = new SSLSocketTest();
sslSocketTest.sslSocketConnect();
}
SSLSocketTest()
{
System.setProperty("javax.net.debug", "all");
try{
File certFile = new File("cacerts");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
char[] certPassword = "changeit".toCharArray();
InputStream fileIS = new FileInputStream(certFile);
keyStore.load(fileIS, certPassword);
fileIS.close();
SSLContext sslContext = SSLContext.getInstance("TLSv1");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
X509TrustManager defaultTrustManager = (X509TrustManager)trustManagerFactory.getTrustManagers()[0];
sslContext.init(null, new TrustManager[] {defaultTrustManager}, null);
sslSocketFactory = sslContext.getSocketFactory();
}catch(Exception e){
e.printStackTrace();
}
}
public void sslSocketConnect()
{
try{
sslSocket = (SSLSocket) sslSocketFactory.createSocket(ipAddress, port);
dataOS = new DataOutputStream(sslSocket.getOutputStream());
dataIS = new DataInputStream(sslSocket.getInputStream());
sslSocket.setSoTimeout(15000);
//Handshake
sslSocket.addHandshakeCompletedListener(new MyHandshakeListener());
sslSocket.startHandshake();
while(!handshakeSuccessful)
{
Thread.sleep(100);
}
//Sending commands
byte[] firstCommand = new byte[]{(byte)0x01, (byte)0x03, (byte)0x03};
String[] firstCommandResponse = processCommand(firstCommand);
byte[] secondCommand = new byte[]{(byte)0x01, (byte)0x48, (byte)0x65, (byte)0x6C, (byte)0x6C, (byte)0x6F};
String[] secondCommandResponse = processCommand(secondCommand);
disconnect();
}catch(Exception e){
e.printStackTrace();
}
}
public void disconnect()
{
try{
byte[] endConnection = new byte[]{(byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03};
processCommand(endConnection);
dataOS.close();
dataIS.close();
sslSocket.close();
}catch (Exception e){
e.printStackTrace();
}
}
public String[] processCommand(byte[] command)
{
String[] returnResponse = null;
byte[] commandResponse = new byte[120];
byte[] trimCommandResponse;
try{
int commandResponseLength = -1;
int errorCount = 0;
while(commandResponseLength == -1)
{
StringBuilder cmdStr = new StringBuilder();
cmdStr.append("Sending: ");
for(int i=0; i<command.length; i++)
{
cmdStr.append(fixHexStringData(Integer.toHexString(command[i])) + " ");
}
System.out.println(cmdStr.toString());
dataOS.write(command, 0, command.length);
dataOS.flush();
commandResponseLength = dataIS.read(commandResponse);
errorCount++;
if(errorCount == 3)
{
throw new Exception();
}
}
returnResponse = new String[commandResponseLength];
trimCommandResponse = new byte[commandResponseLength];
//Getting Reponse Data
for(int i=0; i<commandResponseLength; i++)
{
returnResponse[i] = fixHexStringData(Integer.toHexString(commandResponse[i]));
trimCommandResponse[i] = commandResponse[i];
}
StringBuilder rcvStr = new StringBuilder();
rcvStr.append("Receive: ");
for(int i=0; i<returnResponse.length; i++)
{
rcvStr.append(returnResponse[i] + " ");
}
System.out.println(rcvStr.toString());
}catch(Exception e){
e.printStackTrace();
}
return returnResponse;
}
private String fixHexStringData(String dataByte)
{
if(dataByte.length() < 2)
{
dataByte = "0" + dataByte;
}
else if(dataByte.length() > 2)
{
dataByte = dataByte.substring(dataByte.length()-2);
}
return dataByte;
}
class MyHandshakeListener implements HandshakeCompletedListener
{
public void handshakeCompleted(HandshakeCompletedEvent e)
{
System.out.println("Handshake succesful!");
handshakeSuccessful = true;
}
}
}
我已經是問題:
- 我缺少在寫出字節數組的一個步驟?我已經通過一個標準的Java Socket完成了這個任務,沒有問題,所以通過一個不同於標準Socket的SSL Socket來寫?我尋找這個,但沒有看到任何不同。
- 這可能是一個證書問題?如果握手和第一個命令成功,這是否意味着此時的通信已經建立並且超出了證書?
- 服務器能否影響到這一點?如果是這樣,這背後的原因是什麼?如果將字節數組寫入DataOutputStream位於客戶端,並且第一個字節被丟棄,那麼服務器如何在客戶端產生影響?
- 這可能是一個JVM的錯誤?
你不需要任何的是握手代碼重寫此功能。握手在第一個I/O上自動啓動,如果您第一次同步啓動它,則不需要等待循環。除非你在監聽器中做了一些比設置布爾值更有趣的事情,否則全部刪除它。 – EJP
謝謝。這只是一個示例代碼來找出字節丟失的原因。這種情況下的握手暫時用於在發送任何命令之前確認通信已建立。在此示例之外,意圖是永遠不會使用握手。 – TheBernman