Java 连接两个客户端套接字

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2578254/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 09:26:06  来源:igfitidea点击:

Connect two client sockets

javasocketsclientserversocket

提问by Hernán Eche

Let's say Java has two kind of sockets:

假设 Java 有两种套接字:

  • server sockets "ServerSocket"
  • client sockets or just "Socket"
  • 服务器套接字“ServerSocket”
  • 客户端套接字或只是“套接字”

Imagine the situation of two processes:

想象一下两个进程的情况:

X = Client
Y = Server

X = 客户端
Y = 服务器

The server process Y : has a "ServerSocket", that is listening to a TCP port
The client process X : sends a connection request through a "Socket" to Y.

服务器进程 Y :有一个“ServerSocket”,即监听 TCP 端口
客户端进程 X :通过“Socket”向 Y 发送连接请求。

Y: Then the accept()method returns a new client type "Socket",
when it occurs, the two Sockets get "interconnected",

Y:然后该accept()方法返回一个新的客户端类型“Socket”,
当它发生时,两个Sockets得到“互连”,

So: the socket in client process, is connected with the socket in the server process.
Then: reading/writing through socket X is like reading/writing through socket Y.
Now, two ClientSockets get interconnected!!

所以:客户端进程中的套接字,与服务器进程中的套接字相连。
然后:通过socket X读/写就像通过socket Y读/写一样。
现在,两个ClientSockets互连了!!

But...
What if I create the two Client sockets in same process, and I want to get them "interconnected" ?

但是......
如果我在同一个进程中创建两个客户端套接字,并且我想让它们“互连”怎么办?

... even possible?

……甚至可能?

Let's say how to have two client socket get interconnected without using an intermediate ServerSocket?

让我们说如何在不使用中间 ServerSocket 的情况下让两个客户端套接字互连?

I've solved it by creating two Threads for continuously reading A and writing B, and other for reading B and writng A...
But I think could be a better way... (Those world-energy-consuming threads are not necessary with the client-server approach)

我已经通过创建两个线程来连续读取 A 和写入 B 以及另一个用于读取 B 和写入 A 来解决它...
但我认为可能是一个更好的方法...(那些世界耗能线程不是必需的使用客户端 - 服务器方法)

Any help or advice would be appreciated!! Thanks

任何帮助或建议将不胜感激!谢谢



Edit:

编辑:

Example of application: "An existent server application could be converted to a client one", For example VNC server, one client socket connects to the VNC server, and other client socket is created (to connect to a middle server), then the application interconnects the two client resulting the VNC server is a client application! And then, no public IP is needed.

应用示例:“可以将现有的服务器应用程序转换为客户端应用程序”,例如 VNC 服务器,一个客户端套接字连接到 VNC 服务器,并创建另一个客户端套接字(连接到中间服务器),然后应用程序互连两个客户端导致 VNC 服务器是一个客户端应用程序!然后,不需要公共 IP。

VNCServer---MyApp---> |middle server| <---User

VNCServer---MyApp---> |中间服务器| <---用户

回答by Eric Petroelje

Why would you need to do that?

为什么你需要这样做?

If you want to have a "peer-to-peer" type system, then you just have each client run both a client and a server socket - the server socket for accepting connections from other clients and the client socket for establishing connections to others.

如果您想要一个“点对点”类型的系统,那么您只需让每个客户端同时运行一个客户端和一个服务器套接字——服务器套接字用于接受来自其他客户端的连接,而客户端套接字用于与其他客户端建立连接。

ETA: It wasn't entirely clear what you were asking in the original question, but since your edit, it seems like you are looking to create a sort of proxy server.

ETA:您在原始问题中提出的问题并不完全清楚,但是自从您进行编辑后,您似乎希望创建一种代理服务器

In your example, your app would create two client sockets, one connecting to the VNCServer and the other connecting to the "middle server". The "middle server" would then have two server sockets (one for your app to connect to and one for the user to connect to. Internally it would then need to know how to match those sockets up and shuttle data between the two.

在您的示例中,您的应用程序将创建两个客户端套接字,一个连接到 VNCServer,另一个连接到“中间服务器”。然后,“中间服务器”将有两个服务器套接字(一个供您的应用程序连接,另一个供用户连接。在内部,它需要知道如何匹配这些套接字并在两者之间传输数据。

回答by Marcus Adams

The ServerSocket allows you to listen for connections on a particular port. When a server socket accepts a connection, it spawns another thread, and moves the connection to another port, so the original port can still listen for additional connections.

ServerSocket 允许您侦听特定端口上的连接。当服务器套接字接受连接时,它会生成另一个线程,并将连接移动到另一个端口,因此原始端口仍然可以侦听其他连接。

The client initiates the connection on the known port. Then, typically, the client will send some request, and the server will respond. This will repeat until the communication is complete. This is the simple client/server approach that the web uses.

客户端在已知端口上发起连接。然后,通常,客户端会发送一些请求,服务器会响应。这将重复直到通信完成。这是网络使用的简单客户端/服务器方法。

If you don't need this mechanism, and requests may come from either socket at any time, then implementing the reader and writer threads the way you have seems appropriate.

如果您不需要这种机制,并且请求可能随时来自任一套接字,那么以您拥有的方式实现读取器和写入器线程似乎是合适的。

Internally, they still use wait mechanisms, so you shouldn't see much CPU usage while they wait for data to arrive.

在内部,它们仍然使用等待机制,因此在它们等待数据到达时您应该不会看到太多 CPU 使用率。

I think you still need one end to be a server socket because I don't think it's possible to have a client socket accept a connection. ClientSocket implies TCP, which requires a connection. If you used DatagramSocket, which implies UDP, you could have client to client communication, without a connection.

我认为您仍然需要一端作为服务器套接字,因为我认为客户端套接字接受连接是不可能的。ClientSocket 意味着 TCP,它需要一个连接。如果您使用 DatagramSocket,这意味着 UDP,您可以在没有连接的情况下进行客户端到客户端的通信。

回答by Jim Rush

Are you trying to created a mocked socket ? If so, mocking both sides of the pipe may be a bit more complicated than necessary.

您是否正在尝试创建一个模拟套接字?如果是这样,模拟管道的两侧可能会比必要的复杂一些。

On the other hand, if you just want to create a data pipe between two threads, you could use PipedInputStream and PipedOutputStream.

另一方面,如果您只想在两个线程之间创建数据管道,则可以使用 PipedInputStream 和 PipedOutputStream。

However, without more information about what your trying to accomplish, I cannot tell you if either of these choices is a good fit or if something else would be better.

但是,如果没有关于您尝试完成什么的更多信息,我无法告诉您这些选择中的任何一个是否合适,或者其他选择是否更好。

回答by Hardcoded

A socket(in networking terms) consists of 2 endpoints (The client and the server app) and 2 streams. The output stream of the client is the input stream of the server and vice versa.

A socket(在网络方面)由 2 个端点(客户端和服务器应用程序)和 2 个端点组成streams。客户端的输出流是服务器的输入流,反之亦然。

Now try to imagine what happens if a thread writes a lot of data to a stream while no one reads... There are buffers, true, but they aren't unlimited and they can vary in size. In the end your writing thread will hit the limit of the buffer and will block until someone frees the buffer.

现在试着想象如果一个线程向一个流写入大量数据而没有人读取会发生什么......确实有缓冲区,但它们不是无限的,它们的大小可以变化。最后,您的写入线程将达到缓冲区的限制并阻塞,直到有人释放缓冲区。

Having said that, you should now be aware that this will need at least two different threads per Stream: one that writes and one that reads the written bytes.

话虽如此,您现在应该意识到每个流至少需要两个不同的线程:一个用于写入,另一个用于读取写入的字节。

If your protocol is request-response style, you could stick with 2 threads per socket, but no less.

如果您的协议是请求-响应风格,您可以坚持每个套接字使用 2 个线程,但不能少。

You could try to replace the networking part of your application. Just create an abstract interface where you can hide the whole networking part, like:

您可以尝试替换应用程序的网络部分。只需创建一个抽象接口,您就可以在其中隐藏整个网络部分,例如:

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

This way you could easily remove the whole networking (including en- & decoding of your objects, etc) and minimize threading.

通过这种方式,您可以轻松删除整个网络(包括对象的编码和解码等)并最小化线程。

If you want the data binary, you could use pipes instead or implement your own streams to prevent threading. The business or processing logic should not know about sockets, streams are low-level enough and maybe too much.

如果您想要数据二进制文件,您可以改用管道或实现您自己的流以防止线程化。业务或处理逻辑不应该知道套接字,流足够低级,而且可能太多了。

But either way: Threading isn't bad, as long as you don't overuse it.

但无论哪种方式:线程都不错,只要您不过度使用它。

回答by Babar

Why do we need a middle server? If you just want to expose VNCServer. Why not try an architecture like following

为什么需要中间服务器?如果您只想公开 VNCServer。为什么不尝试像下面这样的架构

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

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

In this case MyApp acts both as a client (for VNCServer) and as a server(for User). So you will have to implement both client and server sockets in MyApp and then relay the data.

在这种情况下,MyApp 既充当客户端(对于 VNCServer)又充当服务器(对于用户)。因此,您必须在 MyApp 中实现客户端和服务器套接字,然后中继数据。

Edit:To communicate with VNCServer, MyApp needs to know the IP of the VNCServer. User will only be communicating with MyApp and only needs to know MyApp's IP Address. User doesn't need VNCServer's ip address.

编辑:要与 VNCServer 通信,MyApp 需要知道 VNCServer 的 IP。用户将只与 MyApp 通信,并且只需要知道 MyApp 的 IP 地址。用户不需要 VNCServer 的 ip 地址。

回答by Martijn Courteaux

First of all, don't call an accepted client (server-side) its socket a Client Socket. That is very confusing.

首先,不要调用已接受的客户端(服务器端)其套接字 a Client Socket。这是非常令人困惑的。

Let's say how to have two client socket get interconnected without using an intermediate ServerSocket?

让我们说如何在不使用中间 ServerSocket 的情况下让两个客户端套接字互连?

That is impossible. You always have to make a server-side, which can accept clients. Now the question is: which side of the connection should be the server-side?
Things you have to think about by this decision:

那是不可能的。您总是必须制作一个可以接受客户端的服务器端。现在的问题是:连接的哪一边应该是服务器端?
通过这个决定你必须考虑的事情:

  • A server should have a static public IP.
  • A server, which is after a router connected, has to do "port forwarding". (See UPnP)
  • A client has to know which host it has to connect to (public IP)
  • 服务器应该有一个静态公共 IP。
  • 连接路由器后的服务器必须进行“端口转发”。(见UPnP
  • 客户端必须知道它必须连接到哪个主机(公共 IP)

Middle server

中间服务器

I don't see what you want to do with that third server. Maybe holding the VNCServer's public IP? *Elister* wrote, you want to make a brigde between the client and the VNCServer. I don't see the advantage of it.我不明白你想用第三台服务器做什么。也许持有 VNCServer 的公共 IP?*Elister* 写道,您想在客户端和 VNCServer 之间建立桥梁。我没有看到它的好处。

Why don't make immediately a connection to the VNCServer?

为什么不立即连接到 VNCServer?

But if you really want it, you can make a situation like this:

但是如果你真的想要它,你可以做出这样的情况:

      /   VNCServer (Server Running)  <---.
     |                                     |
LAN -|                             Connects to VNCServer
     |                                     |
      \   MyApp (Server Running --> Accepts from Middle Server) <------.
                                                                        |
                                                            (Through a router)
                                                                        |
     Middle server (Server Running --> Accepts client) ---> Connects to Your App
                                             ^
                                             |
                                    (Through a router)
                                             |
     Client --> Connects to Middle Server --°

And this is how it looks without the third server (What I recommend you):

这就是没有第三台服务器的情况(我向您推荐):

      /   VNCServer (Server Running)  <---.
     |                                     |
LAN -|                             Connects to VNCServer
     |                                     |
      \   MyApp (Server Running --> Accepts Clients) <------.
                                                             |
                                                      (Through a router)
                                                             |
     Client --> Connects to MyApp --------------------------°



EDIT:

编辑:

I think I got it now:

我想我现在明白了:

We have to visualize your situation like this:

我们必须像这样想象你的情况:

                             Your Main Server (What you called middle server)
                    (1)         |       |      (2)
            /??????????????????/         \?????????????????\
           |                                                |
      Your VNCServer   <---------------------------->   The client
         (5)                        (3)

(1)The VNCServer connects to the main server. So, then the main server got the VNCServer its IP.
(2)The client connects to the main server.
(3)Now the main server knows where server and client are. Then he sends to the client where the server is. Then the client will connect to the IP he received from the main server. That is of course the IP from the VNCServer.
(5)The VNCServer is running is server to accept the client.

(1)VNCServer 连接到主服务器。所以,然后主服务器获得了 VNCServer 的 IP。
(2)客户端连接到主服务器。
(3)现在主服务器知道服务器和客户端在哪里。然后他发送到服务器所在的客户端。然后客户端将连接到他从主服务器收到的 IP。那当然是来自 VNCServer 的 IP。
(5)正在运行的 VNCServer 是接受客户端的服务器。

Now desktop sharing can start.

现在桌面共享可以开始了。

I think this is the most recommend situation you can have.
Of course writing it in Java is to you.

我认为这是您可以拥有的最推荐的情况。
当然,用 Java 编写它是给你的。

回答by mdma

I understand what you are after - I have had to solve the same problem in situations where the server was behind a masquerading firewall with a dynamic IP. I used a small freely available program, javaProxyto provide a solution. It makes the server appear as a client socket - internally, it is still a server, but javaProxy provides forwarding program - My App in the example - that creates client connections "from" the server. It also provides the proxy in the middle (Middle Server, in the example) to join the two client ends together - the client socket forwarded from the server, and the client socket from the actual client trying to connect to the server.

我明白你在追求什么 - 在服务器位于具有动态 IP 的伪装防火墙后面的情况下,我不得不解决同样的问题。我使用了一个免费的小程序javaProxy来提供解决方案。它使服务器显示为客户端套接字 - 在内部,它仍然是一个服务器,但 javaProxy 提供了转发程序 - 在示例中为我的应用程序 - 从服务器“创建”客户端连接。它还提供了中间的代理(在示例中为中间服务器)以将两个客户端连接在一起 - 从服务器转发的客户端套接字,以及尝试连接到服务器的实际客户端的客户端套接字。

The Middle Server is hosted outside the firewall on a known IP. (Even though we can pretend to do this without server sockets, each connection must involve a client and a server end and so we make sure the Middle Server is on an IP that the clients can reach.) In my case I just used a simple hosting provider that let me run a java from the shell.

中间服务器托管在已知 IP 上的防火墙之外。(即使我们可以在没有服务器套接字的情况下假装这样做,但每个连接都必须涉及一个客户端和一个服务器端,因此我们确保中间服务器位于客户端可以访问的 IP 上。)在我的情况下,我只使用了一个简单的托管提供商,让我从 shell 运行 java。

With this setup, I could provide access to remote desktop and other services running behind a NAT firewall with dynamic IP, with access from my home machine which also was behind a NAT with dynamic IP. The only IP address I needed to know was the IP of the Middle Server.

通过这种设置,我可以提供对远程桌面和其他服务的访问,这些服务在具有动态 IP 的 NAT 防火墙后面运行,并可以从我的家用机器访问,该机器也在具有动态 IP 的 NAT 后面。我唯一需要知道的 IP 地址是中间服务器的 IP。

As to threading, the javaproxy library is almost certainly implemented using threads to pump data between the client sockets, but these do not consume any CPU resources (or power) while they are blocking waiting for I/O. When java 7 is released with support for asynchronous I/O then one thread per client socket pair will not be necessary, but this is more about performance and avoiding limits on the maximum number of threads (stack space) rather than power consumption.

至于线程,javaproxy 库几乎肯定是使用线程在客户端套接字之间泵送数据来实现的,但是这些在阻塞等待 I/O 时不会消耗任何 CPU 资源(或功率)。当 Java 7 发布支持异步 I/O 时,每个客户端套接字对一个线程将是不必要的,但这更多的是关于性能和避免对最大线程数(堆栈空间)而不是功耗的限制。

As to implementing this yourself with two client sockets in the same process requires the use of threads so long as java is dependent upon blocking I/O. The model is pull from the read end and push to the write end, so a thread is needed to pull from the read end. (If we had push from the read end, i.e asynchornous I/O then a dedicated thread per socket pair would not be needed.)

至于在同一进程中使用两个客户端套接字自己实现这一点需要使用线程,只要 java 依赖于阻塞 I/O。该模型是从读端拉取并推到写端,所以需要一个线程从读端拉取。(如果我们从读取端推送,即异步 I/O,则不需要每个套接字对的专用线程。)

回答by Chris Dodd

In C you can call socketpair(2)to get a pair of connected sockets, but I'm not sure if java has any built-in way of doing the same thing.

在 C 中,您可以调用socketpair(2)来获取一对连接的套接字,但我不确定 java 是否有任何内置的方式来做同样的事情。

回答by Don Mackenzie

The classic Java approach to connection based socket communication is to set up a ServerSocketon a known IP and port and block on it's accept call, which (following a successful connection attempt) returns a new Socketwith an implementation determined port (different from the ServerSocket's port). Typically the returned socket is passed to a handler implementing Runnable. Handlers are temporarily associated with a particular connection. Handlers can be reused and are associated with a particular thread usually for the lifetime of the connection. The blocking nature of classic Java socket IO makes connecting two sockets served by the same thread very difficult.

基于连接的套接字通信的经典 Java 方法是在已知 IP 和端口上设置ServerSocket并阻止它的接受调用,它(在成功的连接尝试之后)返回一个具有实现确定的端口的新Socket(不同于ServerSocket'运动)。通常,返回的套接字会传递给实现Runnable的处理程序。处理程序临时与特定连接相关联。处理程序可以重用,并且通常在连接的生命周期内与特定线程相关联。经典 Java 套接字 IO 的阻塞性质使得连接由同一线程提供服务的两个套接字非常困难。

However it is possible, and not unusual, to process both input and output streams of a socket on the same thread and supporting a single connection at a time allows the Runnablerequirement to be dropped, i.e. no additional thread is needed for the handler and the ServerSocket acceptcall is postponed until the current connection is closed.

然而,在同一线程上处理套接字的输入和输出流并一次支持单个连接是可能的,并且并不罕见,这允许删除Runnable要求,即处理程序不需要额外的线程,并且ServerSocket 接受调用被推迟到当前连接关闭。

In fact, if you use NIOyou can easily handle many connections simultaniously on the same thread using the Selector mechanism. This is one of the most important features of NIO, non-blocking I/O to decouple threads from connections (allowing very high numbers of connections to be handled by small thread pools).

事实上,如果您使用NIO,您可以使用 Selector 机制轻松地在同一线程上同时处理多个连接。这是NIO最重要的特性之一,非阻塞 I/O 将线程与连接解耦(允许小线程池处理大量连接)。

As far as the topology of your system, I'm sorry I'm not yet clear what you are after but it sounds like a job for either a NATservice or some sort of proxy bridging the public IP to the private IP.

就您系统的拓扑而言,很抱歉,我还不清楚您在追求什么,但这听起来像是NAT服务或某种将公共 IP 桥接到私有 IP 的代理的工作。

回答by Donal Fellows

Generally speaking, a client TCP socket has two ends (local and “remote”) and a server TCP socket has one end (because it is waiting for a client to connect to it). When a client connects to the server, the server internally spawns a client socket to form connected pair of client sockets that represent the communications channel; it's a pair because each socket views the channel from one end. This is How TCP Works (at a high level).

一般来说,客户端 TCP 套接字有两端(本地和“远程”),服务器 TCP 套接字有一端(因为它正在等待客户端连接到它)。当客户端连接到服务器时,服务器在内部产生一个客户端套接字,形成代表通信通道的客户端套接字连接对;它是一对,因为每个套接字从一端查看通道。这就是 TCP 的工作原理(在高层次上)。

You can't have two client sockets connect to each other in TCP, as the low-level connection protocol doesn't work that way. (You can have a connected pair of sockets created that way in Unix, but it's not exposed in Java and they're not TCP sockets.) What you cando is close the server socket once you've accepted a connection; for simple cases, that might be good enough.

你不能让两个客户端套接字在 TCP 中相互连接,因为低级连接协议不能那样工作。(您可以在 Unix 中以这种方式创建一对连接的套接字,但它没有在 Java 中公开,它们也不是 TCP 套接字。)您可以做的是在接受连接后关闭服务器套接字;对于简单的情况,这可能就足够了。

UDP sockets are different, of course, but they work with datagrams and not streams. That's a very different model of communication.

当然,UDP 套接字是不同的,但它们处理数据报而不是流。这是一种非常不同的沟通模式。