如何创建接受客户端连接的Java服务器,然后为客户端对构建中继连接

时间:2014-10-10 12:25:25

标签: java multithreading sockets

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

现在我只是创建这样的对连接:我等待第一个客户端,然后我等待第二个客户端,然后打开一个线程,用于客户端1的输入,转发到客户端2,反之亦然。这不适用于多个客户端。

我怎么能这样做?

1 个答案:

答案 0 :(得分:0)

我看到它的方式,客户需要

  1. 与您的服务器建立TCP(?)连接,
  2. 表明自己
  3. 提供其希望与之交谈的其他客户的ID
  4. 连接的第一个必须保持(在服务器的某个全局表中),直到第二个客户端连接。 一旦一对客户端被识别为对话者,您就会创建一对线程来将每个客户端发送的数据转发给另一个客户端。

    更新:示例

    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:服务器

    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的线程

    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

    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:从套接字读取并写入标准输出

    的示例客户端
    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:写入套接字的示例客户端

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