views:

924

answers:

11

Let's say Java has two kind of sockets:

  • server sockets "ServerSocket"
  • client sockets or just "Socket"

Imagine the situation of two processes:

X = Client
Y = Server

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: Then the accept() method returns a new client type "Socket",
when it occurs, the two Sockets get "interconnected",

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 Client Sockets get interconnected!!

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?

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)

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.

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

+4  A: 

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.

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.

Eric Petroelje
It's to convert any server application to a client one. It works right, but I just doubt about the two-threads approach to be a good one, or if it could 've been solved via other language or library or OS facility.
Hernán Eche
@Hernán - I wouldn't worry about the two threads approach. As long as you manage the threads efficiently by putting them into a wait state on the sockets, you should be fine.
Eric Petroelje
+2  A: 

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.

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.

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.

Marcus Adams
+1  A: 

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.

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.

Jim Rush
I've added an example application in the question, thanks
Hernán Eche
+1  A: 

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.

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.

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.

Hardcoded
A: 

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

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.

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.

Babar
Simply because in that approach you would need to know VNCServer IP, and that's exactly one of the reasons I made this, to avoid that need (and others like dynamic IP, private IP, configuring routers, proxies, etc..it's really much easier to reach the network being a TCP Client)
Hernán Eche
To communicate with VNCServer, MyApp needs to know the IP of the VNCServer. Even in your case [VNCServer<---MyApp--->|middle server|<---User], MyApp will need the VNCServer IP, right ?
Babar
@Babar No, in my case, MyApp is in the same machine
Hernán Eche
@Hernán: But, with your middle server, you have to know the external IP from that middle server, so what is the diff between knowing the middle servers IP and the VNCServers IP?
Martijn Courteaux
@Martijn Courteaux, I have access to the middle server, and it has public static IP, VNC computer can be in any place, with any unknown network configuration, dinamic IP, proxy, whatever, so the best way to get out of there is being a Client Socket
Hernán Eche
+2  A: 

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

Let's say how to have two client socket get interconnected without using an intermediate 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)

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.

Why don't make immediately a connection to the 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.

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.

Martijn Courteaux
@Martijn Courteaux If you want to know why please read comments on Babar answer
Hernán Eche
@Hernán Eche: +1 because now I understand what your problem is ;-)
Martijn Courteaux
I <3 ASCII art, +1
slf
It was the selected answer because the ASCII art!! =S weird..this is the last time I use bounty, it sucks..., and @Martijn Corteaux, no, your solution is not posible, conection labeled as (3) can't be done, because the client and the VNCServer could be behind NAT/Firewall/Etc, you keep thinking they can be reached as server, they don't
Hernán Eche
+1  A: 

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, javaProxy to 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.

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.

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.

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.

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.)

mdma
This is first answer I read that fit the question I made, of course I am not looking for an application to do the job but how to solve that from java source, anyway is something! +1, I will keep working, and waiting for java 7! thanks
Hernán Eche
A: 

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.

Chris Dodd
Only works for endpoints running on the same machine, and is *nix specific. though.
nos
A: 

The classic Java approach to connection based socket communication is to set up a ServerSocket on a known IP and port and block on it's accept call, which (following a successful connection attempt) returns a new Socket with 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.

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 Runnable requirement to be dropped, i.e. no additional thread is needed for the handler and the ServerSocket accept call is postponed until the current connection is closed.

In fact, if you use NIO you 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).

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 NAT service or some sort of proxy bridging the public IP to the private IP.

Don Mackenzie
A: 

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).

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 can do is close the server socket once you've accepted a connection; for simple cases, that might be good enough.

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

Donal Fellows
A: 

If you want a peer-to-peer connection you might want to consider using UDP.

UDP can receive from anything without establishing a connection first, you will still need a server to tell the clients who they're receiving data from though.

Hope this helped.

Kevin