15

Javaに2種類のソケットがあるとしましょう。

  • サーバーソケット「ServerSocket」
  • クライアントソケットまたは単に「ソケット」

2つのプロセスの状況を想像してみてください。

X=クライアント
Y=サーバー

サーバープロセスY:には、TCPポートをリッスンしている「ServerSocket」があります
。クライアントプロセスX:は、「ソケット」を介してYに接続要求を送信します。

Y:次に、accept()メソッドは新しいクライアントタイプ「ソケット」を返します。
それが発生すると、2つのソケットは「相互接続」されます。

つまり、クライアントプロセスのソケットは、サーバープロセスのソケットに接続されています。
次に、ソケットXを介した読み取り/書き込みは、ソケットYを介した読み取り/書き込みに似ています。
これで、2つのクライアントソケットが相互接続されます。

しかし...
同じプロセスで2つのクライアントソケットを作成し、それらを「相互接続」したい場合はどうなりますか?

...可能ですか?

中間のServerSocketを使用せずに2つのクライアントソケットを相互接続する方法を考えてみましょう。

Aを継続的に読み取ってBを書き込むためのスレッドと、Bを読み取ってAを書き込むためのスレッドを2つ作成することで解決しました...
しかし、もっと良い方法だと思います...(これらの世界的なエネルギーを消費するスレッドは必要ありませんクライアントサーバーアプローチを使用)

どんな助けやアドバイスもいただければ幸いです!! ありがとう


編集:

アプリケーションの例:「既存のサーバーアプリケーションをクライアントアプリケーションに変換できます」、たとえば、VNCサーバーでは、1つのクライアントソケットがVNCサーバーに接続し、他のクライアントソケットが作成され(中間サーバーに接続するため)、アプリケーションが作成されます。 2つのクライアントを相互接続すると、VNCサーバーがクライアントアプリケーションになります。そして、パブリックIPは必要ありません。

VNCServer --- MyApp--->|中間サーバー| <---ユーザー

4

12 に答える 12

17

まず第一に、受け入れられたクライアント (サーバー側) をそのソケット a と呼ばないでくださいClient Socket。それは非常に紛らわしいです。

中間の ServerSocket を使用せずに 2 つのクライアント ソケットを相互接続する方法を考えてみましょう。

それは不可能です。クライアントを受け入れることができるサーバー側を常に作成する必要があります。問題は、接続のどちら側をサーバー側にするかということです。
この決定によって考慮しなければならないこと:

  • サーバーには静的パブリック IP が必要です。
  • ルーターが接続された後のサーバーは、「ポート転送」を行う必要があります。( UPnPを参照)
  • クライアントは、接続先のホスト (パブリック IP) を認識している必要があります。

ミドルサーバー

その 3 番目のサーバーで何をしたいのかわかりません。たぶん、VNCServer のパブリック IP を保持していますか? *Elister* は、クライアントと VNCServer の間に橋渡しをしたいと書いています。その利点がわかりません。

なぜすぐに VNCServer に接続しないのですか?

しかし、本当にそれが必要な場合は、次のような状況を作ることができます:

      / VNCServer (サーバー実行中) <---.
     | | | |
LAN -| VNCServer に接続します
     | | | |
      \ MyApp (実行中のサーバー --> 中間サーバーから受け入れます) <------.
                                                                        | |
                                                            (ルーター経由)
                                                                        | |
     中間サーバー (実行中のサーバー --> クライアントを受け入れる) ---> アプリに接続
                                             ^
                                             | |
                                    (ルーター経由)
                                             | |
     クライアント --> 中間サーバーに接続 --°

そして、これは3番目のサーバーなしでどのように見えるかです(私がお勧めするもの):

      / VNCServer (サーバー実行中) <---.
     | | | |
LAN -| VNCServer に接続します
     | | | |
      \ MyApp (実行中のサーバー --> クライアントを受け入れる) <-----.
                                                             | |
                                                      (ルーター経由)
                                                             | |
     クライアント --> MyApp に接続 --------------------------°


編集:

私は今それを手に入れたと思います:

次のように状況を視覚化する必要があります。

                             あなたのメインサーバー(あなたがミドルサーバーと呼んだもの)
                    (1) | | | (2)
            \⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻\
           | | | |
      VNCServer <--------------------------------> クライアント
         (5) (3)

(1)VNCServer はメイン サーバーに接続します。そのため、メインサーバーは VNCServer の IP を取得しました。
(2)クライアントはメイン サーバーに接続します。
(3)これで、メイン サーバーはサーバーとクライアントの場所を認識します。次に、サーバーがあるクライアントに送信します。次に、クライアントはメイン サーバーから受け取った IP に接続します。もちろん、これは VNCServer からの IP です。
(5)VNCServer が実行されているのは、クライアントを受け入れるためのサーバーです。

これで、デスクトップ共有を開始できます。

これが最も推奨される状況だと思います。
もちろん、Java で書くのはあなた次第です。

于 2010-06-13T12:26:32.870 に答える
5

なぜそれをする必要があるのですか?

「ピアツーピア」タイプのシステムが必要な場合は、各クライアントでクライアントとサーバーソケットの両方を実行するだけです。サーバーソケットは他のクライアントからの接続を受け入れるためのもので、クライアントソケットは他のクライアントへの接続を確立するためのものです。

ETA: 元の質問であなたが何を求めていたのか完全には明確ではありませんでしたが、あなたの編集以降、一種のプロキシ サーバーを作成しようとしているようです。

あなたの例では、アプリは 2 つのクライアント ソケットを作成します。1 つは VNCServer に接続し、もう 1 つは「中間サーバー」に接続します。「中間サーバー」には、2 つのサーバー ソケット (アプリが接続するためのものと、ユーザーが接続するためのもの) があります。内部的には、これらのソケットを一致させ、2 つの間でデータをやり取りする方法を知る必要があります。

于 2010-04-05T12:17:00.917 に答える
2

Socketこれは、何もせずに2つを接続したコードですServerSocket:

package primary;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Main {
    private static Object locker;
    public static void main(String[] args) {

        locker = new Object();
        final int[][] a = new int[6][];
        final int[][] b = new int[6][];
        final int[][] c;
        a[0] = new int[] {12340, 12341};
        a[1] = new int[] {12342, 12344};
        a[2] = new int[] {12342, 12343};
        a[3] = new int[] {12340, 12345};
        a[4] = new int[] {12344, 12345};
        a[5] = new int[] {12341, 12343};

        b[0] = new int[] {22340, 22341};
        b[1] = new int[] {22342, 22344};
        b[2] = new int[] {22342, 22343};
        b[3] = new int[] {22340, 22345};
        b[4] = new int[] {22344, 22345};
        b[5] = new int[] {22341, 22343};

        c = a;
        SwingUtilities.invokeLater(
                new Runnable() {

            @Override
            public void run() {
                Client client1 = new Client("client1", c[0], c[1]);
                client1.exe();
                client1.setLocation(0, 200);
                client1.setVisible(true);
                client1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
        SwingUtilities.invokeLater(
                new Runnable() {

            @Override
            public void run() {
                Client client2 = new Client("client2", c[2], c[3]);
                client2.exe();
                client2.setLocation(400, 200);
                client2.setVisible(true);
                client2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
        SwingUtilities.invokeLater(
                new Runnable() {

            @Override
            public void run() {
                Client client3 = new Client("client3", c[4], c[5]);
                client3.exe();
                client3.setLocation(800, 200);
                client3.setVisible(true);
                client3.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            }
        });
    }
}

package primary;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.*;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Client extends JFrame implements Runnable {
    private final String myName;
    private ServerSocket listener;
    private Socket connection1;
    private Socket connection2;
    private ObjectOutputStream output1;
    private ObjectOutputStream output2;
    private ObjectInputStream input1;
    private ObjectInputStream input2;
    private Object receiveObject;
    private Object1 sendObject1;
    private Object2 sendObject2;
    private final int[] myLocalPort;
    private final int[] connectionPort;
    private ExecutorService service;
    private Future<Boolean> future1;
    private Future<Boolean> future2;

    public Client(final String myName, int[] myLocalPort, int[] connectionPort) {
        super(myName);
        this.myName = myName;
        this.myLocalPort = myLocalPort;
        this.connectionPort = connectionPort;
        sendObject1 = new Object1("string1", "string2", myName);
        sendObject2 = new Object2("string1", 2.5, 2, true, myName);
        initComponents();
    }
    public void exe() {
        ExecutorService eService = Executors.newCachedThreadPool();
        eService.execute(this);
    }

    @Override
    public void run() {
        try {
                displayMessage("Attempting connection\n");
                try {
                    connection1  = new Socket(InetAddress.getByName("localhost"), connectionPort[0], InetAddress.getByName("localhost"), myLocalPort[0]);
                    displayMessage(myName + " connection1\n");
                } catch (Exception e) {
                    displayMessage("failed1\n");
                    System.err.println("1" + myName + e.getMessage() + "\n");
                }
                try {
                    connection2  = new Socket(InetAddress.getByName("localhost"), connectionPort[1], InetAddress.getByName("localhost"), myLocalPort[1]);
                    displayMessage(myName + " connection2\n");
                } catch (Exception e) {
                    displayMessage("failed2\n");
                    System.err.println("2" + myName + e.getMessage() + "\n");
                }
            displayMessage("Connected to: " + connection1.getInetAddress().getHostName() + "\n\tport: "
                + connection1.getPort() + "\n\tlocal port: " + connection1.getLocalPort() + "\n"
                + connection2.getInetAddress().getHostName() + "\n\tport: " + connection2.getPort()
                + "\n\tlocal port: " + connection2.getLocalPort() + "\n\n");
            output1 = new ObjectOutputStream(connection1.getOutputStream());
            output1.flush();
            output2 = new ObjectOutputStream(connection2.getOutputStream());
            output2.flush();
            input1 = new ObjectInputStream(connection1.getInputStream());
            input2 = new ObjectInputStream(connection2.getInputStream());
            displayMessage("Got I/O stream\n");
            setTextFieldEditable(true);
            service = Executors.newFixedThreadPool(2);
            future1 = service.submit(
                    new Callable<Boolean>() {

                @Override
                public Boolean call() throws Exception {
                    try {
                        processConnection(input1);
                        displayMessage("input1 finished");
                    } catch (IOException e) {
                        displayMessage("blah");
                    }
                    return true;
                }
            });
            future2 = service.submit(
                    new Callable<Boolean>() {

                @Override
                public Boolean call() throws Exception {
                    try {
                        processConnection(input2);
                        displayMessage("input2 finished");
                    } catch (IOException e) {
                        displayMessage("foo");
                    }
                    return true;
                }
            });
        } catch (UnknownHostException e) {
            displayMessage("UnknownHostException\n");
            e.printStackTrace();
        } catch (EOFException e) {
            displayMessage("EOFException\n");
            e.printStackTrace();
        } catch (IOException e) {
            displayMessage("IOException\n");
            e.printStackTrace();
        } catch(NullPointerException e) {
            System.err.println("asdf " + e.getMessage());
        } finally {
            try {
                displayMessage("i'm here\n");
                if((future1 != null && future1.get()) && (future2 != null && future2.get())) {
                    displayMessage(future1.get() + " " + future2.get() + "\n");
                    displayMessage("Closing Connection\n");
                    setTextFieldEditable(false);
                    if(!connection1.isClosed()) {
                        output1.close();
                        input1.close();
                        connection1.close();
                    }
                    if(!connection2.isClosed()) {
                        output2.close();
                        input2.close();
                        connection2.close();
                    }
                    displayMessage("connection closed\n");
                }
            } catch (IOException e) {
                displayMessage("IOException on closing");
            } catch (InterruptedException e) {
                displayMessage("InterruptedException on closing");
            } catch (ExecutionException e) {
                displayMessage("ExecutionException on closing");
            }
        }
    }//method run ends
    private void processConnection(ObjectInputStream input) throws IOException {
        String message = "";
        do {
            try {
                receiveObject = input.readObject();
                if(receiveObject instanceof String) {
                    message = (String) receiveObject;
                    displayMessage(message + "\n");
                } else if (receiveObject instanceof Object1) {
                    Object1 receiveObject1 = (Object1) receiveObject;
                    displayMessage(receiveObject1.getString1() + " " + receiveObject1.getString2()
                        + " " + receiveObject1.toString() + "\n");
                } else if (receiveObject instanceof Object2) {
                    Object2 receiveObject2 = (Object2) receiveObject;
                    displayMessage(receiveObject2.getString1() + " " + receiveObject2.getD()
                        + " " + receiveObject2.getI() + " " + receiveObject2.toString() + "\n");
                }
            } catch (ClassNotFoundException e) {
                displayMessage("Unknown object type received.\n");
            }
            displayMessage(Boolean.toString(message.equals("terminate\n")));
        } while(!message.equals("terminate"));
        displayMessage("finished\n");
        input = null;
    }
/**
 * This method is called from within the constructor to initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is always
 * regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    dataField = new javax.swing.JTextField();
    sendButton1 = new javax.swing.JButton();
    sendButton2 = new javax.swing.JButton();
    jScrollPane1 = new javax.swing.JScrollPane();
    resultArea = new javax.swing.JTextArea();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    dataField.setEditable(false);
    dataField.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            dataFieldActionPerformed(evt);
        }
    });

    sendButton1.setText("Send Object 1");
    sendButton1.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            sendButton1ActionPerformed(evt);
        }
    });

    sendButton2.setText("Send Object 2");
    sendButton2.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            sendButton2ActionPerformed(evt);
        }
    });

    resultArea.setColumns(20);
    resultArea.setEditable(false);
    resultArea.setRows(5);
    jScrollPane1.setViewportView(resultArea);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
            .addContainerGap()
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                .addComponent(jScrollPane1)
                .addComponent(dataField, javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                    .addComponent(sendButton1)
                    .addGap(18, 18, 18)
                    .addComponent(sendButton2)
                    .addGap(0, 115, Short.MAX_VALUE)))
            .addContainerGap())
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            .addComponent(dataField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(18, 18, 18)
            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(sendButton1)
                .addComponent(sendButton2))
            .addGap(18, 18, 18)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 144, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
    );

    pack();
}// </editor-fold>                        

private void dataFieldActionPerformed(java.awt.event.ActionEvent evt) {                                          
    // TODO add your handling code here:
    sendData(evt.getActionCommand());
    dataField.setText("");
}                                         

private void sendButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                            
    // TODO add your handling code here:
    sendData(sendObject1);
}                                           

private void sendButton2ActionPerformed(java.awt.event.ActionEvent evt) {                                            
    // TODO add your handling code here:
    sendData(sendObject2);
}                                           

/**
 * @param args the command line arguments
 */
private void displayMessage(final String messageToDisplay) {
    SwingUtilities.invokeLater(
            new Runnable() {
        @Override
                public void run() {
                    resultArea.append(messageToDisplay);
                }
            });
}
private void setTextFieldEditable(final boolean editable) {
    SwingUtilities.invokeLater(
            new Runnable() {

        @Override
        public void run() {
            dataField.setEditable(editable);
        }
    });
}
private void sendData(final Object object) {
    try {
        output1.writeObject(object);
        output1.flush();
        output2.writeObject(object);
        output2.flush();
        displayMessage(myName + ": " + object.toString() + "\n");
    } catch (IOException e) {
        displayMessage("Error writing object\n");
    }
}
// Variables declaration - do not modify                     
    private javax.swing.JTextField dataField;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextArea resultArea;
    private javax.swing.JButton sendButton1;
    private javax.swing.JButton sendButton2;
    // End of variables declaration                   
}

ここObject1Object2は 2 つのSerializableオブジェクトだけです。すべてのソケットが完全に接続されているようです。close()ソケットとその入力、出力ストリームのメソッドを呼び出さずに System.exit() を実行して再実行しても、問題なく動作します。しかし、メソッドが呼び出されていることを確認して System.exit() をclose()実行し、再度実行すると、次のようになります。

1client2Address already in use: connect

1client3Address already in use: connect

2client3Address already in use: connect

asdf null
1client1Connection refused: connect

2client2Connection refused: connect

asdf null
2client1Connection refused: connect

asdf null

私は何度も何度も再実行しますが、一定時間待ってから再実行しない限り、これを取得し続けます。最初と同じように問題なく動作します。

于 2012-08-04T10:25:11.570 に答える
2

ServerSocket を使用すると、特定のポートで接続をリッスンできます。サーバーソケットが接続を受け入れると、別のスレッドが生成され、接続が別のポートに移動されるため、元のポートは引き続き追加の接続をリッスンできます。

クライアントは既知のポートで接続を開始します。次に、通常、クライアントが何らかのリクエストを送信し、サーバーが応答します。これを通信が完了するまで繰り返します。これは、Web が使用する単純なクライアント/サーバー アプローチです。

このメカニズムが必要なく、要求がいつでもどちらのソケットからも来る可能性がある場合は、リーダー スレッドとライター スレッドを適切な方法で実装します。

内部的には、依然として待機メカニズムを使用しているため、データの到着を待機している間、CPU 使用率はそれほど高くありません。

クライアントソケットに接続を受け入れさせることはできないと思うので、サーバーソケットにするためにはまだ一方の端が必要だと思います。ClientSocket は、接続を必要とする TCP を意味します。UDP を意味する DatagramSocket を使用した場合、接続なしでクライアント間通信を行うことができます。

于 2010-04-05T12:31:59.677 に答える
1

A socket(ネットワーク用語で) は、2 つのエンドポイント (クライアントとサーバー アプリ) と 2 で構成されますstreams。クライアントの出力ストリームはサーバーの入力ストリームであり、その逆も同様です。

ここで、スレッドが大量のデータをストリームに書き込み、誰も読み取っていない場合にどうなるか想像してみてください... バッファはありますが、無制限ではなく、サイズが異なる場合があります。最終的に、書き込みスレッドはバッファーの限界に達し、誰かがバッファーを解放するまでブロックされます。

そうは言っても、ストリームごとに少なくとも 2 つの異なるスレッドが必要になることに注意してください。

プロトコルが要求応答スタイルの場合、ソケットごとに 2 つのスレッドを使用できますが、それ以下ではありません。

アプリケーションのネットワーキング部分を置き換えることができます。次のように、ネットワーク部分全体を非表示にできる抽象的なインターフェイスを作成するだけです。

interface MyCommunicator{
  public void send(MyObject object);
  public void addReader(MyReader reader);
}

interface MyReader{ //See Observer Pattern for more details
  public void received(MyObject object);
}

このようにして、ネットワーク全体 (オブジェクトのエンコードとデコードなどを含む) を簡単に削除し、スレッドを最小限に抑えることができます。

データ バイナリが必要な場合は、代わりにパイプを使用するか、独自のストリームを実装してスレッド化を防ぐことができます。ビジネスまたは処理ロジックはソケットについて認識すべきではありません。ストリームは十分に低レベルであり、多すぎる可能性があります。

しかし、いずれにせよ、使いすぎない限り、スレッド化は悪くありません。

于 2010-06-08T15:50:16.333 に答える
1

私はあなたが求めていることを理解しています - サーバーが動的 IP を持つマスカレード ファイアウォールの背後にある状況で、同じ問題を解決しなければなりませんでした。無料で入手できる小さなプログラムjavaProxyを使用して解決策を提供しました。サーバーをクライアントソケットとして表示します-内部的にはサーバーですが、javaProxyは転送プログラムを提供します-例ではMy App-サーバーからのクライアント接続を作成します。また、2 つのクライアント エンド (サーバーから転送されたクライアント ソケットと、サーバーに接続しようとしている実際のクライアントからのクライアント ソケット) を結合するために、中間 (例では中間サーバー) にプロキシを提供します。

中間サーバーは、既知の IP でファイアウォールの外側にホストされています。(サーバーソケットなしでこれを行うふりをすることはできますが、各接続にはクライアントとサーバー側が含まれている必要があるため、クライアントが到達できるIP上に中間サーバーがあることを確認します。)私の場合、単純なシェルからJavaを実行できるホスティングプロバイダー。

この設定により、動的 IP を使用して NAT ファイアウォールの背後で実行されているリモート デスクトップやその他のサービスへのアクセスを、同じく動的 IP を使用して NAT の背後にある自宅のマシンからアクセスできるようにすることができました。私が知る必要がある唯一の IP アドレスは、中間サーバーの IP でした。

スレッド化に関しては、javaproxy ライブラリはほぼ確実にスレッドを使用してクライアント ソケット間でデータをポンプするように実装されていますが、これらは I/O の待機をブロックしている間、CPU リソース (または電力) を消費しません。非同期 I/O をサポートする Java 7 がリリースされた場合、クライアント ソケット ペアごとに 1 つのスレッドは必要ありませんが、これは消費電力よりもパフォーマンスとスレッドの最大数 (スタック スペース) の制限の回避に関するものです。

同じプロセスで 2 つのクライアント ソケットを使用してこれを自分で実装するには、Java が I/O のブロックに依存している限り、スレッドを使用する必要があります。モデルは読み取り側からプルし、書き込み側にプッシュするため、読み取り側からプルするにはスレッドが必要です。(読み取り側からプッシュする場合、つまり非同期 I/O の場合、ソケット ペアごとに専用スレッドは必要ありません。)

于 2010-06-14T22:21:24.480 に答える
1

モックされたソケットを作成しようとしていますか? その場合、パイプの両側をモックすることは、必要以上に複雑になる可能性があります。

一方、2 つのスレッド間にデータ パイプを作成するだけの場合は、PipedInputStream と PipedOutputStream を使用できます。

ただし、何を達成しようとしているのかについての詳細情報がなければ、これらの選択肢のいずれかが適しているかどうか、または他の選択肢が優れているかどうかはわかりません.

于 2010-04-05T12:33:42.033 に答える
0

C では、socketpair(2)を呼び出して、接続されたソケットのペアを取得できますが、Java に同じことを行う組み込みの方法があるかどうかはわかりません。

于 2010-06-14T22:38:34.743 に答える
0

なぜ中間サーバーが必要なのですか? VNCServer を公開したいだけの場合。次のようなアーキテクチャを試してみませんか

VNCServer(S) <-> (C)MyApp(S) <-> (C) User

(S) represents a server socket
(C) represents a client socket

この場合、MyApp はクライアント (VNCServer の場合) とサーバー (ユーザーの場合) の両方として機能します。そのため、MyApp にクライアント ソケットとサーバー ソケットの両方を実装してから、データを中継する必要があります。

編集: VNCServer と通信するには、MyApp は VNCServer の IP を知る必要があります。ユーザーは MyApp とのみ通信し、MyApp の IP アドレスのみを知る必要があります。ユーザーは VNCServer の IP アドレスを必要としません。

于 2010-06-08T16:23:24.783 に答える
0

接続が必要な場合は、 のpeer-to-peer使用を検討してUDPください。

UDP最初に接続を確立しなくても何でも受信できますが、データを受信して​​いるクライアントをクライアントに伝えるサーバーが必要です。

これが役に立ったことを願っています。

于 2010-08-30T08:19:29.747 に答える
0

一般的に言えば、クライアント TCP ソケットには 2 つの端 (ローカルと「リモート」) があり、サーバー TCP ソケットには 1 つの端があります (クライアントが接続するのを待っているため)。クライアントがサーバーに接続すると、サーバーは内部的にクライアント ソケットを生成して、通信チャネルを表すクライアント ソケットの接続されたペアを形成します。各ソケットが一方の端からチャネルを表示するため、これはペアです。これがTCPの仕組みです(高レベル)。

低レベルの接続プロトコルはそのようには機能しないため、2 つのクライアント ソケットを TCP で相互に接続することはできません。(Unix ではこの方法で接続されたソケットのペアを作成できますが、Java では公開されておらず、TCP ソケットではありません。)接続を受け入れたら、サーバー ソケットを閉じることができます。単純なケースでは、それで十分かもしれません。

もちろん、UDP ソケットは異なりますが、ストリームではなくデータグラムで機能します。それはコミュニケーションの非常に異なるモデルです。

于 2010-06-15T12:29:11.457 に答える
-1

接続ベースのソケット通信に対する従来の Java のアプローチは、既知の IP とポートでServerSocketをセットアップし、その受け入れ呼び出しをブロックすることです。(接続の試行が成功すると) 実装によって決定されたポート ( ServerSocketとは異なる) を持つ新しいソケットが返されます。のポート)。通常、返されたソケットはRunnableを実装するハンドラに渡されます。ハンドラーは、特定の接続に一時的に関連付けられます。ハンドラーは再利用でき、通常は接続の存続期間中、特定のスレッドに関連付けられます。従来の Java ソケット IO のブロッキングの性質により、同じスレッドがサービスを提供する 2 つのソケットを接続することは非常に困難です。

ただし、ソケットの入力ストリームと出力ストリームの両方を同じスレッドで処理することは可能であり、珍しいことではありません。一度に 1 つの接続をサポートすることで、Runnable要件を削除できます。つまり、ハンドラとServerSocket の受け入れ呼び出しは、現在の接続が閉じられるまで延期されます。

実際、NIOを使用すると、Selector メカニズムを使用して、同じスレッドで同時に多くの接続を簡単に処理できます。これはNIOの最も重要な機能の 1 つであり、スレッドを接続から分離するためのノンブロッキング I/O です (非常に多数の接続を小さなスレッド プールで処理できるようにします)。

システムのトポロジーに関しては、申し訳ありませんが、あなたが何を求めているのかまだはっきりしていませんが、NATサービスまたはパブリック IP をプライベート IP にブリッジする何らかのプロキシの仕事のように思えます。

于 2010-06-15T00:18:14.813 に答える