如何创建一个接受客户端连接的Java Server,然后为客户端对建立中继连接 [英] How to create a java Server that accepts client connections and then build a relay connection for a client pair

查看:86
本文介绍了如何创建一个接受客户端连接的Java Server,然后为客户端对建立中继连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个服务器,该服务器可以接受多个连接,然后将两个客户端成对绑定,并在这两个客户端之间转发数据.但这是关于多对客户的.我已经有多线程服务器,可以为每个新连接的客户端创建一个新线程.对我来说,问题是这些线程彼此不认识,因此我必须以某种方式将2个客户端连接到连接对.

I want to create a server that can accept multiple connections and then bind 2 clients as a pair and forward the data between these 2 clients. But it is about multiple pairs of clients. I already have multithread server that can create a new thread for each new connected client. The problem for me is that these threads dont know of each other and somehow I have to connect 2 clients to a connection pair.

现在,我只是按以下方式创建这些对连接:我等待第一个客户端,然后等待第二个客户端,然后为输入的客户端1打开一个线程,该线程被转发到客户端2,反之亦然. .这不适用于多个客户端.

For now I just create these pair connection as this: I wait for the first client, then I wait for the second client and then open a thread for the input of client 1 that gets forwarded to client 2 and the other way around. This is not usable for multiple clients.

我如何做到这一点?

推荐答案

我的看法是,客户需要

  1. 与服务器建立TCP(?)连接,
  2. 表明自己的身份
  3. 提供希望与之交谈的其他客户的ID

连接的第一个客户端必须保持等待状态(在服务器的某些全局表中),直到第二个客户端连接为止. 一旦将一对客户端识别为对话者,您将创建一对线程,以将每个客户端发送的数据转发给另一个客户端.

The first that connects would have to be kept on hold (in some global table in your server) until the second client connects. Once a pair of clients would have been recognized as interlocutors, you would create a pair of threads to forward the data sent by each client to the other one.

更新:示例

ClientSocket.java

ClientSocket.java

package matchmaker;

import java.io.Closeable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class ClientSocket implements Closeable {
    private final Socket socket;
    private final InputStream in;
    private final OutputStream out;
    private final String ownId;
    private final String peerId;

    public ClientSocket(Socket socket) throws IOException {
        this.socket = socket;
        this.in = socket.getInputStream();
        this.out = socket.getOutputStream();
        DataInputStream din = new DataInputStream(in);
        this.ownId = din.readUTF();
        this.peerId = din.readUTF();
    }

    public ClientSocket(String server, int port, String ownId, String peerId)
            throws IOException {
        this.socket = new Socket(server, port);
        this.socket.setTcpNoDelay(true);
        this.in = socket.getInputStream();
        this.out = socket.getOutputStream();
        this.ownId = ownId;
        this.peerId = peerId;
        DataOutputStream dout = new DataOutputStream(out);
        dout.writeUTF(ownId);
        dout.writeUTF(peerId);
    }

    public String getOwnId() {
        return ownId;
    }

    public String getPeerId() {
        return peerId;
    }

    public InputStream getInputStream() {
        return in;
    }

    public OutputStream getOutputStream() {
        return out;
    }

    @Override
    public void close() throws IOException {
        socket.close();
    }
}

Matchmaker.java:服务器

Matchmaker.java: the server

package matchmaker;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Matchmaker extends Thread {
    private static final Logger LOG
            = Logger.getLogger(Matchmaker.class.getName());

    private final int port;
    private final Map<ClientPair,ClientSocket> waiting = new HashMap<>();

    public static void main(String[] args) {
        try {
            int port = 1234;
            int st = 0;
            for (String arg: args) {
                switch (st) {
                    case 0:
                        switch (arg) {
                            case "-p":
                                st = 1;
                                break;
                            default:
                                System.out.println("Unknown option: " + arg);
                                return;
                        }
                        break;
                    case 1:
                        port = Integer.parseInt(arg);
                        st = 0;
                        break;
                }
            }
            Matchmaker server = new Matchmaker(port);
            server.start();
            server.join();
        } catch (InterruptedException ex) {
            LOG.log(Level.SEVERE, null, ex);
        }
    }

    private Matchmaker(int port) {
        this.port = port;
        setDaemon(true);
    }

    @Override
    public void run() {
        try {
            ServerSocket server = new ServerSocket(port);
            while (true) {
                ClientSocket socket = new ClientSocket(server.accept());
                ClientPair pair = new ClientPair(
                        socket.getOwnId(), socket.getPeerId());
                ClientSocket other;
                synchronized(this) {
                    other = waiting.remove(pair.opposite());
                    if (other == null) {
                        waiting.put(pair, socket);
                    }
                }
                if (other != null) {
                    LOG.log(Level.INFO, "Establishing connection for {0}",
                            pair);
                    establishConnection(socket, other);
                } else {
                    LOG.log(Level.INFO, "Waiting for counterpart {0}", pair);
                }
            }
        } catch (IOException ex) {
            LOG.log(Level.SEVERE, null, ex);
        }
    }

    private void establishConnection(ClientSocket socket, ClientSocket other)
            throws IOException {
        Thread thread = new StreamCopier(
                socket.getInputStream(), other.getOutputStream());
        thread.start();
        thread = new StreamCopier(
                other.getInputStream(), socket.getOutputStream());
        thread.start();
    }
}

StreamCopier.java:从InputStream读取并写入OutputStream的线程

StreamCopier.java: a thread that reads from an InputStream and writes to an OutputStream

package matchmaker;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

public class StreamCopier extends Thread {
    private static final Logger LOG
            = Logger.getLogger(StreamCopier.class.getName());

    private final InputStream in;
    private final OutputStream out;

    public StreamCopier(InputStream in, OutputStream out) {
        this.in = in;
        this.out = out;
        setDaemon(true);
    }

    @Override
    public void run() {
        LOG.info("Start stream copier");
        try {
            for (int b = in.read(); b != -1; b = in.read()) {
                out.write(b);
            }
        } catch (IOException ex) {
            LOG.log(Level.SEVERE, null, ex);
        } finally {
            LOG.info("End stream copier");
            try {
                out.close();
            } catch (IOException ex) {
                LOG.log(Level.SEVERE, null, ex);
            }
        }
    }
}

ClientPair.java:一对客户端ID

ClientPair.java: a pair of client IDs

package matchmaker;

public class ClientPair {
    private final String client1;
    private final String client2;

    public ClientPair(String client1, String client2) {
        this.client1 = client1;
        this.client2 = client2;
    }

    public String getClient1() {
        return client1;
    }

    public String getClient2() {
        return client2;
    }

    public ClientPair opposite() {
        return new ClientPair(client2, client1);
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 73 * hash + client1.hashCode();
        hash = 73 * hash + client2.hashCode();
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ClientPair other = (ClientPair) obj;
        return client1.equals(other.client1) && client2.equals(other.client2);
    }

    @Override
    public String toString() {
        return "[" + client1 + "," + client2 + "]";
    }
}

ReaderClient.java:从套接字读取并写入标准输出的示例客户端

ReaderClient.java: a sample client that reads from the socket and writes to standard output

package matchmaker;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ReaderClient {
    private static final Logger LOG = Logger.getLogger(ReaderClient.class.getName());

    public static void main(String[] args) {
        try (ClientSocket client
                = new ClientSocket("localhost", 1234, "reader", "writer")) {
            Reader reader
                    = new InputStreamReader(client.getInputStream(), "UTF-8");
            BufferedReader in = new BufferedReader(reader);
            for (String s = in.readLine(); s != null; s = in.readLine()) {
                System.out.println(s);
            }
        } catch (IOException ex) {
            LOG.log(Level.SEVERE, null, ex);
        }
    }
}

WriterClient.java:一个写入套接字的示例客户端

WriterClient.java: a sample client that writes to the socket

package matchmaker;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.logging.Level;
import java.util.logging.Logger;

public class WriterClient {
    private static final Logger LOG = Logger.getLogger(ReaderClient.class.getName());

    public static void main(String[] args) {
        try (ClientSocket client
                = new ClientSocket("localhost", 1234, "writer", "reader")) {
            Writer writer
                    = new OutputStreamWriter(client.getOutputStream(), "UTF-8");
            PrintWriter out = new PrintWriter(writer);
            for (int i = 0; i < 30; ++i) {
                out.println("Message line " + i);
            }
            out.flush();
        } catch (IOException ex) {
            LOG.log(Level.SEVERE, null, ex);
        }
    }    
}

这篇关于如何创建一个接受客户端连接的Java Server,然后为客户端对建立中继连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆