1

サーバー SSL ソケット Java とクライアント SSL ソケット Python を通信しようとしています。最初に送信されたメッセージは問題ありませんが、サーバーが別のメッセージを送信すると、クライアントはメッセージを 2 つの部分に分割して受信します。例: サーバーがメッセージ "abcdefghij" を送信すると、クライアントは最初に "a" を受信し、その後に "bcdefghij" を受信します。

最初にメッセージが 2 つの部分に分けて受信された後、なぜ誰かが知っていますか? よろしく。

クライアントコード:

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());
    }
}
4

3 に答える 3

2

あるブロックで送信したデータの量に関係なく、反対側から常に読み取ることができると期待しているようです。

これはよくある間違いで、SSL/TLS に固有のものではありませんが、プレーンな TCP 通信にも関連しています。

読みたいものは常にループして読む必要があります。また、コマンドと要求/応答ターミネータを考慮して、プロトコルを定義する (または既存のものを使用する) 必要があります。

たとえば、HTTP は空行を使用してヘッダーの終わりを伝え、Content-Lengthヘッダーまたはチャンク転送エンコーディングを使用して、本文の読み取りをいつ停止するかを受信者に伝えます。

.SMTP は、行区切りのコマンドと、メッセージの最後に単一のコマンドを使用します。

于 2012-08-03T00:19:20.600 に答える
1

これは、サーバー側の Naggle アルゴリズムとクライアントの TCP スタックの遅延 ACK の組み合わせです。また、2 つのパケット間に最大 40 ミリ秒の遅延が見られます。

サーバー側で Naggle アルゴを無効にして解決します。

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

これが発生する理由の詳細については、http: //www.stuartcheshire.org/papers/NagleDelayedAck/を参照してください。

追加する編集:以下のBrunoの回答に注意してください。これは、ここに表示されている特定の理由を説明していますが、サーバーからのデータが期待される方法は保証されていません。

于 2012-08-03T00:07:33.800 に答える
0

私は 2 つの方法で問題を解決しようとしました。メッセージは常に最初の文字で切れるので、各メッセージの先頭と末尾に 3 つのスペースを追加することにしました。したがって、クライアントがそれらを受信したら、メッセージをトリムします。私が見つけた別の方法は、DataOutputStreamを使用することでした。これは、writeBytesをバイトごとに配信するメソッドであり、サーバーで使用され、クライアントはデータを受信すると変更され、メッセージはクライアント側の処理を構築して、最終的に収容したいことを実行する必要がありましたメッセージの終わり。議論していただきありがとうございます!

于 2012-08-04T01:23:46.477 に答える