Java多个SSL客户端到一个服务器

时间:2013-05-29 00:48:57

标签: java sockets ssl

我目前正在为Android这个学期开展一个小小的个人项目。我要做的是用我的Android手机与我的https服务器建立大量连接,以便服务器停机。我对编程一无所知,因为我正在学习网络而不是计算机语言。但我不知怎的从这里和那里一块一块地收集并制作了如下代码。我认为它正在使用套接字连接。

import java.net.*;
import java.io.*;
import java.security.*;
import javax.net.ssl.*;

public class HTTPSClient {
    public static void main(String[] args) {
        System.out.println("Usage: java HTTPSClient host");

        int port = 443; // default https port
        String host = "192.168.0.8";

        TrustManager[] trustAll = new javax.net.ssl.TrustManager[]{
          new javax.net.ssl.X509TrustManager(){
              public java.security.cert.X509Certificate[] getAcceptedIssuers(){
                return null;
              }
              public void checkClientTrusted(java.security.cert.X509Certificate[] certs,String authType){}
              public void checkServerTrusted(java.security.cert.X509Certificate[] certs,String authType){}
          }
        };

        try {
            javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");
            sc.init(null, trustAll, new java.security.SecureRandom());

            Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
            SSLSocketFactory factory = (SSLSocketFactory) sc.getSocketFactory();
            SSLSocket socket = (SSLSocket) factory.createSocket(host, port);

            Writer out = new OutputStreamWriter(socket.getOutputStream());
            out.write("GET / HTTP/1.0\\r\\n");
            out.write("\\r\\n");
            out.flush();

            // read response
            BufferedReader in = new BufferedReader(
            new InputStreamReader(socket.getInputStream()));
            int c;
            while ((c = in.read()) != -1) {
                System.out.write(c);
            }
            // out.close();
            // in.close(); 
            // socket.close();

        } catch (Exception e) {
            System.err.println(e);
        }
    }
}

我在我的macbook上启用了https,我可以看到端口443正在侦听。当我执行上面的代码时,我可以通过'netstat -an |看到一个已建立的连接grep 443'直到我停止它。我的问题是:如果我想与此代码建立多个连接,我应该添加什么?这段代码可以吗?我的想法是,如果我能看到大量已建立的连接到我的macbook上的443端口,我将无法将https :: // localhost与浏览器连接,因为机器已关闭。我不知道这是否正确,但我希望如此。因为这个学期差不多结束了,我无论如何都要报道。

当我为Android手机制作代码时,我不确定该代码是否相同,但我只想先看看发生的事情。我真的很绝望,请帮助我。非常感谢你。

2 个答案:

答案 0 :(得分:1)

根据我的理解,您正尝试将多个客户端(电话)连接到您的服务器。

您的服务器看起来很稳固。您应该能够修改它以轻松处理多个客户端。

通常,您需要某种处理程序来处理传入的客户端连接。您将需要一个循环来等待新连接,然后需要一个线程来独立处理每个连接。套接字的每个实例只能处理一个连接。套接字工厂允许您将多个套接字实例绑定到服务器。我有两个类来处理多个连接。我的第一个类是服务器本身,第二个类是处理每个客户端的线程。

如果您不熟悉线程,则应检查它。

这是服务器类:

public class ServerThread extends Thread
{
    private Vector<ClientHandlerThread> connectedClients = new Vector<ClientHandlerThread>(20, 5);

   public void run()
   {
    SSLServerSocket sslDataTraffic = null;
    SSLServerSocket sslFileTraffic = null;
    SSLServerSocketFactory sslFac = null;

    try
    {
        System.out.print("Validating SSL certificate... ");
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(new FileInputStream(certificateDir), password);
        System.out.println("DONE.");

        System.out.print("Creating trust engine........ ");
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStore);
        System.out.println("DONE.");

        System.out.print("Creating key engine.......... ");
        KeyManagerFactory kmf = KeyManagerFactory.getInstance((KeyManagerFactory.getDefaultAlgorithm()));
        kmf.init(keyStore, password);
        System.out.println("DONE.");

        System.out.print("Creating SSL context......... ");
        System.setProperty("https.protocols", "SSL");
        SSLContext  ctx = SSLContext.getInstance("SSL");
        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        sslFac = ctx.getServerSocketFactory();
        System.out.println("DONE.");
    }
    catch (Exception e) {}

    try
    {
        System.out.print("Creating data socket......... ");
        sslDataTraffic = (SSLServerSocket) sslFac.createServerSocket(dataPort);
        System.out.println("DONE. Est. on:" + dataPort);
    }
    catch (IOException e)
    {
        System.out.println("FAILED.");
        System.out.println(e.toString() + " ::: " + e.getCause());
        System.exit(-1);
    }

    try
    {
        System.out.print("Creating file socket......... ");
        sslFileTraffic = (SSLServerSocket) sslFac.createServerSocket(filePort);
        System.out.println("DONE. Est. on:" + filePort);

    }
    catch (IOException e)
    {
        System.out.println("FAILED.");
        System.out.println(e.toString() + " ::: " + e.getCause());
        System.exit(-1);
    }

    while (running)
        {
            SSLSocket sslDataTrafficSocketInstance = (SSLSocket) sslDataTraffic.accept();
            SSLSocket sslFileTrafficSocketInstance = (SSLSocket) sslFileTraffic.accept();
            ClientHandlerThread c = new ClientHandlerThread(sslDataTrafficSocketInstance, sslFileTrafficSocketInstance);
            c.start();
            connectedClients.add(c);
        }
}

注意课程结束时的while循环。它会等到客户端连接(调用accept()方法)。创建一个独立的线程来处理该客户端(电话)。

客户端线程如下:

public class ClientHandlerThread extends Thread
{
private boolean running = true;

private SSLSocket dataSocket;
private SSLSocket fileSocket;

private PrintWriter writer;
private BufferedReader reader;
private InputStream inputStream;
private OutputStream outputStream;

public ClientHandlerThread(
        SSLSocket dataSocket,
        SSLSocket fileSocket)
{
    this.dataSocket = dataSocket;
    this.fileSocket = fileSocket;

    try
    {
        this.reader = new BufferedReader(new InputStreamReader(this.dataSocket.getInputStream()));
        this.writer = new PrintWriter(this.dataSocket.getOutputStream());
        this.inputStream = fileSocket.getInputStream();
        this.outputStream = fileSocket.getOutputStream();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

    this.ip = this.dataSocket.getInetAddress().getHostAddress();
}

public void run()
{
    try
    {
        writer.println("SERVER_HANDSHAKE_INIT");
        writer.flush();

        String fromClient;
        while (running && (fromClient = reader.readLine()) != null)
        {
            if (fromClient.equals("CLIENT_HANDSHAKE_INIT"))
                System.out.println("Client Connected: " + getIP());
        }
    }
    catch (IOException e)
    {
        e.getCause();

    }
}

    public String getIP()
{
    return ip;
}

public boolean isRunning()
{
    return running;
}

public void setRunning(boolean running)
{
    this.running = running;
}
}

您现在可以遍历包含所有客户端的Vector中的每个客户端线程。这将允许您处理多个客户端并独立地与每个客户端进行交互。这包括读取输入/输出流。

这些类是我用于夏季开发的简单远程管理系统的版本。您应该能够根据需要修改它们以满足您的需求。您可以向客户端线程构造函数添加一个参数,以跟踪命名,例如。

我希望这解释了如何处理到服务器的多个传入连接。 随意DM或给我发电子邮件以获取更多信息。

干杯

答案 1 :(得分:0)

您可以更改代码以在循环中打开连接:

        int numConnections = 100;
        for (int i=0; i<numConnections; i++) {
            SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
            Writer out = new OutputStreamWriter(socket.getOutputStream());
            out.write("GET / HTTP/1.0\\r\\n");
            out.write("\\r\\n");
            out.flush();

            // read response
            BufferedReader in = new BufferedReader(
            new InputStreamReader(socket.getInputStream()));
            int c;
            while ((c = in.read()) != -1) {
                System.out.write(c);
            }
            // out.close();
            // in.close(); 
            // socket.close();
        }

我强烈建议将套接字对象保留在数组或集合变量中,并在完成后关闭I / O流和套接字,但这也将在main()退出时完成 - 只知道这通常是不好的做法编程,如果你想要在打开连接的代码块之后整个程序没有退出的情况下重用这段代码。