简体   繁体   English

Java Server SSL套接字和Python Client SSL套接字-服务器发送消息时出现问题

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

I'm trying communicate a server SSL socket Java and a client SSL socket Python. 我正在尝试传达服务器SSL套接字Java和客户端SSL套接字Python。 The first message sent is ok, but when the server sends another messages, the client receives the messages dividing in 2 parts. 发送的第一条消息可以,但是当服务器发送另一条消息时,客户端将接收到的消息分为两部分。 For example: If the server send message "abcdefghij", the client receives first "a", and after "bcdefghij". 例如:如果服务器发送消息“ abcdefghij”,则客户端将收到第一个“ a”,然后是“ bcdefghij”。

Somebody knows why after the first time the messages are received in two parts? 有人知道为什么在第一次收到消息后会分为两个部分吗? Regards. 问候。

The client code: 客户端代码:

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()

The server code: 服务器代码:

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

It seems that you expect to be always able to read whatever quantity of data you've sent in one block from the other side. 看来您希望总是能够从另一侧读取在一个块中发送的任何数量的数据。

This is a common mistake, not specific to SSL/TLS, but also related to plain TCP communications. 这是一个常见错误,不仅针对SSL / TLS,而且与纯TCP通信有关。

You should always loop and read whatever you intend to read. 您应该始终循环阅读您打算阅读的内容。 You should also define your protocol (or use an existing one) to take into account commands and request/response terminators. 您还应该定义协议(或使用现有协议)以考虑命令和请求/响应终止符。

HTTP, for example, uses blank lines to tell the end of the headers and the Content-Length header or chunked transfer encoding to tell the recipient when to stop reading the body. 例如,HTTP使用空白行来告知标头的末尾,并使用Content-Length标头或分块传输编码来告知接收方何时停止读取正文。

SMTP uses line-delimited commands and a single . SMTP使用行分隔命令和单个. for the end of a message. 在邮件末尾。

It's a combination of the Naggle algorithm on the server side and delayed ACK in the client's TCP stack. 它是服务器端Naggle算法与客户端TCP堆栈中的延迟ACK的组合。 You'll also find ~ 40ms delay between the two packets. 您还会发现两个数据包之间有40ms的延迟。

Disable the Naggle algo on the server side to remedy: 在服务器端禁用Naggle算法以补救:

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

More information on why this occurs here: http://www.stuartcheshire.org/papers/NagleDelayedAck/ 有关发生这种情况的原因的更多信息,请访问: http : //www.stuartcheshire.org/papers/NagleDelayedAck/

Edit to add: Note Bruno's answer below. 编辑添加:注意下面的布鲁诺答案。 While this describes the specific reason for what you're seeing here, the way you're expecting data from the server is not guaranteed. 虽然这描述了您在此处看到的具体原因,但不能保证期望服务器提供数据的方式。

I tried to solve my problem in two ways, the message always break at the first character, so I decided to add three spaces at the beginning of each message and end. 我试图通过两种方式解决问题,消息总是在第一个字符处中断,因此我决定在每个消息的开头和结尾添加三个空格。 So when the client receives them do a trim on the message. 因此,当客户收到消息时,请对消息进行修剪。 Another way I found was using DataOutputStream, the method that delivers writeBytes byte by byte, used it on the server and the client had q change as it receives data, the message had to build client-side processing to finally do what I want to house the end of message. 我发现的另一种方法是使用DataOutputStream,该方法逐字节传递writeBytes,在服务器上使用它,并且客户端在接收数据时进行q更改,消息必须构建客户端处理才能最终完成我想容纳的内容消息的结尾。 Thanks for the discussion! 感谢您的讨论!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM