I would like to realize a proof of concept TCP transparent proxy in Vert.x.
Requirement
A verticle that listens on port X and when somebody connects and sends data it opens a client connection towards a preconfigured TCP server.
From this moment until any of the peers closes the connection, a bidirectional channel is kept and data flows up and down the channel from client to server and viceversa.
Here's my attempt which is not working.
vertx.createNetServer().connectHandler(new Handler<NetSocket>() {
public void handle(final NetSocket socket) {
vertx.createNetClient().connect(6367, "localhost", new Handler<NetSocket>() {
#Override
public void handle(NetSocket cliSocket) {
Pump.createPump(socket, cliSocket);
Pump.createPump(cliSocket, socket);
}
});
}
}).listen(3000);
}
At least this is how I understood the meaning of the Pump class:
http://vertx.io/core_manual_java.html#pump
Where's my error?
I just was missing to start the pumps. Then it worked.
Pump.createPump(socket, cliSocket).start();
Pump.createPump(cliSocket, socket).start();
Related
I am currently trying to make an application that will send messages to a server using one port, but will receive messages on another port. However, based on tutorials I have followed, it looks like the act of connecting to the server is where ports come into play and my client is receiving and sending messages on the same port. How do I make it so it sends on one port but receives on the other?
Here is the code that I think is relevant from the client side (I put some stuff that seems unrelated because I think they are things that would be altered by receiving on one port but sending on another, and ignore the comment about replacing inetaddress, that is just me working on implementing this in a gui):
public void startRunning(){
try{
connectToServer();
setupStreams();
whileChatting();
}catch(EOFException eofException){
showMessage("\n Client terminated connection");
}catch(IOException ioException){
ioException.printStackTrace();
}finally{
closeStuff();
}
}
//connect to server
private void connectToServer() throws IOException{
showMessage("Attempting connection... \n");
connection = new Socket(InetAddress.getByName(serverIP), 480);//replace serverIP with ipTextField.getText or set serverIP to equal ipTextField.getText? Same with port number.
showMessage("Connected to: " + connection.getInetAddress().getHostName() );
}
//set up streams to send and receive messages
private void setupStreams() throws IOException{
output = new ObjectOutputStream(connection.getOutputStream());
output.flush();
input = new ObjectInputStream(connection.getInputStream());
showMessage("\n Streams are good! \n");
}
//while talking with server
private void whileChatting() throws IOException{
ableToType(true);
do{
try{
message = (String) input.readObject();
showMessage("\n" + message);
}catch(ClassNotFoundException classNotfoundException){
showMessage("\n Don't know that object type");
}
}while(!message.equals("SERVER - END"));
}
//send messages to server
private void sendMessage(String message){
try{
output.writeObject("CLIENT - " + message);
output.flush();
showMessage("\nCLIENT - " + message);
}catch(IOException ioException){
messageWindow.append("\n something messed up ");
}
}
//change/update message window
private void showMessage(final String m){
SwingUtilities.invokeLater(
new Runnable(){
public void run(){
messageWindow.append(m);
}
}
);
}
EDIT/UPDATE: To help clarify some things, here is some more information. The device that sends the first message is connected to a sensor, and it sends information when that sensor detects something to the other device. The receiving device sends a message back on a different port telling the original sending device how to respond. Lets name these two devices the "reporter-action taker" and the "decision maker-commander".
If you want to use TCP/IP sockets you can't use a a socket to send and another to read. That's not what they are for.
If you use a centralized distributed algorithm (server/client communication) you have to set the server to listen on a single socket port with the ServerSocket class: then the server tries to accept clients through that socket.
Example:
ServerSocket listener = new ServerSocket(Port)
While (true) {
new Clienthandler(listener.accept());
}
The server will listen on that port, and when a client tries to connect to that port if it is accepted the server launches its handler. On this handler constructor the Socket object used on the client is received on an argument and can then be used to get the writers and the readers. The reader on this handler class will be the writer on the client class and vice-versa, maybe that's what you were looking for.
Your question about using two ports in this manner is a bit strange. You state that you have a client and a server and that they should communicate on different ports.
Just to clarify picture the server as a hanging rack for jackets with several hooks in a row. Each port the server listened on represents a hook. When it comes to the client server relationship the client or jacket knows where to find its hook, however the hook is blind and have no idea where to find jackets.
Now, the client selects a port or a hook and connects to it. The connection is like a pipeline with two pipes. One for the client to deliver data to the server with and the other to send data from the server back to the client. When the connection is established data can be transferred both ways. This means that we only need one port open on the server to send data both from the client to the server and in the opposite direction.
The reason for only having one open port open on the server for the clients to connect to is that holding an open port for connections is hard to do on a regular client computer. The normal desktop user will be behind several firewalls blocking incoming connections. If that wasn't the case the client would probably be hacked senseless from malicious viruses.
Moving on with the two port solution we could not call this a client server connection per say. It would be more like a peer to peer connection or something like that. But if this is what you want to do, the application connecting first would have to start by telling the other application what ip and port to use for connecting back, it should probably also want to give some kind of token that are to be used to pair the new incoming connection when connecting back.
You should take note that making such an implementation is not a good idea most of the time as it complicates things a whole lot for simple data transfer between a client and server application.
To convince some people to switch from old school tech, I need to build a chat demo application that manages more than 10K concurrent connections using Java (like Node.Js stuff).
I have tested Netty 5.0 which is awesome but requires lot of work to be done; on the other hand Jetty 9.3 is great but is slow compared to other competitors.
After some search I found the Vert.x 3 toolkit which is based on Netty with a plethora of great tools (no need to reinvent the wheel), I have seen the examples in git and I was able to build a websocket server, etc.
public void start() throws Exception {
vertx.createHttpServer().websocketHandler(new Handler<ServerWebSocket>() {
#Override
public void handle(ServerWebSocket e) {
// business stuff in the old style not yet lambda
}
}).listen(port);
}
Being new to the Vert.x world, I could not figure out how to manage connected users using it, normally the old fashion way is to use something like:
HashMap<UUID,ServerWebSocket> connectedUsers;
When a connection is established I check if it exists; if not I add it as a new entry and do some functions to send, broadcast, retrieve through the collection and so on.
My question is does Vert.x 3 have something to deal with connections to track them and remove those who left (ping pong), broadcast, etc. or should I implement them from scratch using cookies, session, ....)
I could not find any real example using Vert.x 3.
Basically, the scope of the websocketHandler represents a connection. In your example this is your anonymous class. I created a little websocket chat example where I use the Vert.x event bus to distribute the messages to all the clients.
In the start method of the server we handle the websocket connections. You can implement the closeHandler to monitor client disconnection. There are also handlers for exceptions, ping-pong, etc. You can identify a specific connection by using the textHandlerID, but you have also access to the remote address.
public void start() throws Exception {
vertx.createHttpServer().websocketHandler(handler -> {
System.out.println("client connected: "+handler.textHandlerID());
vertx.eventBus().consumer(CHAT_CHANNEL, message -> {
handler.writeTextMessage((String)message.body());
});
handler.textMessageHandler(message -> {
vertx.eventBus().publish(CHAT_CHANNEL,message);
});
handler.closeHandler(message ->{
System.out.println("client disconnected "+handler.textHandlerID());
});
}).listen(8080);
}
The client example is also written in Java. It just prints all the received messages on the websocket connection to the console. After connection it sends a message.
public void start() throws Exception {
HttpClient client = vertx.createHttpClient();
client.websocket(8080, "localhost", "", websocket -> {
websocket.handler(data -> System.out.println(data.toString("ISO-8859-1")));
websocket.writeTextMessage(NAME+ ":hello from client");
});
}
I'm using Netty for sending and receiving UDP Multicast messages and when I create multiple servers on same node it sometimes stops stop receiving packets from other servers. However even though they don't receive any packet from other servers, they still keep receiving packets that are sent using same multicast server.
Here is the server code:
multicastAddress = new InetSocketAddress("239.255.27.1", 14878);
Bootstrap a = new Bootstrap()
.group(group)
.channelFactory(() -> new NioDatagramChannel(InternetProtocolFamily.IPv4))
.localAddress(multicastAddress)
.option(ChannelOption.IP_MULTICAST_IF, NetUtil.LOOPBACK_IF)
.option(ChannelOption.SO_REUSEADDR, true)
.handler(new ChannelInitializer<NioDatagramChannel>() {
#Override
public void initChannel(NioDatagramChannel ch) throws Exception {
h.pipeline().addLast(new ChannelInboundHandler() {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg);
}
});
}
});
multicastServer = (NioDatagramChannel) a.bind().sync().channel();
multicastServer.joinGroup(multicastAddress, NetUtil.LOOPBACK_IF).sync();
The client periodically sends same ByteBuf:
multicastServer.writeAndFlush(new DatagramPacket(Unpooled.copyInt(1), multicastAddress));
When I start a server on a node, it starts receiving packets that are sent from that server. Then, if create the second server on the same node, it usually works and the server receives packets sent from both first and second server. However when I create a few more servers that are bound to same port (I use ChannelOption.SO_REUSEADDR so it should be a problem) sometimes all of the servers stop receiving packets from other servers instead only receive packets that are sent from the same server.
The problem is here:
.option(ChannelOption.IP_MULTICAST_IF, NetUtil.LOOPBACK_IF)
This tells UDP to only send membership reports to the localhost, so the other hosts don't know that this host is a member. Just remove it.
I have the read the netty proxy server example. However I would like to know how to implement a client to talk to the proxy. The solution I am implementing is a server and it needs to connect to a socket server whenever a client connect to the server.So each client connected to the server will be able to send/receive data from another server.
I need help to inplement such architecture with netty because the server side is built on netty.
It seems what you want to implement can be pretty much answered by the Netty proxy example
The code segment below shows how you can connect to the remote server once a new client channel is opened.
#Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
// Suspend incoming traffic until connected to the remote host.
final Channel inboundChannel = e.getChannel();
inboundChannel.setReadable(false);
// Start the connection attempt.
ClientBootstrap cb = new ClientBootstrap(cf);
cb.getPipeline().addLast("handler", new OutboundHandler(e.getChannel()));
ChannelFuture f = cb.connect(new InetSocketAddress(remoteHost, remotePort));
outboundChannel = f.getChannel();
f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// Connection attempt succeeded:
// Begin to accept incoming traffic.
inboundChannel.setReadable(true);
} else {
// Close the connection if the connection attempt has failed.
inboundChannel.close();
}
}
});
}
Once connected to the remote server, whatever the client sends (via inbound channel) is forwarded to the remote server (outbound channel).
I suggest you to follow and implement the proxy example if you haven't done it so already.
I try to play with sockets a bit. For that I wrote very simple "client" and "server" applications.
Client:
import java.net.*;
public class client {
public static void main(String[] args) throws Exception {
InetAddress localhost = InetAddress.getLocalHost();
System.out.println("before");
Socket clientSideSocket = null;
try {
clientSideSocket = new Socket(localhost,12345,localhost,54321);
} catch (ConnectException e) {
System.out.println("Connection Refused");
}
System.out.println("after");
if (clientSideSocket != null) {
clientSideSocket.close();
}
}
}
Server:
import java.net.*;
public class server {
public static void main(String[] args) throws Exception {
ServerSocket listener = new ServerSocket(12345);
while (true) {
Socket serverSideSocket = listener.accept();
System.out.println("A client-request is accepted.");
}
}
}
And I found a behavior that I cannot explain:
I start a server, than I start a client. Connection is successfully established (client stops running and server is running). Then I close the server and start it again in a second. After that I start a client and it writes "Connection Refused". It seems to me that the server "remember" the old connection and does not want to open the second connection twice. But I do not understand how it is possible. Because I killed the previous server and started a new one!
I do not start the server immediately after the previous one was killed (I wait like 20 seconds). In this case the server "forget" the socket from the previous server and accepts the request from the client.
I start the server and then I start the client. Connection is established (server writes: "A client-request is accepted"). Then I wait a minute and start the client again. And server (which was running the whole time) accept the request again! Why? The server should not accept the request from the same client-IP and client-port but it does!
When you close the server , the OS will keep the socket alive for a while so it can tell the client the connection has been closed. This involves timeouts and retransmissions which can take some time. You might find some info here and here. If you want your server to be able to immediately rebind the same socket, call setReuseAddress(true) on it, though it might be the client sockets that's in a TIME_WAIT state.
The socket is no longer in TIME_WAIT state, and can be reused again by any program.
Your client code just connects, closes the socket and then exits. As far as the server/OS tcp stack is concerned, these are different connections - it's fine to reuse the source port as long as any prior connection have been torn down. (Note that the OS might not tear down all of the housekeeping of the connection immediately after you call .close() or your program exits, there's some time delay involved so it can be sure all packets have been sent/received)
It is likely the operating system has not yet shutdown the sockets, try the netstat command (should work on Windows or Unix/Linux). If you run it immediately after you close client or server you should still the socket in "TIME_WAIT" "CLOSE_WAIT" or something similar. You wont be able to reuse those ports until they are fully closed.
Per Question #3: Many clients can connect to a server attached to a single port. Apache runs on port 80 but that doesn't mean only one person can view your website at a time. Also you are closing your client socket before you're opening a new one.