簡體   English   中英

Java Server SSL套接字和Python Client SSL套接字-服務器發送消息時出現問題

[英]Java Server SSL Socket and Python Client SSL socket - problems when server send messages

我正在嘗試傳達服務器SSL套接字Java和客戶端SSL套接字Python。 發送的第一條消息可以,但是當服務器發送另一條消息時,客戶端將接收到的消息分為兩部分。 例如:如果服務器發送消息“ abcdefghij”,則客戶端將收到第一個“ a”,然后是“ bcdefghij”。

有人知道為什么在第一次收到消息后會分為兩個部分嗎? 問候。

客戶端代碼:

import socket, ssl, pprint
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ssl_sock = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1, cert_reqs=ssl.CERT_NONE)
ssl_sock.connect(('localhost', 7000))
pprint.pprint(ssl_sock.getpeercert())

while(1):
    print "Waiting"
    data = ssl_sock.recv()  
    print "Received:", data
    data = ""

ssl_sock.close()

服務器代碼:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.security.KeyStore;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;


public class SslReverseEchoer {

    public static void main(String[] args) {
        char ksPass[] = "123456".toCharArray();
        char ctPass[] = "123456".toCharArray();

        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("keystore.jks"), ksPass);
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, ctPass);
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(kmf.getKeyManagers(), null, null);
            SSLServerSocketFactory ssf = sc.getServerSocketFactory();
            SSLServerSocket s = (SSLServerSocket) ssf.createServerSocket(7000);
            printServerSocketInfo(s);
            SSLSocket c = (SSLSocket) s.accept();
            printSocketInfo(c);
            BufferedWriter w = new BufferedWriter(new OutputStreamWriter(c.getOutputStream()));
            BufferedReader r = new BufferedReader(new InputStreamReader(c.getInputStream()));
            //1th time
            String m = "abcdefghj1234567890";
            w.write(m, 0, m.length());
            w.newLine();
            w.flush();
            //2th time
            String m2 = "#abcdefghj1234567890";
            w.write(m2, 0, m2.length());
            w.newLine();
            w.flush();
            //3th time
            String m3 = "?abcdefghj1234567890";
            w.write(m3, 0, m3.length());
            w.newLine();
            w.flush();
            while ((m = r.readLine()) != null) {
                if (m.equals("."))
                    break;
                char[] a = m.toCharArray();
                int n = a.length;
                for (int i = 0; i < n / 2; i++) {
                    char t = a[i];
                    a[i] = a[n - 1 - i];
                    a[n - i - 1] = t;
                }
                w.write(a, 0, n);
                w.newLine();
                w.flush();
            }
            w.close();
            r.close();
            c.close();
            s.close();
        } catch (Exception e) {
            System.err.println(e.toString());
        }
    }

    private static void printSocketInfo(SSLSocket s) {
        System.out.println("Socket class: " + s.getClass());
        System.out.println("   Remote address = " + s.getInetAddress().toString());
        System.out.println("   Remote port = " + s.getPort());
        System.out.println("   Local socket address = " + s.getLocalSocketAddress().toString());
        System.out.println("   Local address = " + s.getLocalAddress().toString());
        System.out.println("   Local port = " + s.getLocalPort());
        System.out.println("   Need client authentication = " + s.getNeedClientAuth());
        SSLSession ss = s.getSession();
        System.out.println("   Cipher suite = " + ss.getCipherSuite());
        System.out.println("   Protocol = " + ss.getProtocol());
    }

    private static void printServerSocketInfo(SSLServerSocket s) {
        System.out.println("Server socket class: " + s.getClass());
        System.out.println("   Socker address = " + s.getInetAddress().toString());
        System.out.println("   Socker port = " + s.getLocalPort());
        System.out.println("   Need client authentication = " + s.getNeedClientAuth());
        System.out.println("   Want client authentication = " + s.getWantClientAuth());
        System.out.println("   Use client mode = " + s.getUseClientMode());
    }
}

看來您希望總是能夠從另一側讀取在一個塊中發送的任何數量的數據。

這是一個常見錯誤,不僅針對SSL / TLS,而且與純TCP通信有關。

您應該始終循環閱讀您打算閱讀的內容。 您還應該定義協議(或使用現有協議)以考慮命令和請求/響應終止符。

例如,HTTP使用空白行來告知標頭的末尾,並使用Content-Length標頭或分塊傳輸編碼來告知接收方何時停止讀取正文。

SMTP使用行分隔命令和單個. 在郵件末尾。

它是服務器端Naggle算法與客戶端TCP堆棧中的延遲ACK的組合。 您還會發現兩個數據包之間有40ms的延遲。

在服務器端禁用Naggle算法以補救:

SSLSocket c = (SSLSocket) s.accept();
c.setTcpNoDelay(true);

有關發生這種情況的原因的更多信息,請訪問: http : //www.stuartcheshire.org/papers/NagleDelayedAck/

編輯添加:注意下面的布魯諾答案。 雖然這描述了您在此處看到的具體原因,但不能保證期望服務器提供數據的方式。

我試圖通過兩種方式解決問題,消息總是在第一個字符處中斷,因此我決定在每個消息的開頭和結尾添加三個空格。 因此,當客戶收到消息時,請對消息進行修剪。 我發現的另一種方法是使用DataOutputStream,該方法逐字節傳遞writeBytes,在服務器上使用它,並且客戶端在接收數據時進行q更改,消息必須構建客戶端處理才能最終完成我想容納的內容消息的結尾。 感謝您的討論!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM